<commit_before>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

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

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

* 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.

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 main

import (
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"math/big"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"

)

type supervisor struct {
	input       chan msg.Request
	shutdown    chan bool
	conn        *sql.DB
	seed        []byte
	key         []byte
	readonly    bool
	tokenExpiry uint64
	kexExpiry   uint64
	kex         svKexMap
	tokens      svTokenMap
	credentials svCredMap
	stmt_FToken *sql.Stmt
}

func (s *supervisor) run() {
	var err error

	// set library options
	auth.TokenExpirySeconds = s.tokenExpiry
	auth.KexExpirySeconds = s.kexExpiry

	// initialize maps
	s.tokens = s.newTokenMap()
	s.credentials = s.newCredentialMap()
	s.kex = s.newKexMap()

	// prepare SQL statements
	if s.stmt_FToken, err = s.conn.Prepare(stmt.SelectToken); err != nil {
		log.Fatal("supervisor/fetch-token: ", err)
	}
	defer s.stmt_FToken.Close()

runloop:
	for {
		select {
		case <-s.shutdown:
			break runloop
		case req := <-s.input:
			go func() {
				s.process(&req)
			}()
		}
	}
}

func (s *supervisor) process(q *msg.Request) {
	switch q.Action {
	case `kex_init`:
		s.kexInit(q)
	case `bootstrap_root`:
		s.bootstrapRoot(q)
	}
}

func (s *supervisor) kexInit(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `kex_reply`}
	kex := q.Super.Kex
	var err error

	// check the client submitted IV for fishyness
	err = s.checkIV(kex.InitializationVector)
	for err != nil {
		if err = kex.GenerateNewVector(); err != nil {
			continue
		}
		err = s.checkIV(kex.InitializationVector)
	}

	// record the kex submission time
	kex.SetTimeUTC()

	// record the client ip address
	kex.SetIPAddressString(q.Super.RemoteAddr)

	// generate a request ID
	kex.GenerateNewRequestID()

	// set the client submitted public key as peer key
	kex.SetPeerKey(kex.PublicKey())

	// generate our own keypair
	kex.GenerateNewKeypair()

	// save kex
	s.kex.insert(kex)

	// send out reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Kex: auth.Kex{
			Public:               kex.Public,
			InitializationVector: kex.InitializationVector,
			Request:              kex.Request,
		},
	}
	result.OK()

	q.Reply <- result
}

func (s *supervisor) bootstrapRoot(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `bootstrap_root`}
	kexId := q.Super.KexId
	data := q.Super.Data
	var kex *auth.Kex
	var err error
	var plain []byte
	var token auth.Token
	var rootToken string
	var mcf scrypth64.Mcf
	var tx *sql.Tx
	var validFrom, expiresAt time.Time

	// start response timer
	timer := time.NewTimer(1 * time.Second)
	defer timer.Stop()

	// -> check if root is not already active
	if s.credentials.read(`root`) != nil {
		result.BadRequest(fmt.Errorf(`Root account is already active`))
		//    --> delete kex
		s.kex.remove(kexId)
		goto dispatch
	}
	// -> get kex
	if kex = s.kex.read(kexId); kex == nil {
		//    --> reply 404 if not found
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> check kex.SameSource
	if !kex.IsSameSourceString(q.Super.RemoteAddr) {
		//    --> reply 404 if !SameSource
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> delete kex from s.kex (kex is now used)
	s.kex.remove(kexId)
	// -> rdata = kex.DecodeAndDecrypt(data)
	if err = kex.DecodeAndDecrypt(&data, &plain); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> json.Unmarshal(rdata, &token)
	if err = json.Unmarshal(plain, &token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> check token.UserName == `root`
	if token.UserName != `root` {
		//    --> reply 401
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> check token.Token is correct bearer token
	if rootToken, err = s.fetchRootToken(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if token.Token != rootToken || len(token.Password) == 0 {
		//    --> reply 401
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> scrypth64.Digest(Password, nil)
	if mcf, err = scrypth64.Digest(token.Password, nil); err != nil {
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> generate token
	token.SetIPAddressString(q.Super.RemoteAddr)
	if err = token.Generate(mcf, s.key, s.seed); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	validFrom, _ = time.Parse(rfc3339Milli, token.ValidFrom)
	expiresAt, _ = time.Parse(rfc3339Milli, token.ExpiresAt)

	// -> DB Insert: root password data
	if tx, err = s.conn.Begin(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	defer tx.Rollback()
	if _, err = tx.Exec(
		stmt.SetRootCredentials,
		uuid.Nil,
		mcf.String(),
		validFrom.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Insert: token data
	if _, err = tx.Exec(
		stmt.InsertToken,
		token.Token,
		token.Salt,
		validFrom.UTC(),
		expiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> s.credentials Update
	s.credentials.insert(`root`, uuid.Nil, validFrom.UTC(),
		PosTimeInf.UTC(), mcf)
	// -> s.tokens Update
	if err = s.tokens.insert(token.Token, token.ValidFrom, token.ExpiresAt,
		token.Salt); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = tx.Commit(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> sdata = kex.EncryptAndEncode(&token)
	plain = []byte{}
	data = []byte{}
	if plain, err = json.Marshal(token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = kex.EncryptAndEncode(&plain, &data); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> send sdata reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Data:    data,
	}
	result.OK()

dispatch:
	<-timer.C
	q.Reply <- result
}

// TODO: timer
// delete all expired key exchanges
func (s *supervisor) pruneKex() {
	s.kex.lock()
	defer s.kex.unlock()
	for kexId, kex := range s.kex.KMap {
		if kex.IsExpired() {
			delete(s.kex.KMap, kexId)
		}
	}
}

func (s *supervisor) newTokenMap() svTokenMap {
	m := svTokenMap{}
	m.TMap = make(map[string]svToken)
	return m
}

func (s *supervisor) newCredentialMap() svCredMap {
	m := svCredMap{}
	m.CMap = make(map[string]svCredential)
	return m
}

func (s *supervisor) newKexMap() svKexMap {
	m := svKexMap{}
	m.KMap = make(map[string]auth.Kex)
	return m
}

func (s *supervisor) Validate(account, token, addr string) bool {
	tok := s.tokens.read(token)
	if tok == nil && !s.readonly {
		// rw instance knows every token
		goto unauthorized
	} else if tok == nil {
		if !s.fetchTokenFromDB(token) {
			goto unauthorized
		}
		tok = s.tokens.read(token)
	}
	if time.Now().UTC().Before(tok.validFrom.UTC()) ||
		time.Now().UTC().After(tok.expiresAt.UTC()) {
		goto unauthorized
	}

	if auth.Verify(account, addr, tok.binToken, s.key,
		s.seed, tok.binExpiresAt, tok.salt) {
		return true
	}

unauthorized:
	return false
}

func (s *supervisor) fetchTokenFromDB(token string) bool {
	var (
		err                       error
		salt, strValid, strExpire string
		validF, validU            time.Time
	)

	err = s.stmt_FToken.QueryRow(token).Scan(&salt, &validF, &validU)
	if err == sql.ErrNoRows {
		return false
	} else if err != nil {
		// XXX log error
		return false
	}

	strValid = validF.UTC().Format(rfc3339Milli)
	strExpire = validU.UTC().Format(rfc3339Milli)

	if err = s.tokens.insert(token, strValid, strExpire, salt); err == nil {
		return true
	}
	return false
}

func (s *supervisor) fetchRootToken() (string, error) {
	var (
		err   error
		token string
	)

	err = s.conn.QueryRow(stmt.SelectRootToken).Scan(&token)
	if err != nil {
		return "", err
	}
	return token, nil
}

// the nonces used for encryption are implemented as
// a counter on top of the agreed upon IV. The first
// nonce used is IV+1.
// Check that the IV is not 0, this is likely to indicate
// a bad client. An IV of -1 would be worse, resulting in
// an initial nonce of 0 which can always lead to crypto
// swamps. Why are safe from that, since the Nonce calculation
// always takes the Abs value of the IV, stripping the sign.
func (s *supervisor) checkIV(iv string) error {
	var (
		err       error
		bIV       []byte
		iIV, zero *big.Int
	)
	zero = big.NewInt(0)

	if bIV, err = hex.DecodeString(iv); err != nil {
		return err
	}

	iIV = big.NewInt(0)
	iIV.SetBytes(bIV)
	iIV.Abs(iIV)
	if iIV.Cmp(zero) == 0 {
		return fmt.Errorf(`Invalid Initialization vector`)
	}
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Provide input-channel based BasicAuth function<commit_after>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

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

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

* 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.

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 main

import (
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"math/big"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"

)

type supervisor struct {
	input       chan msg.Request
	shutdown    chan bool
	conn        *sql.DB
	seed        []byte
	key         []byte
	readonly    bool
	tokenExpiry uint64
	kexExpiry   uint64
	kex         svKexMap
	tokens      svTokenMap
	credentials svCredMap
	stmt_FToken *sql.Stmt
}

func (s *supervisor) run() {
	var err error

	// set library options
	auth.TokenExpirySeconds = s.tokenExpiry
	auth.KexExpirySeconds = s.kexExpiry

	// initialize maps
	s.tokens = s.newTokenMap()
	s.credentials = s.newCredentialMap()
	s.kex = s.newKexMap()

	// prepare SQL statements
	if s.stmt_FToken, err = s.conn.Prepare(stmt.SelectToken); err != nil {
		log.Fatal("supervisor/fetch-token: ", err)
	}
	defer s.stmt_FToken.Close()

runloop:
	for {
		select {
		case <-s.shutdown:
			break runloop
		case req := <-s.input:
			go func() {
				s.process(&req)
			}()
		}
	}
}

func (s *supervisor) process(q *msg.Request) {
	switch q.Action {
	case `kex_init`:
		s.kexInit(q)
	case `bootstrap_root`:
		s.bootstrapRoot(q)
	case `basic_auth`:
		s.validate_basic_auth(q)
	}
}

func (s *supervisor) kexInit(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `kex_reply`}
	kex := q.Super.Kex
	var err error

	// check the client submitted IV for fishyness
	err = s.checkIV(kex.InitializationVector)
	for err != nil {
		if err = kex.GenerateNewVector(); err != nil {
			continue
		}
		err = s.checkIV(kex.InitializationVector)
	}

	// record the kex submission time
	kex.SetTimeUTC()

	// record the client ip address
	kex.SetIPAddressString(q.Super.RemoteAddr)

	// generate a request ID
	kex.GenerateNewRequestID()

	// set the client submitted public key as peer key
	kex.SetPeerKey(kex.PublicKey())

	// generate our own keypair
	kex.GenerateNewKeypair()

	// save kex
	s.kex.insert(kex)

	// send out reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Kex: auth.Kex{
			Public:               kex.Public,
			InitializationVector: kex.InitializationVector,
			Request:              kex.Request,
		},
	}
	result.OK()

	q.Reply <- result
}

func (s *supervisor) bootstrapRoot(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `bootstrap_root`}
	kexId := q.Super.KexId
	data := q.Super.Data
	var kex *auth.Kex
	var err error
	var plain []byte
	var token auth.Token
	var rootToken string
	var mcf scrypth64.Mcf
	var tx *sql.Tx
	var validFrom, expiresAt time.Time

	// start response timer
	timer := time.NewTimer(1 * time.Second)
	defer timer.Stop()

	// -> check if root is not already active
	if s.credentials.read(`root`) != nil {
		result.BadRequest(fmt.Errorf(`Root account is already active`))
		//    --> delete kex
		s.kex.remove(kexId)
		goto dispatch
	}
	// -> get kex
	if kex = s.kex.read(kexId); kex == nil {
		//    --> reply 404 if not found
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> check kex.SameSource
	if !kex.IsSameSourceString(q.Super.RemoteAddr) {
		//    --> reply 404 if !SameSource
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> delete kex from s.kex (kex is now used)
	s.kex.remove(kexId)
	// -> rdata = kex.DecodeAndDecrypt(data)
	if err = kex.DecodeAndDecrypt(&data, &plain); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> json.Unmarshal(rdata, &token)
	if err = json.Unmarshal(plain, &token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> check token.UserName == `root`
	if token.UserName != `root` {
		//    --> reply 401
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> check token.Token is correct bearer token
	if rootToken, err = s.fetchRootToken(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if token.Token != rootToken || len(token.Password) == 0 {
		//    --> reply 401
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> scrypth64.Digest(Password, nil)
	if mcf, err = scrypth64.Digest(token.Password, nil); err != nil {
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> generate token
	token.SetIPAddressString(q.Super.RemoteAddr)
	if err = token.Generate(mcf, s.key, s.seed); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	validFrom, _ = time.Parse(rfc3339Milli, token.ValidFrom)
	expiresAt, _ = time.Parse(rfc3339Milli, token.ExpiresAt)

	// -> DB Insert: root password data
	if tx, err = s.conn.Begin(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	defer tx.Rollback()
	if _, err = tx.Exec(
		stmt.SetRootCredentials,
		uuid.Nil,
		mcf.String(),
		validFrom.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Insert: token data
	if _, err = tx.Exec(
		stmt.InsertToken,
		token.Token,
		token.Salt,
		validFrom.UTC(),
		expiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> s.credentials Update
	s.credentials.insert(`root`, uuid.Nil, validFrom.UTC(),
		PosTimeInf.UTC(), mcf)
	// -> s.tokens Update
	if err = s.tokens.insert(token.Token, token.ValidFrom, token.ExpiresAt,
		token.Salt); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = tx.Commit(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> sdata = kex.EncryptAndEncode(&token)
	plain = []byte{}
	data = []byte{}
	if plain, err = json.Marshal(token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = kex.EncryptAndEncode(&plain, &data); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> send sdata reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Data:    data,
	}
	result.OK()

dispatch:
	<-timer.C
	q.Reply <- result
}

// TODO: timer
// delete all expired key exchanges
func (s *supervisor) pruneKex() {
	s.kex.lock()
	defer s.kex.unlock()
	for kexId, kex := range s.kex.KMap {
		if kex.IsExpired() {
			delete(s.kex.KMap, kexId)
		}
	}
}

func (s *supervisor) newTokenMap() svTokenMap {
	m := svTokenMap{}
	m.TMap = make(map[string]svToken)
	return m
}

func (s *supervisor) newCredentialMap() svCredMap {
	m := svCredMap{}
	m.CMap = make(map[string]svCredential)
	return m
}

func (s *supervisor) newKexMap() svKexMap {
	m := svKexMap{}
	m.KMap = make(map[string]auth.Kex)
	return m
}

func (s *supervisor) validate_basic_auth(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `authenticate`}

	tok := s.tokens.read(q.Super.BasicAuthToken)
	if tok == nil && !s.readonly {
		// rw instance knows every token
		result.ServerError(fmt.Errorf(`Unknown Token (TokenMap)`))
		goto unauthorized
	} else if tok == nil {
		if !s.fetchTokenFromDB(q.Super.BasicAuthToken) {
			result.ServerError(fmt.Errorf(`Unknown Token (pgSQL)`))
			goto unauthorized
		}
		tok = s.tokens.read(q.Super.BasicAuthToken)
	}
	if time.Now().UTC().Before(tok.validFrom.UTC()) ||
		time.Now().UTC().After(tok.expiresAt.UTC()) {
		result.Unauthorized(fmt.Errorf(`Token expired`))
		goto unauthorized
	}

	if auth.Verify(q.Super.BasicAuthUser, q.Super.RemoteAddr, tok.binToken, s.key,
		s.seed, tok.binExpiresAt, tok.salt) {
		// valid token
		result.Super = &msg.Supervisor{Verdict: 200}
		result.OK()
		q.Reply <- result
	}

unauthorized:
	result.Super = &msg.Supervisor{Verdict: 401}
	q.Reply <- result
}

func (s *supervisor) fetchTokenFromDB(token string) bool {
	var (
		err                       error
		salt, strValid, strExpire string
		validF, validU            time.Time
	)

	err = s.stmt_FToken.QueryRow(token).Scan(&salt, &validF, &validU)
	if err == sql.ErrNoRows {
		return false
	} else if err != nil {
		// XXX log error
		return false
	}

	strValid = validF.UTC().Format(rfc3339Milli)
	strExpire = validU.UTC().Format(rfc3339Milli)

	if err = s.tokens.insert(token, strValid, strExpire, salt); err == nil {
		return true
	}
	return false
}

func (s *supervisor) fetchRootToken() (string, error) {
	var (
		err   error
		token string
	)

	err = s.conn.QueryRow(stmt.SelectRootToken).Scan(&token)
	if err != nil {
		return "", err
	}
	return token, nil
}

// the nonces used for encryption are implemented as
// a counter on top of the agreed upon IV. The first
// nonce used is IV+1.
// Check that the IV is not 0, this is likely to indicate
// a bad client. An IV of -1 would be worse, resulting in
// an initial nonce of 0 which can always lead to crypto
// swamps. Why are safe from that, since the Nonce calculation
// always takes the Abs value of the IV, stripping the sign.
func (s *supervisor) checkIV(iv string) error {
	var (
		err       error
		bIV       []byte
		iIV, zero *big.Int
	)
	zero = big.NewInt(0)

	if bIV, err = hex.DecodeString(iv); err != nil {
		return err
	}

	iIV = big.NewInt(0)
	iIV.SetBytes(bIV)
	iIV.Abs(iIV)
	if iIV.Cmp(zero) == 0 {
		return fmt.Errorf(`Invalid Initialization vector`)
	}
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package model

import "time"

// AccountInfo holds all information for an account from the oldest to
// the latest transaction
type AccountInfo struct {
	IBAN              string         `json:"iban"`
	BIC               string         `json:"bic"`
	Balance           int32          `json:"balance"`
	Predeccessor      string         `json:"predeccessor"`
	OldestTransaction string         `json:"oldestTransaction"`
	LatestTransaction string         `json:"latestTransaction"`
	Transactions      []*Transaction `json:"transactions"`
}

// GetTransactionsAfter returns all transaction of the given accountinfo
func (ai *AccountInfo) GetTransactionsAfter(after time.Time) []*Transaction {
	length := len(ai.Transactions)

	for i := 0; i < length; i++ {
		if ai.Transactions[i].BookingDate.After(after) ||
			ai.Transactions[i].BookingDate.Equal(after) {
			return ai.Transactions[i:]
		}
	}

	return []*Transaction{}
}

// NewAccountInfo creates a new accountInf
func NewAccountInfo(bic string, iban string, balance int32, transactions []*Transaction) *AccountInfo {
	return &AccountInfo{BIC: bic, IBAN: iban, Balance: balance, Transactions: transactions}
}
<commit_msg>Omit last transaction, oldest transaction when empty<commit_after>package model

import "time"

// AccountInfo holds all information for an account from the oldest to
// the latest transaction
type AccountInfo struct {
	IBAN              string         `json:"iban"`
	BIC               string         `json:"bic"`
	Balance           int32          `json:"balance"`
	Predeccessor      string         `json:"predeccessor,omitempty"`
	OldestTransaction string         `json:"oldestTransaction,omitempty"`
	LatestTransaction string         `json:"latestTransaction,omitempty"`
	Transactions      []*Transaction `json:"transactions"`
}

// GetTransactionsAfter returns all transaction of the given accountinfo
func (ai *AccountInfo) GetTransactionsAfter(after time.Time) []*Transaction {
	length := len(ai.Transactions)

	for i := 0; i < length; i++ {
		if ai.Transactions[i].BookingDate.After(after) ||
			ai.Transactions[i].BookingDate.Equal(after) {
			return ai.Transactions[i:]
		}
	}

	return []*Transaction{}
}

// NewAccountInfo creates a new accountInf
func NewAccountInfo(bic string, iban string, balance int32, transactions []*Transaction) *AccountInfo {
	return &AccountInfo{BIC: bic, IBAN: iban, Balance: balance, Transactions: transactions}
}
<|endoftext|>


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

// Taken and extracted from
// https://github.com/juju/juju/blob/master/utils/ssh/authorisedkeys.go The
// functions in the original were too Juju specific and not something that
// could be used orthogonal in other packages. I've removed all third party
// dependencies and add the necessary functions into this package - arslan

package sshkeys

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"

	"golang.org/x/crypto/ssh"
)

type ListMode bool

var (
	FullKeys     ListMode = true
	Fingerprints ListMode = false
)

const (
	authKeysFile = "authorized_keys"
)

type AuthorisedKey struct {
	Type    string
	Key     []byte
	Comment string
}

func authKeysDir(username string) (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", err
	}

	return filepath.Join(u.HomeDir, ".ssh"), nil
}

// ParseAuthorisedKey parses a non-comment line from an
// authorized_keys file and returns the constituent parts.
// Based on description in "man sshd".
func ParseAuthorisedKey(line string) (*AuthorisedKey, error) {
	key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(line))
	if err != nil {
		return nil, fmt.Errorf("invalid authorized_key %q", line)
	}
	return &AuthorisedKey{
		Type:    key.Type(),
		Key:     key.Marshal(),
		Comment: comment,
	}, nil
}

// SplitAuthorisedKeys extracts a key slice from the specified key data,
// by splitting the key data into lines and ignoring comments and blank lines.
func SplitAuthorisedKeys(keyData string) []string {
	var keys []string
	for _, key := range strings.Split(string(keyData), "\n") {
		key = strings.Trim(key, " \r")
		if len(key) == 0 {
			continue
		}
		if key[0] == '#' {
			continue
		}
		keys = append(keys, key)
	}
	return keys
}

func readAuthorisedKeys(username string) ([]string, error) {
	keyDir, err := authKeysDir(username)
	if err != nil {
		return nil, err
	}
	sshKeyFile := filepath.Join(keyDir, authKeysFile)
	log.Printf("reading authorised keys file %s", sshKeyFile)
	keyData, err := ioutil.ReadFile(sshKeyFile)
	if os.IsNotExist(err) {
		return []string{}, nil
	}
	if err != nil {
		return nil, fmt.Errorf("reading ssh authorised keys file: %v", err)
	}
	var keys []string
	for _, key := range strings.Split(string(keyData), "\n") {
		if len(strings.Trim(key, " \r")) == 0 {
			continue
		}
		keys = append(keys, key)
	}
	return keys, nil
}

func writeAuthorisedKeys(username string, keys []string) error {
	keyDir, err := authKeysDir(username)
	if err != nil {
		return err
	}
	err = os.MkdirAll(keyDir, os.FileMode(0755))
	if err != nil {
		return fmt.Errorf("cannot create ssh key directory: %v", err)
	}
	keyData := strings.Join(keys, "\n") + "\n"

	// Get perms to use on auth keys file
	sshKeyFile := filepath.Join(keyDir, authKeysFile)
	perms := os.FileMode(0644)
	info, err := os.Stat(sshKeyFile)
	if err == nil {
		perms = info.Mode().Perm()
	}

	log.Printf("writing authorised keys file %s", sshKeyFile)
	err = AtomicWriteFile(sshKeyFile, []byte(keyData), perms)
	if err != nil {
		return err
	}

	// TODO (wallyworld) - what to do on windows (if anything)
	// TODO(dimitern) - no need to use user.Current() if username
	// is "" - it will use the current user anyway.
	if runtime.GOOS != "windows" {
		// Ensure the resulting authorised keys file has its ownership
		// set to the specified username.
		var u *user.User
		if username == "" {
			u, err = user.Current()
		} else {
			u, err = user.Lookup(username)
		}
		if err != nil {
			return err
		}
		// chown requires ints but user.User has strings for windows.
		uid, err := strconv.Atoi(u.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(u.Gid)
		if err != nil {
			return err
		}
		err = os.Chown(sshKeyFile, uid, gid)
		if err != nil {
			return err
		}
	}
	return nil
}

// We need a mutex because updates to the authorised keys file are done by
// reading the contents, updating, and writing back out. So only one caller
// at a time can use either Add, Delete, List.
var mutex sync.Mutex

// AddKeys adds the specified ssh keys to the authorized_keys file for user.
// Returns an error if there is an issue with *any* of the supplied keys.
func AddKeys(user string, newKeys ...string) error {
	mutex.Lock()
	defer mutex.Unlock()
	existingKeys, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	for _, newKey := range newKeys {
		fingerprint, comment, err := KeyFingerprint(newKey)
		if err != nil {
			return err
		}
		if comment == "" {
			return fmt.Errorf("cannot add ssh key without comment")
		}
		for _, key := range existingKeys {
			existingFingerprint, existingComment, err := KeyFingerprint(key)
			if err != nil {
				// Only log a warning if the unrecognised key line is not a comment.
				if key[0] != '#' {
					log.Printf("invalid existing ssh key %q: %v", key, err)
				}
				continue
			}
			if existingFingerprint == fingerprint {
				return fmt.Errorf("cannot add duplicate ssh key: %v", fingerprint)
			}
			if existingComment == comment {
				return fmt.Errorf("cannot add ssh key with duplicate comment: %v", comment)
			}
		}
	}
	sshKeys := append(existingKeys, newKeys...)
	return writeAuthorisedKeys(user, sshKeys)
}

// DeleteKeys removes the specified ssh keys from the authorized ssh keys file for user.
// keyIds may be either key comments or fingerprints.
// Returns an error if there is an issue with *any* of the keys to delete.
func DeleteKeys(user string, keyIds ...string) error {
	mutex.Lock()
	defer mutex.Unlock()
	existingKeyData, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	// Build up a map of keys indexed by fingerprint, and fingerprints indexed by comment
	// so we can easily get the key represented by each keyId, which may be either a fingerprint
	// or comment.
	var keysToWrite []string
	var sshKeys = make(map[string]string)
	var keyComments = make(map[string]string)
	for _, key := range existingKeyData {
		fingerprint, comment, err := KeyFingerprint(key)
		if err != nil {
			log.Printf("keeping unrecognised existing ssh key %q: %v", key, err)
			keysToWrite = append(keysToWrite, key)
			continue
		}
		sshKeys[fingerprint] = key
		if comment != "" {
			keyComments[comment] = fingerprint
		}
	}
	for _, keyId := range keyIds {
		// assume keyId may be a fingerprint
		fingerprint := keyId
		_, ok := sshKeys[keyId]
		if !ok {
			// keyId is a comment
			fingerprint, ok = keyComments[keyId]
		}
		if !ok {
			return fmt.Errorf("cannot delete non existent key: %v", keyId)
		}
		delete(sshKeys, fingerprint)
	}
	for _, key := range sshKeys {
		keysToWrite = append(keysToWrite, key)
	}
	if len(keysToWrite) == 0 {
		return fmt.Errorf("cannot delete all keys")
	}
	return writeAuthorisedKeys(user, keysToWrite)
}

// ReplaceKeys writes the specified ssh keys to the authorized_keys file for user,
// replacing any that are already there.
// Returns an error if there is an issue with *any* of the supplied keys.
func ReplaceKeys(user string, newKeys ...string) error {
	mutex.Lock()
	defer mutex.Unlock()

	existingKeyData, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	var existingNonKeyLines []string
	for _, line := range existingKeyData {
		_, _, err := KeyFingerprint(line)
		if err != nil {
			existingNonKeyLines = append(existingNonKeyLines, line)
		}
	}
	return writeAuthorisedKeys(user, append(existingNonKeyLines, newKeys...))
}

// ListKeys returns either the full keys or key comments from the authorized ssh keys file for user.
func ListKeys(user string, mode ListMode) ([]string, error) {
	mutex.Lock()
	defer mutex.Unlock()
	keyData, err := readAuthorisedKeys(user)
	if err != nil {
		return nil, err
	}
	var keys []string
	for _, key := range keyData {
		fingerprint, comment, err := KeyFingerprint(key)
		if err != nil {
			// Only log a warning if the unrecognised key line is not a comment.
			if key[0] != '#' {
				log.Printf("ignoring invalid ssh key %q: %v", key, err)
			}
			continue
		}
		if mode == FullKeys {
			keys = append(keys, key)
		} else {
			shortKey := fingerprint
			if comment != "" {
				shortKey += fmt.Sprintf(" (%s)", comment)
			}
			keys = append(keys, shortKey)
		}
	}
	return keys, nil
}

// Any ssh key added to the authorised keys list by Juju will have this prefix.
// This allows Juju to know which keys have been added externally and any such keys
// will always be retained by Juju when updating the authorised keys file.
const JujuCommentPrefix = "Juju:"

func EnsureJujuComment(key string) string {
	ak, err := ParseAuthorisedKey(key)
	// Just return an invalid key as is.
	if err != nil {
		log.Printf("invalid Juju ssh key %s: %v", key, err)
		return key
	}
	if ak.Comment == "" {
		return key + " " + JujuCommentPrefix + "sshkey"
	} else {
		// Add the Juju prefix to the comment if necessary.
		if !strings.HasPrefix(ak.Comment, JujuCommentPrefix) {
			commentIndex := strings.LastIndex(key, ak.Comment)
			return key[:commentIndex] + JujuCommentPrefix + ak.Comment
		}
	}
	return key
}

// AtomicWriteFile atomically writes the filename with the given
// contents and permissions, replacing any existing file at the same
// path.
func AtomicWriteFile(filename string, contents []byte, perms os.FileMode) (err error) {
	return AtomicWriteFileAndChange(filename, contents, func(f *os.File) error {
		// FileMod.Chmod() is not implemented on Windows, however, os.Chmod() is
		if err := os.Chmod(f.Name(), perms); err != nil {
			return fmt.Errorf("cannot set permissions: %v", err)
		}
		return nil
	})
}

// AtomicWriteFileAndChange atomically writes the filename with the
// given contents and calls the given function after the contents were
// written, but before the file is renamed.
func AtomicWriteFileAndChange(filename string, contents []byte, change func(*os.File) error) (err error) {
	dir, file := filepath.Split(filename)
	f, err := ioutil.TempFile(dir, file)
	if err != nil {
		return fmt.Errorf("cannot create temp file: %v", err)
	}
	defer f.Close()
	defer func() {
		if err != nil {
			// Don't leave the temp file lying around on error.
			// Close the file before removing. Trying to remove an open file on
			// Windows will fail.
			f.Close()
			os.Remove(f.Name())
		}
	}()
	if _, err := f.Write(contents); err != nil {
		return fmt.Errorf("cannot write %q contents: %v", filename, err)
	}
	if err := change(f); err != nil {
		return err
	}
	f.Close()
	if err := os.Rename(f.Name(), filename); err != nil {
		return fmt.Errorf("cannot replace %q with %q: %v", f.Name(), filename, err)
	}
	return nil
}

// KeyFingerprint returns the fingerprint and comment for the specified key
// in authorized_key format. Fingerprints are generated according to RFC4716.
// See ttp://www.ietf.org/rfc/rfc4716.txt, section 4.
func KeyFingerprint(key string) (fingerprint, comment string, err error) {
	ak, err := ParseAuthorisedKey(key)
	if err != nil {
		return "", "", fmt.Errorf("generating key fingerprint: %v", err)
	}
	hash := md5.New()
	hash.Write(ak.Key)
	sum := hash.Sum(nil)
	var buf bytes.Buffer
	for i := 0; i < hash.Size(); i++ {
		if i > 0 {
			buf.WriteByte(':')
		}
		buf.WriteString(fmt.Sprintf("%02x", sum[i]))
	}
	return buf.String(), ak.Comment, nil
}
<commit_msg>sshkeys: i hate global variables in the middle of nowhere<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Taken and extracted from
// https://github.com/juju/juju/blob/master/utils/ssh/authorisedkeys.go The
// functions in the original were too Juju specific and not something that
// could be used orthogonal in other packages. I've removed all third party
// dependencies and add the necessary functions into this package - arslan

package sshkeys

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"

	"golang.org/x/crypto/ssh"
)

type ListMode bool

var (
	FullKeys     ListMode = true
	Fingerprints ListMode = false

	// We need a mutex because updates to the authorised keys file are done by
	// reading the contents, updating, and writing back out. So only one caller
	// at a time can use either Add, Delete, List.
	mutex sync.Mutex
)

const (
	authKeysFile = "authorized_keys"
)

type AuthorisedKey struct {
	Type    string
	Key     []byte
	Comment string
}

func authKeysDir(username string) (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", err
	}

	return filepath.Join(u.HomeDir, ".ssh"), nil
}

// ParseAuthorisedKey parses a non-comment line from an
// authorized_keys file and returns the constituent parts.
// Based on description in "man sshd".
func ParseAuthorisedKey(line string) (*AuthorisedKey, error) {
	key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(line))
	if err != nil {
		return nil, fmt.Errorf("invalid authorized_key %q", line)
	}
	return &AuthorisedKey{
		Type:    key.Type(),
		Key:     key.Marshal(),
		Comment: comment,
	}, nil
}

// SplitAuthorisedKeys extracts a key slice from the specified key data,
// by splitting the key data into lines and ignoring comments and blank lines.
func SplitAuthorisedKeys(keyData string) []string {
	var keys []string
	for _, key := range strings.Split(string(keyData), "\n") {
		key = strings.Trim(key, " \r")
		if len(key) == 0 {
			continue
		}
		if key[0] == '#' {
			continue
		}
		keys = append(keys, key)
	}
	return keys
}

func readAuthorisedKeys(username string) ([]string, error) {
	keyDir, err := authKeysDir(username)
	if err != nil {
		return nil, err
	}
	sshKeyFile := filepath.Join(keyDir, authKeysFile)
	log.Printf("reading authorised keys file %s", sshKeyFile)
	keyData, err := ioutil.ReadFile(sshKeyFile)
	if os.IsNotExist(err) {
		return []string{}, nil
	}
	if err != nil {
		return nil, fmt.Errorf("reading ssh authorised keys file: %v", err)
	}
	var keys []string
	for _, key := range strings.Split(string(keyData), "\n") {
		if len(strings.Trim(key, " \r")) == 0 {
			continue
		}
		keys = append(keys, key)
	}
	return keys, nil
}

func writeAuthorisedKeys(username string, keys []string) error {
	keyDir, err := authKeysDir(username)
	if err != nil {
		return err
	}
	err = os.MkdirAll(keyDir, os.FileMode(0755))
	if err != nil {
		return fmt.Errorf("cannot create ssh key directory: %v", err)
	}
	keyData := strings.Join(keys, "\n") + "\n"

	// Get perms to use on auth keys file
	sshKeyFile := filepath.Join(keyDir, authKeysFile)
	perms := os.FileMode(0644)
	info, err := os.Stat(sshKeyFile)
	if err == nil {
		perms = info.Mode().Perm()
	}

	log.Printf("writing authorised keys file %s", sshKeyFile)
	err = AtomicWriteFile(sshKeyFile, []byte(keyData), perms)
	if err != nil {
		return err
	}

	// TODO (wallyworld) - what to do on windows (if anything)
	// TODO(dimitern) - no need to use user.Current() if username
	// is "" - it will use the current user anyway.
	if runtime.GOOS != "windows" {
		// Ensure the resulting authorised keys file has its ownership
		// set to the specified username.
		var u *user.User
		if username == "" {
			u, err = user.Current()
		} else {
			u, err = user.Lookup(username)
		}
		if err != nil {
			return err
		}
		// chown requires ints but user.User has strings for windows.
		uid, err := strconv.Atoi(u.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(u.Gid)
		if err != nil {
			return err
		}
		err = os.Chown(sshKeyFile, uid, gid)
		if err != nil {
			return err
		}
	}
	return nil
}

// AddKeys adds the specified ssh keys to the authorized_keys file for user.
// Returns an error if there is an issue with *any* of the supplied keys.
func AddKeys(user string, newKeys ...string) error {
	mutex.Lock()
	defer mutex.Unlock()
	existingKeys, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	for _, newKey := range newKeys {
		fingerprint, comment, err := KeyFingerprint(newKey)
		if err != nil {
			return err
		}
		if comment == "" {
			return fmt.Errorf("cannot add ssh key without comment")
		}
		for _, key := range existingKeys {
			existingFingerprint, existingComment, err := KeyFingerprint(key)
			if err != nil {
				// Only log a warning if the unrecognised key line is not a comment.
				if key[0] != '#' {
					log.Printf("invalid existing ssh key %q: %v", key, err)
				}
				continue
			}
			if existingFingerprint == fingerprint {
				return fmt.Errorf("cannot add duplicate ssh key: %v", fingerprint)
			}
			if existingComment == comment {
				return fmt.Errorf("cannot add ssh key with duplicate comment: %v", comment)
			}
		}
	}
	sshKeys := append(existingKeys, newKeys...)
	return writeAuthorisedKeys(user, sshKeys)
}

// DeleteKeys removes the specified ssh keys from the authorized ssh keys file for user.
// keyIds may be either key comments or fingerprints.
// Returns an error if there is an issue with *any* of the keys to delete.
func DeleteKeys(user string, keyIds ...string) error {
	mutex.Lock()
	defer mutex.Unlock()
	existingKeyData, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	// Build up a map of keys indexed by fingerprint, and fingerprints indexed by comment
	// so we can easily get the key represented by each keyId, which may be either a fingerprint
	// or comment.
	var keysToWrite []string
	var sshKeys = make(map[string]string)
	var keyComments = make(map[string]string)
	for _, key := range existingKeyData {
		fingerprint, comment, err := KeyFingerprint(key)
		if err != nil {
			log.Printf("keeping unrecognised existing ssh key %q: %v", key, err)
			keysToWrite = append(keysToWrite, key)
			continue
		}
		sshKeys[fingerprint] = key
		if comment != "" {
			keyComments[comment] = fingerprint
		}
	}
	for _, keyId := range keyIds {
		// assume keyId may be a fingerprint
		fingerprint := keyId
		_, ok := sshKeys[keyId]
		if !ok {
			// keyId is a comment
			fingerprint, ok = keyComments[keyId]
		}
		if !ok {
			return fmt.Errorf("cannot delete non existent key: %v", keyId)
		}
		delete(sshKeys, fingerprint)
	}
	for _, key := range sshKeys {
		keysToWrite = append(keysToWrite, key)
	}
	if len(keysToWrite) == 0 {
		return fmt.Errorf("cannot delete all keys")
	}
	return writeAuthorisedKeys(user, keysToWrite)
}

// ReplaceKeys writes the specified ssh keys to the authorized_keys file for user,
// replacing any that are already there.
// Returns an error if there is an issue with *any* of the supplied keys.
func ReplaceKeys(user string, newKeys ...string) error {
	mutex.Lock()
	defer mutex.Unlock()

	existingKeyData, err := readAuthorisedKeys(user)
	if err != nil {
		return err
	}
	var existingNonKeyLines []string
	for _, line := range existingKeyData {
		_, _, err := KeyFingerprint(line)
		if err != nil {
			existingNonKeyLines = append(existingNonKeyLines, line)
		}
	}
	return writeAuthorisedKeys(user, append(existingNonKeyLines, newKeys...))
}

// ListKeys returns either the full keys or key comments from the authorized ssh keys file for user.
func ListKeys(user string, mode ListMode) ([]string, error) {
	mutex.Lock()
	defer mutex.Unlock()
	keyData, err := readAuthorisedKeys(user)
	if err != nil {
		return nil, err
	}
	var keys []string
	for _, key := range keyData {
		fingerprint, comment, err := KeyFingerprint(key)
		if err != nil {
			// Only log a warning if the unrecognised key line is not a comment.
			if key[0] != '#' {
				log.Printf("ignoring invalid ssh key %q: %v", key, err)
			}
			continue
		}
		if mode == FullKeys {
			keys = append(keys, key)
		} else {
			shortKey := fingerprint
			if comment != "" {
				shortKey += fmt.Sprintf(" (%s)", comment)
			}
			keys = append(keys, shortKey)
		}
	}
	return keys, nil
}

// Any ssh key added to the authorised keys list by Juju will have this prefix.
// This allows Juju to know which keys have been added externally and any such keys
// will always be retained by Juju when updating the authorised keys file.
const JujuCommentPrefix = "Juju:"

func EnsureJujuComment(key string) string {
	ak, err := ParseAuthorisedKey(key)
	// Just return an invalid key as is.
	if err != nil {
		log.Printf("invalid Juju ssh key %s: %v", key, err)
		return key
	}
	if ak.Comment == "" {
		return key + " " + JujuCommentPrefix + "sshkey"
	} else {
		// Add the Juju prefix to the comment if necessary.
		if !strings.HasPrefix(ak.Comment, JujuCommentPrefix) {
			commentIndex := strings.LastIndex(key, ak.Comment)
			return key[:commentIndex] + JujuCommentPrefix + ak.Comment
		}
	}
	return key
}

// AtomicWriteFile atomically writes the filename with the given
// contents and permissions, replacing any existing file at the same
// path.
func AtomicWriteFile(filename string, contents []byte, perms os.FileMode) (err error) {
	return AtomicWriteFileAndChange(filename, contents, func(f *os.File) error {
		// FileMod.Chmod() is not implemented on Windows, however, os.Chmod() is
		if err := os.Chmod(f.Name(), perms); err != nil {
			return fmt.Errorf("cannot set permissions: %v", err)
		}
		return nil
	})
}

// AtomicWriteFileAndChange atomically writes the filename with the
// given contents and calls the given function after the contents were
// written, but before the file is renamed.
func AtomicWriteFileAndChange(filename string, contents []byte, change func(*os.File) error) (err error) {
	dir, file := filepath.Split(filename)
	f, err := ioutil.TempFile(dir, file)
	if err != nil {
		return fmt.Errorf("cannot create temp file: %v", err)
	}
	defer f.Close()
	defer func() {
		if err != nil {
			// Don't leave the temp file lying around on error.
			// Close the file before removing. Trying to remove an open file on
			// Windows will fail.
			f.Close()
			os.Remove(f.Name())
		}
	}()
	if _, err := f.Write(contents); err != nil {
		return fmt.Errorf("cannot write %q contents: %v", filename, err)
	}
	if err := change(f); err != nil {
		return err
	}
	f.Close()
	if err := os.Rename(f.Name(), filename); err != nil {
		return fmt.Errorf("cannot replace %q with %q: %v", f.Name(), filename, err)
	}
	return nil
}

// KeyFingerprint returns the fingerprint and comment for the specified key
// in authorized_key format. Fingerprints are generated according to RFC4716.
// See ttp://www.ietf.org/rfc/rfc4716.txt, section 4.
func KeyFingerprint(key string) (fingerprint, comment string, err error) {
	ak, err := ParseAuthorisedKey(key)
	if err != nil {
		return "", "", fmt.Errorf("generating key fingerprint: %v", err)
	}
	hash := md5.New()
	hash.Write(ak.Key)
	sum := hash.Sum(nil)
	var buf bytes.Buffer
	for i := 0; i < hash.Size(); i++ {
		if i > 0 {
			buf.WriteByte(':')
		}
		buf.WriteString(fmt.Sprintf("%02x", sum[i]))
	}
	return buf.String(), ak.Comment, nil
}
<|endoftext|>


<commit_before>package raven

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

type FunctionNameTest struct {
	skip int
	pack string
	name string
}

var (
	thisFile          string
	thisPackage       string
	functionNameTests []FunctionNameTest
)

func TestFunctionName(t *testing.T) {
	for _, test := range functionNameTests {
		pc, _, _, _ := runtime.Caller(test.skip)
		pack, name := functionName(pc)

		if pack != test.pack {
			t.Errorf("incorrect package; got %s, want %s", pack, test.pack)
		}
		if name != test.name {
			t.Errorf("incorrect function; got %s, want %s", name, test.name)
		}
	}
}

func TestStacktrace(t *testing.T) {
	st := trace()
	if st == nil {
		t.Error("got nil stacktrace")
	}
	if len(st.Frames) == 0 {
		t.Error("got zero frames")
	}

	f := st.Frames[len(st.Frames)-1]
	if f.Filename != thisFile {
		t.Errorf("incorrect Filename; got %s, want %s", f.Filename, thisFile)
	}
	if !strings.HasSuffix(f.AbsolutePath, thisFile) {
		t.Error("incorrect AbsolutePath:", f.AbsolutePath)
	}
	if f.Function != "trace" {
		t.Error("incorrect Function:", f.Function)
	}
	if f.Module != thisPackage {
		t.Error("incorrect Module:", f.Module)
	}
	if f.Lineno != 87 {
		t.Error("incorrect Lineno:", f.Lineno)
	}
	if f.ContextLine != "\treturn NewStacktrace(0, 2, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
	if len(f.PreContext) != 2 || f.PreContext[0] != "// a" || f.PreContext[1] != "func trace() *Stacktrace {" {
		t.Errorf("incorrect PreContext %#v", f.PreContext)
	}
	if len(f.PostContext) != 2 || f.PostContext[0] != "\t// b" || f.PostContext[1] != "}" {
		t.Errorf("incorrect PostContext %#v", f.PostContext)
	}
	_, filename, _, _ := runtime.Caller(0)
	runningInVendored := strings.Contains(filename, "vendor")
	if f.InApp != !runningInVendored {
		t.Error("expected InApp to be true")
	}

	if f.InApp && st.Culprit() != fmt.Sprintf("%s.trace", thisPackage) {
		t.Error("incorrect Culprit:", st.Culprit())
	}
}

// a
func trace() *Stacktrace {
	return NewStacktrace(0, 2, []string{thisPackage})
	// b
}

func derivePackage() (file, pack string) {
	// Get file name by seeking caller's file name.
	_, callerFile, _, ok := runtime.Caller(1)
	if !ok {
		return
	}

	// Trim file name
	file = callerFile
	for _, dir := range build.Default.SrcDirs() {
		dir := dir + string(filepath.Separator)
		if trimmed := strings.TrimPrefix(callerFile, dir); len(trimmed) < len(file) {
			file = trimmed
		}
	}

	// Now derive package name
	dir := filepath.Dir(callerFile)

	dirPkg, err := build.ImportDir(dir, build.AllowBinary)
	if err != nil {
		return
	}

	pack = dirPkg.ImportPath
	return
}

func init() {
	thisFile, thisPackage = derivePackage()
	functionNameTests = []FunctionNameTest{
		{0, thisPackage, "TestFunctionName"},
		{1, "testing", "tRunner"},
		{2, "runtime", "goexit"},
		{100, "", ""},
	}
}

// TestNewStacktrace_outOfBounds verifies that a context exceeding the number
// of lines in a file does not cause a panic.
func TestNewStacktrace_outOfBounds(t *testing.T) {
	st := NewStacktrace(0, 1000000, []string{thisPackage})
	f := st.Frames[len(st.Frames)-1]
	if f.ContextLine != "\tst := NewStacktrace(0, 1000000, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
}

func TestNewStacktrace_noFrames(t *testing.T) {
	st := NewStacktrace(999999999, 0, []string{})
	if st != nil {
		t.Errorf("expected st.Frames to be nil:", st)
	}
}

func TestFileContext(t *testing.T) {
	// reset the cache
	fileCache = make(map[string][][]byte)

	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal("failed to create temporary directory:", err)
	}
	defer os.RemoveAll(tempdir)

	okPath := filepath.Join(tempdir, "ok")
	missingPath := filepath.Join(tempdir, "missing")
	noPermissionPath := filepath.Join(tempdir, "noperms")

	err = ioutil.WriteFile(okPath, []byte("hello\nworld\n"), 0600)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}
	err = ioutil.WriteFile(noPermissionPath, []byte("no access\n"), 0000)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}

	tests := []struct {
		path          string
		expectedLines int
		expectedIndex int
	}{
		{okPath, 1, 0},
		{missingPath, 0, 0},
		{noPermissionPath, 0, 0},
	}
	for i, test := range tests {
		lines, index := fileContext(test.path, 1, 0)
		if !(len(lines) == test.expectedLines && index == test.expectedIndex) {
			t.Errorf("%d: fileContext(%#v, 1, 0) = %v, %v; expected len()=%d, %d",
				i, test.path, lines, index, test.expectedLines, test.expectedIndex)
		}
		if len(fileCache) != i+1 {
			t.Errorf("%d: result was not cached; len(fileCached)=%d", i, len(fileCache))
		}
	}
}
<commit_msg>Update stacktrace_test.go Errorf formatting <commit_after>package raven

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

type FunctionNameTest struct {
	skip int
	pack string
	name string
}

var (
	thisFile          string
	thisPackage       string
	functionNameTests []FunctionNameTest
)

func TestFunctionName(t *testing.T) {
	for _, test := range functionNameTests {
		pc, _, _, _ := runtime.Caller(test.skip)
		pack, name := functionName(pc)

		if pack != test.pack {
			t.Errorf("incorrect package; got %s, want %s", pack, test.pack)
		}
		if name != test.name {
			t.Errorf("incorrect function; got %s, want %s", name, test.name)
		}
	}
}

func TestStacktrace(t *testing.T) {
	st := trace()
	if st == nil {
		t.Error("got nil stacktrace")
	}
	if len(st.Frames) == 0 {
		t.Error("got zero frames")
	}

	f := st.Frames[len(st.Frames)-1]
	if f.Filename != thisFile {
		t.Errorf("incorrect Filename; got %s, want %s", f.Filename, thisFile)
	}
	if !strings.HasSuffix(f.AbsolutePath, thisFile) {
		t.Error("incorrect AbsolutePath:", f.AbsolutePath)
	}
	if f.Function != "trace" {
		t.Error("incorrect Function:", f.Function)
	}
	if f.Module != thisPackage {
		t.Error("incorrect Module:", f.Module)
	}
	if f.Lineno != 87 {
		t.Error("incorrect Lineno:", f.Lineno)
	}
	if f.ContextLine != "\treturn NewStacktrace(0, 2, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
	if len(f.PreContext) != 2 || f.PreContext[0] != "// a" || f.PreContext[1] != "func trace() *Stacktrace {" {
		t.Errorf("incorrect PreContext %#v", f.PreContext)
	}
	if len(f.PostContext) != 2 || f.PostContext[0] != "\t// b" || f.PostContext[1] != "}" {
		t.Errorf("incorrect PostContext %#v", f.PostContext)
	}
	_, filename, _, _ := runtime.Caller(0)
	runningInVendored := strings.Contains(filename, "vendor")
	if f.InApp != !runningInVendored {
		t.Error("expected InApp to be true")
	}

	if f.InApp && st.Culprit() != fmt.Sprintf("%s.trace", thisPackage) {
		t.Error("incorrect Culprit:", st.Culprit())
	}
}

// a
func trace() *Stacktrace {
	return NewStacktrace(0, 2, []string{thisPackage})
	// b
}

func derivePackage() (file, pack string) {
	// Get file name by seeking caller's file name.
	_, callerFile, _, ok := runtime.Caller(1)
	if !ok {
		return
	}

	// Trim file name
	file = callerFile
	for _, dir := range build.Default.SrcDirs() {
		dir := dir + string(filepath.Separator)
		if trimmed := strings.TrimPrefix(callerFile, dir); len(trimmed) < len(file) {
			file = trimmed
		}
	}

	// Now derive package name
	dir := filepath.Dir(callerFile)

	dirPkg, err := build.ImportDir(dir, build.AllowBinary)
	if err != nil {
		return
	}

	pack = dirPkg.ImportPath
	return
}

func init() {
	thisFile, thisPackage = derivePackage()
	functionNameTests = []FunctionNameTest{
		{0, thisPackage, "TestFunctionName"},
		{1, "testing", "tRunner"},
		{2, "runtime", "goexit"},
		{100, "", ""},
	}
}

// TestNewStacktrace_outOfBounds verifies that a context exceeding the number
// of lines in a file does not cause a panic.
func TestNewStacktrace_outOfBounds(t *testing.T) {
	st := NewStacktrace(0, 1000000, []string{thisPackage})
	f := st.Frames[len(st.Frames)-1]
	if f.ContextLine != "\tst := NewStacktrace(0, 1000000, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
}

func TestNewStacktrace_noFrames(t *testing.T) {
	st := NewStacktrace(999999999, 0, []string{})
	if st != nil {
		t.Errorf("expected st.Frames to be nil: %v", st)
	}
}

func TestFileContext(t *testing.T) {
	// reset the cache
	fileCache = make(map[string][][]byte)

	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal("failed to create temporary directory:", err)
	}
	defer os.RemoveAll(tempdir)

	okPath := filepath.Join(tempdir, "ok")
	missingPath := filepath.Join(tempdir, "missing")
	noPermissionPath := filepath.Join(tempdir, "noperms")

	err = ioutil.WriteFile(okPath, []byte("hello\nworld\n"), 0600)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}
	err = ioutil.WriteFile(noPermissionPath, []byte("no access\n"), 0000)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}

	tests := []struct {
		path          string
		expectedLines int
		expectedIndex int
	}{
		{okPath, 1, 0},
		{missingPath, 0, 0},
		{noPermissionPath, 0, 0},
	}
	for i, test := range tests {
		lines, index := fileContext(test.path, 1, 0)
		if !(len(lines) == test.expectedLines && index == test.expectedIndex) {
			t.Errorf("%d: fileContext(%#v, 1, 0) = %v, %v; expected len()=%d, %d",
				i, test.path, lines, index, test.expectedLines, test.expectedIndex)
		}
		if len(fileCache) != i+1 {
			t.Errorf("%d: result was not cached; len(fileCached)=%d", i, len(fileCache))
		}
	}
}
<|endoftext|>


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

//+build linux

package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
	"github.com/coreos/go-systemd/util"
	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/networking"
	stage1commontypes "github.com/coreos/rkt/stage1/common/types"
	stage1initcommon "github.com/coreos/rkt/stage1/init/common"
	"github.com/coreos/rkt/stage1/init/kvm"
	"github.com/hashicorp/errwrap"
)

const journalDir = "/var/log/journal"

// KvmNetworkingToSystemd generates systemd unit files for a pod according to network configuration
func KvmNetworkingToSystemd(p *stage1commontypes.Pod, n *networking.Networking) error {
	podRoot := common.Stage1RootfsPath(p.Root)

	// networking
	netDescriptions := kvm.GetNetworkDescriptions(n)
	if err := kvm.GenerateNetworkInterfaceUnits(filepath.Join(podRoot, stage1initcommon.UnitsDir), netDescriptions); err != nil {
		return errwrap.Wrap(errors.New("failed to transform networking to units"), err)
	}

	return nil
}

func mountSharedVolumes(root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	app := ra.App
	appName := ra.Name
	volumes := p.Manifest.Volumes
	vols := make(map[types.ACName]types.Volume)
	for _, v := range volumes {
		vols[v.Name] = v
	}

	sharedVolPath := common.SharedVolumesPath(root)
	if err := os.MkdirAll(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(errors.New("could not create shared volumes directory"), err)
	}
	if err := os.Chmod(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(fmt.Errorf("could not change permissions of %q", sharedVolPath), err)
	}

	imageManifest := p.Images[appName.String()]
	mounts := stage1initcommon.GenerateMounts(ra, vols, imageManifest)
	for _, m := range mounts {
		vol := vols[m.Volume]

		absRoot, err := filepath.Abs(p.Root) // Absolute path to the pod's rootfs.
		if err != nil {
			return errwrap.Wrap(errors.New("could not get pod's root absolute path"), err)
		}

		absAppRootfs := common.AppRootfsPath(absRoot, appName)
		if err != nil {
			return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName)
		}

		mntPath, err := stage1initcommon.EvaluateSymlinksInsideApp(absAppRootfs, m.Path)
		if err != nil {
			return errwrap.Wrap(fmt.Errorf("could not evaluate path %v", m.Path), err)
		}
		absDestination := filepath.Join(absAppRootfs, mntPath)
		shPath := filepath.Join(sharedVolPath, vol.Name.String())
		if err := stage1initcommon.PrepareMountpoints(shPath, absDestination, &vol, m.DockerImplicit); err != nil {
			return err
		}

		readOnly := stage1initcommon.IsMountReadOnly(vol, app.MountPoints)
		var source string
		switch vol.Kind {
		case "host":
			source = vol.Source
		case "empty":
			source = filepath.Join(common.SharedVolumesPath(root), vol.Name.String())
		default:
			return fmt.Errorf(`invalid volume kind %q. Must be one of "host" or "empty"`, vol.Kind)
		}
		if cleanedSource, err := filepath.EvalSymlinks(source); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not resolve symlink for source: %v", source), err)
		} else if err := ensureDestinationExists(cleanedSource, absDestination); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not create destination mount point: %v", absDestination), err)
		} else if err := doBindMount(cleanedSource, absDestination, readOnly); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Path, source, absDestination), err)
		}
	}
	return nil
}

func doBindMount(source, destination string, readOnly bool) error {
	if err := syscall.Mount(source, destination, "bind", syscall.MS_BIND, ""); err != nil {
		return err
	}
	if readOnly {
		return syscall.Mount(source, destination, "bind", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
	}
	return nil
}

func ensureDestinationExists(source, destination string) error {
	fileInfo, err := os.Stat(source)
	if err != nil {
		return errwrap.Wrap(fmt.Errorf("could not stat source location: %v", source), err)
	}

	targetPathParent, _ := filepath.Split(destination)
	if err := os.MkdirAll(targetPathParent, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(fmt.Errorf("could not create parent directory: %v", targetPathParent), err)
	}

	if fileInfo.IsDir() {
		if err := os.Mkdir(destination, stage1initcommon.SharedVolPerm); !os.IsExist(err) {
			return err
		}
	} else {
		if file, err := os.OpenFile(destination, os.O_CREATE, stage1initcommon.SharedVolPerm); err != nil {
			return err
		} else {
			file.Close()
		}
	}
	return nil
}

func prepareMountsForApp(s1Root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	// bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn)
	if err := mountSharedVolumes(s1Root, p, ra); err != nil {
		return errwrap.Wrap(errors.New("failed to prepare mount point"), err)
	}

	return nil
}

func KvmPrepareMounts(s1Root string, p *stage1commontypes.Pod) error {
	for i := range p.Manifest.Apps {
		ra := &p.Manifest.Apps[i]
		if err := prepareMountsForApp(s1Root, p, ra); err != nil {
			return errwrap.Wrap(fmt.Errorf("failed prepare mounts for app %q", ra.Name), err)
		}
	}

	return nil
}

func linkJournal(s1Root, machineID string) error {
	if !util.IsRunningSystemd() {
		return nil
	}

	absS1Root, err := filepath.Abs(s1Root)
	if err != nil {
		return err
	}

	// /var/log/journal doesn't exist on the host, don't do anything
	if _, err := os.Stat(journalDir); os.IsNotExist(err) {
		return nil
	}

	machineJournalDir := filepath.Join(journalDir, machineID)
	podJournalDir := filepath.Join(absS1Root, machineJournalDir)

	hostMachineID, err := util.GetMachineID()
	if err != nil {
		return err
	}

	// unlikely, machine ID is random (== pod UUID)
	if hostMachineID == machineID {
		return fmt.Errorf("host and pod machine IDs are equal (%s)", machineID)
	}

	fi, err := os.Lstat(machineJournalDir)
	switch {
	case os.IsNotExist(err):
		// good, we'll create the symlink
	case err != nil:
		return err
	// unlikely, machine ID is random (== pod UUID)
	default:
		if fi.IsDir() {
			if err := os.Remove(machineJournalDir); err != nil {
				return err
			}
		}

		link, err := os.Readlink(machineJournalDir)
		if err != nil {
			return err
		}

		if link == podJournalDir {
			return nil
		} else {
			if err := os.Remove(machineJournalDir); err != nil {
				return err
			}
		}
	}

	if err := os.Symlink(podJournalDir, machineJournalDir); err != nil {
		return err
	}

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

//+build linux

package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
	"github.com/coreos/go-systemd/util"
	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/networking"
	stage1commontypes "github.com/coreos/rkt/stage1/common/types"
	stage1initcommon "github.com/coreos/rkt/stage1/init/common"
	"github.com/coreos/rkt/stage1/init/kvm"
	"github.com/hashicorp/errwrap"
)

const journalDir = "/var/log/journal"

// KvmNetworkingToSystemd generates systemd unit files for a pod according to network configuration
func KvmNetworkingToSystemd(p *stage1commontypes.Pod, n *networking.Networking) error {
	podRoot := common.Stage1RootfsPath(p.Root)

	// networking
	netDescriptions := kvm.GetNetworkDescriptions(n)
	if err := kvm.GenerateNetworkInterfaceUnits(filepath.Join(podRoot, stage1initcommon.UnitsDir), netDescriptions); err != nil {
		return errwrap.Wrap(errors.New("failed to transform networking to units"), err)
	}

	return nil
}

func mountSharedVolumes(root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	app := ra.App
	appName := ra.Name
	volumes := p.Manifest.Volumes
	vols := make(map[types.ACName]types.Volume)
	for _, v := range volumes {
		vols[v.Name] = v
	}

	sharedVolPath := common.SharedVolumesPath(root)
	if err := os.MkdirAll(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(errors.New("could not create shared volumes directory"), err)
	}
	if err := os.Chmod(sharedVolPath, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(fmt.Errorf("could not change permissions of %q", sharedVolPath), err)
	}

	imageManifest := p.Images[appName.String()]
	mounts := stage1initcommon.GenerateMounts(ra, vols, imageManifest)
	for _, m := range mounts {
		vol := vols[m.Volume]

		absRoot, err := filepath.Abs(p.Root) // Absolute path to the pod's rootfs.
		if err != nil {
			return errwrap.Wrap(errors.New("could not get pod's root absolute path"), err)
		}

		absAppRootfs := common.AppRootfsPath(absRoot, appName)
		if err != nil {
			return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName)
		}

		mntPath, err := stage1initcommon.EvaluateSymlinksInsideApp(absAppRootfs, m.Path)
		if err != nil {
			return errwrap.Wrap(fmt.Errorf("could not evaluate path %v", m.Path), err)
		}
		absDestination := filepath.Join(absAppRootfs, mntPath)
		shPath := filepath.Join(sharedVolPath, vol.Name.String())
		if err := stage1initcommon.PrepareMountpoints(shPath, absDestination, &vol, m.DockerImplicit); err != nil {
			return err
		}

		readOnly := stage1initcommon.IsMountReadOnly(vol, app.MountPoints)
		var source string
		switch vol.Kind {
		case "host":
			source = vol.Source
		case "empty":
			source = filepath.Join(common.SharedVolumesPath(root), vol.Name.String())
		default:
			return fmt.Errorf(`invalid volume kind %q. Must be one of "host" or "empty"`, vol.Kind)
		}
		if cleanedSource, err := filepath.EvalSymlinks(source); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not resolve symlink for source: %v", source), err)
		} else if err := ensureDestinationExists(cleanedSource, absDestination); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not create destination mount point: %v", absDestination), err)
		} else if err := doBindMount(cleanedSource, absDestination, readOnly, vol.Recursive); err != nil {
			return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Path, source, absDestination), err)
		}
	}
	return nil
}

func doBindMount(source, destination string, readOnly bool, recursive *bool) error {
	var flags uintptr = syscall.MS_BIND | syscall.MS_REC
	if readOnly {
		flags |= syscall.MS_RDONLY
	}

	// Enable recursive by default and remove it if explicitly requested
	if recursive != nil && *recursive == false {
		flags ^= syscall.MS_REC
	}
	return syscall.Mount(source, destination, "bind", flags, "")
}

func ensureDestinationExists(source, destination string) error {
	fileInfo, err := os.Stat(source)
	if err != nil {
		return errwrap.Wrap(fmt.Errorf("could not stat source location: %v", source), err)
	}

	targetPathParent, _ := filepath.Split(destination)
	if err := os.MkdirAll(targetPathParent, stage1initcommon.SharedVolPerm); err != nil {
		return errwrap.Wrap(fmt.Errorf("could not create parent directory: %v", targetPathParent), err)
	}

	if fileInfo.IsDir() {
		if err := os.Mkdir(destination, stage1initcommon.SharedVolPerm); !os.IsExist(err) {
			return err
		}
	} else {
		if file, err := os.OpenFile(destination, os.O_CREATE, stage1initcommon.SharedVolPerm); err != nil {
			return err
		} else {
			file.Close()
		}
	}
	return nil
}

func prepareMountsForApp(s1Root string, p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
	// bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn)
	if err := mountSharedVolumes(s1Root, p, ra); err != nil {
		return errwrap.Wrap(errors.New("failed to prepare mount point"), err)
	}

	return nil
}

func KvmPrepareMounts(s1Root string, p *stage1commontypes.Pod) error {
	for i := range p.Manifest.Apps {
		ra := &p.Manifest.Apps[i]
		if err := prepareMountsForApp(s1Root, p, ra); err != nil {
			return errwrap.Wrap(fmt.Errorf("failed prepare mounts for app %q", ra.Name), err)
		}
	}

	return nil
}

func linkJournal(s1Root, machineID string) error {
	if !util.IsRunningSystemd() {
		return nil
	}

	absS1Root, err := filepath.Abs(s1Root)
	if err != nil {
		return err
	}

	// /var/log/journal doesn't exist on the host, don't do anything
	if _, err := os.Stat(journalDir); os.IsNotExist(err) {
		return nil
	}

	machineJournalDir := filepath.Join(journalDir, machineID)
	podJournalDir := filepath.Join(absS1Root, machineJournalDir)

	hostMachineID, err := util.GetMachineID()
	if err != nil {
		return err
	}

	// unlikely, machine ID is random (== pod UUID)
	if hostMachineID == machineID {
		return fmt.Errorf("host and pod machine IDs are equal (%s)", machineID)
	}

	fi, err := os.Lstat(machineJournalDir)
	switch {
	case os.IsNotExist(err):
		// good, we'll create the symlink
	case err != nil:
		return err
	// unlikely, machine ID is random (== pod UUID)
	default:
		if fi.IsDir() {
			if err := os.Remove(machineJournalDir); err != nil {
				return err
			}
		}

		link, err := os.Readlink(machineJournalDir)
		if err != nil {
			return err
		}

		if link == podJournalDir {
			return nil
		} else {
			if err := os.Remove(machineJournalDir); err != nil {
				return err
			}
		}
	}

	if err := os.Symlink(podJournalDir, machineJournalDir); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package enmime

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
)

// Terminology from RFC 2047:
//  encoded-word: the entire =?charset?encoding?encoded-text?= string
//  charset: the character set portion of the encoded word
//  encoding: the character encoding type used for the encoded-text
//  encoded-text: the text we are decoding
const (
	PLAIN    int = iota // In plain text
	RESET               // Parse error, output encoded-word as plaintext
	CHARSET             // In charset name
	ENCODING            // In encoding name
	ENCTEXT             // In encoded-text
	SPACE               // Space following an encoded-word
)

// Decode a MIME header per RFC 2047
func decodeHeader(input string) (utf8 string, err error) {
	if ! strings.Contains(input, "=?") {
		// Don't scan if there is nothing to do here
		return input, nil
	}

	outbuf := new(bytes.Buffer)
	state := PLAIN
	startPos := 0
	var charsetBytes, encodingBytes, encTextBytes []byte

	for pos := 0; pos < len(input); pos++ {
		ch := input[pos]
		switch state {
		case SPACE:
			// Eat space characters only between encoded words
			switch ch {
			case ' ', '\t', '\r', '\n':
				// Still in space
				continue
			case '=':
				if len(input) > pos+1 && input[pos+1] == '?' {
					// Start of new encoded word.  If the word is valid, we want to eat
					// the whitespace.  If not, startPos was defined in transition to SPACE,
					// and we will output it.
					pos++
					state = CHARSET
					charsetBytes = make([]byte, 0, 128)
					continue
				}
			}
			// We hit plain text, will need to output whitespace
			for i := startPos; i <= pos; i++ {
				outbuf.WriteByte(input[i])
			}
			state = PLAIN
		case PLAIN:
			// Scan for start of encoded word: =?
			if ch == '=' && len(input) > pos+1 && input[pos+1] == '?' {
				// Save start in case this turns out to be corrupt
				startPos = pos
				pos++
				state = CHARSET
				charsetBytes = make([]byte, 0, 128)
				continue
			}

			// Plain text
			outbuf.WriteByte(ch)
		case RESET:
			// There was a problem parsing an encoded-word, recover by outputting
			// plain-text until next encoded word
			for i := startPos; i <= pos; i++ {
				outbuf.WriteByte(input[i])
			}
			state = PLAIN
		case CHARSET:
			// Parse character set
			switch {
			case isTokenChar(ch):
				// Part of charset name
				charsetBytes = append(charsetBytes, ch)
			case ch == '?':
				// End of charset name
				state = ENCODING
				encodingBytes = make([]byte, 0, 128)
			default:
				state = RESET
			}
		case ENCODING:
			// Parse encoding
			switch {
			case isTokenChar(ch):
				// Part of encoding name
				encodingBytes = append(encodingBytes, ch)
			case ch == '?':
				// End of encoding
				state = ENCTEXT
				encTextBytes = make([]byte, 0, 128)
			default:
				state = RESET
			}
		case ENCTEXT:
			// Decode encoded-text
			switch {
			case ch < 33:
				// No controls or space allowed
				state = RESET
			case ch > 126:
				// No DEL or extended ascii allowed
				state = RESET
			case ch == '?':
				if len(input) > pos+1 && input[pos+1] == '=' {
					text, err := convertText(charsetBytes, encodingBytes, encTextBytes)
					if err == nil {
						outbuf.WriteString(text)
						pos++
						// Entering post-word space
						state = SPACE
						startPos = pos+1
					} else {
						// Conversion failed
						state = RESET
					}
				} else {
					// Invalid termination
					state = RESET
				}
			default:
				encTextBytes = append(encTextBytes, ch)
			}
		}
	}

	return outbuf.String(), nil
}

// Convert the encTextBytes to UTF-8 and return as a string
func convertText(charsetBytes, encodingBytes, encTextBytes []byte) (string, error) {
	encoding := strings.ToLower(string(encodingBytes))
	switch encoding {
	case "b":
		// Base64 encoded
		textBytes, err := decodeBase64(encTextBytes)
		return string(textBytes), err
	case "q":
		// Quoted printable encoded
		textBytes, err := decodeQuotedPrintable(encTextBytes)
		return string(textBytes), err
	default:
		return "", fmt.Errorf("Invalid encoding: %v", encoding)
	}
}

func decodeQuotedPrintable(input []byte) ([]byte, error) {
	output := make([]byte, 0, len(input))
	for pos := 0; pos < len(input); pos++ {
		switch ch := input[pos]; ch {
		case '_':
			output = append(output, ' ')
		case '=':
			if len(input) < pos+3 {
				return nil, fmt.Errorf("Ran out of chars parsing: %v", input[pos:])
			}
			x, err := strconv.ParseInt(string(input[pos+1:pos+3]), 16, 64)
			if err != nil {
				return nil, fmt.Errorf("Failed to convert: %v", input[pos:pos+3])
			}
			output = append(output, byte(x))
			pos += 2
		default:
			output = append(output, input[pos])
		}
	}
	return output, nil
}

func decodeBase64(input []byte) ([]byte, error) {
	output := make([]byte, len(input))
	n, err := base64.StdEncoding.Decode(output, input)
	return output[:n], err
}

// Is this an especial character per RFC 2047
func isEspecialChar(ch byte) bool {
	switch ch {
	case '(', ')', '<', '>', '@', ',', ';', ':':
		return true
	case '"', '/', '[', ']', '?', '.', '=':
		return true
	}
	return false
}

// Is this a "token" character per RFC 2047
func isTokenChar(ch byte) bool {
	// No controls or space
	if ch < 33 {
		return false
	}
	// No DEL or extended ascii
	if ch > 126 {
		return false
	}
	// No especials
	return !isEspecialChar(ch)
}
<commit_msg>Refactored parser<commit_after>package enmime

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
)

func debug(format string, args ...interface{}) {
	if false {
		fmt.Printf(format, args...)
		fmt.Println()
	}
}

// Terminology from RFC 2047:
//  encoded-word: the entire =?charset?encoding?encoded-text?= string
//  charset: the character set portion of the encoded word
//  encoding: the character encoding type used for the encoded-text
//  encoded-text: the text we are decoding

// State function modeled on Rob Pike's lexer talk (see source of
// Go's text/template/parser/lex.go)
type stateFn func(*headerDec) stateFn

const eof = -1

// headerDec holds the state of the scanner and an output buffer
type headerDec struct {
	input    []byte  // Input to decode
	state    stateFn // Next state
	start    int     // Start of text we don't yet know what to do with
	pos      int     // Current parsing position
	charset  string  // Character set of current encoded word
	encoding string  // Encoding of current encoded word
	outbuf   bytes.Buffer
}

// next returns the next rune in the input
func (h *headerDec) next() rune {
	if h.pos >= len(h.input) {
		return eof
	}
	r := h.input[h.pos]
	h.pos++
	return rune(r)
}

// backup a single rune
func (h *headerDec) backup() {
	if h.pos > 0 {
		h.pos--
	}
}

// peek at the next rune without consuming it
func (h *headerDec) peek() rune {
	r := h.next()
	h.backup()
	return r
}

// ignore will forget all input between start and pos
func (h *headerDec) ignore() {
	h.start = h.pos
}

// output will append all input from start to pos (inclusive) to outbuf
func (h *headerDec) output() {
	if h.pos > h.start {
		h.outbuf.Write(h.input[h.start : h.pos])
		h.start = h.pos
	}
}

// accept consumes the next rune if it's part of the valid set
func (h *headerDec) accept(valid string) bool {
	if r := h.next(); r != eof {
		if strings.IndexRune(valid, r) >= 0 {
			return true
		}
		h.backup()
	}
	return false
}

// Decode a MIME header per RFC 2047
func decodeHeader(input string) (utf8 string, err error) {
	//if ! strings.Contains(input, "=?") {
	//	// Don't scan if there is nothing to do here
	//	return input, nil
	//}

	h := &headerDec{
		input: []byte(input),
		state: plainState,
	}

	debug("Starting parse of: '%v'\n", input)

	for h.state != nil {
		h.state = h.state(h)
	}

	return h.outbuf.String(), nil
}

// State: Reset, output mangled encoded-word as plaintext
//
// There was a problem parsing an encoded-word, recover by outputting
// plain-text until next encoded word
func resetState(h *headerDec) stateFn {
	debug("entering reset state with buf %q", h.outbuf.String())
	h.output()
	return plainState
}

// State: In plain text
func plainState(h *headerDec) stateFn {
	debug("entering plain state with buf %q", h.outbuf.String())
	for r := h.next(); r != eof; r = h.next() {
		//fmt.Printf(" %q\n", r)
		// Scan for start of encoded word: =?
		if r == '=' {
			// Dump out preceeding plaintext, w/o =
			h.backup()
			h.output()
			h.next()
			if h.accept("?") {
				return charsetState
			}
		}
	}
	// Hitting EOF in plain state means we are done
	h.output()
	return nil
}

// State: In charset name
func charsetState(h *headerDec) stateFn {
	debug("entering charset state with buf %q", h.outbuf.String())
	myStart := h.pos
	for r := h.next(); r != eof; r = h.next() {
		// Parse character set
		switch {
		case isTokenChar(r):
			// Part of charset name, keep going
		case r == '?':
			// End of charset name
			h.charset = string(h.input[myStart : h.pos-1])
			debug("charset %q", h.charset)
			return encodingState
		default:
			// Invalid character
			return resetState
		}
	}
	// Hit eof!
	return resetState
}

// State: In encoding name
func encodingState(h *headerDec) stateFn {
	debug("entering encoding state with buf %q", h.outbuf.String())
	myStart := h.pos
	for r := h.next(); r != eof; r = h.next() {
		// Parse encoding
		switch {
		case isTokenChar(r):
			// Part of encoding name, keep going
		case r == '?':
			// End of encoding name
			h.encoding = string(h.input[myStart : h.pos-1])
			debug("encoding %q", h.encoding)
			return encTextState
		default:
			// Invalid character
			return resetState
		}
	}
	// Hit eof!
	return resetState
}

// State: In encoded-text
func encTextState(h *headerDec) stateFn {
	debug("entering encText state with buf %q", h.outbuf.String())
	myStart := h.pos
	for r := h.next(); r != eof; r = h.next() {
		// Decode encoded-text
		switch {
		case r < 33:
			// No controls or space allowed
			debug("Encountered control character")
			return resetState
		case r > 126:
			// No DEL or extended ascii allowed
			debug("Encountered DEL or extended ascii")
			return resetState
		case r == '?':
			if h.accept("=") {
				text, err := convertText(h.charset, h.encoding, h.input[myStart:h.pos-2])
				if err == nil {
					debug("Text converted to: %q", text)
					h.outbuf.WriteString(text)
					h.ignore()
					// Entering post-word space
					return spaceState
				} else {
					// Conversion failed
					debug("Text conversion failed: %q", err)
					return resetState
				}
			} else {
				// Invalid termination
				debug("Invalid termination")
				return resetState
			}
		}
	}
	// Hit eof!
	return resetState
}

// State: White space following an encoded-word
func spaceState(h *headerDec) stateFn {
	debug("entering space state with buf %q", h.outbuf.String())
Loop:
	for {
		// Eat space characters only between encoded words
		switch {
		case h.accept(" \t\r\n"):
			debug("In space")
			// Still in space
		case h.accept("="):
			debug("In =")
			if h.accept("?") {
				// Start of new encoded word.  If the word is valid, we want to eat
				// the whitespace.  If not, h.start was set in transition to SPACE,
				// and we will output the space.
				return charsetState
			}
		default:
			debug("In default")
			break Loop
		}
	}
	debug("In plain")
	// We hit plain text, will need to output whitespace
	h.output()
	return plainState
}

// Convert the encTextBytes to UTF-8 and return as a string
func convertText(charset string, encoding string, encTextBytes []byte) (string, error) {
	switch strings.ToLower(encoding) {
	case "b":
		// Base64 encoded
		textBytes, err := decodeBase64(encTextBytes)
		return string(textBytes), err
	case "q":
		// Quoted printable encoded
		textBytes, err := decodeQuotedPrintable(encTextBytes)
		return string(textBytes), err
	default:
		return "", fmt.Errorf("Invalid encoding: %v", encoding)
	}
}

func decodeQuotedPrintable(input []byte) ([]byte, error) {
	output := make([]byte, 0, len(input))
	for pos := 0; pos < len(input); pos++ {
		switch ch := input[pos]; ch {
		case '_':
			output = append(output, ' ')
		case '=':
			if len(input) < pos+3 {
				return nil, fmt.Errorf("Ran out of chars parsing: %v", input[pos:])
			}
			x, err := strconv.ParseInt(string(input[pos+1:pos+3]), 16, 64)
			if err != nil {
				return nil, fmt.Errorf("Failed to convert: %v", input[pos:pos+3])
			}
			output = append(output, byte(x))
			pos += 2
		default:
			output = append(output, input[pos])
		}
	}
	return output, nil
}

func decodeBase64(input []byte) ([]byte, error) {
	output := make([]byte, len(input))
	n, err := base64.StdEncoding.Decode(output, input)
	return output[:n], err
}

// Is this an especial character per RFC 2047
func isEspecialChar(ch rune) bool {
	switch ch {
	case '(', ')', '<', '>', '@', ',', ';', ':':
		return true
	case '"', '/', '[', ']', '?', '.', '=':
		return true
	}
	return false
}

// Is this a "token" character per RFC 2047
func isTokenChar(ch rune) bool {
	// No controls or space
	if ch < 33 {
		return false
	}
	// No DEL or extended ascii
	if ch > 126 {
		return false
	}
	// No especials
	return !isEspecialChar(ch)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net"
	"net/http"
	"regexp"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/julienschmidt/httprouter"
)

type HealthStatus struct {
	Refs  string
	Admin string
	Probe string
}
type Backends map[string]HealthStatus
type Servers map[string]Backends

type HealthPost struct {
	Set_health string
}

func GetHealth(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	service := ps.ByName("service")
	backend := ps.ByName("backend")
	if s, ok := services[service]; ok {
		// We need the WaitGroup for some awesome Go concurrency
		var wg sync.WaitGroup
		servers := Servers{}
		for _, server := range s.Hosts {
			// Increment the WaitGroup counter.
			wg.Add(1)
			go func(server string) {
				// Decrement the counter when the goroutine completes.
				defer wg.Done()
				backends := Backends{}
				backends = StatusHealth(server, s.Secret, backend)
				servers[server] = backends
			}(server)
		}
		wg.Wait()
		r.JSON(w, http.StatusOK, servers)
	} else {
		w.Write([]byte("Service could not be found."))
		return
	}
}

func PostHealth(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	service := ps.ByName("service")
	backend := ps.ByName("backend")
	healthpost := HealthPost{}
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(&healthpost)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	if healthpost.Set_health == "" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Set_health is required"))
		return
	}
	if s, ok := services[service]; ok {
		// We need the WaitGroup for some awesome Go concurrency
		var wg sync.WaitGroup
		messages := Messages{}
		for _, server := range s.Hosts {
			// Increment the WaitGroup counter.
			wg.Add(1)
			go func(server string) {
				// Decrement the counter when the goroutine completes.
				defer wg.Done()
				message := Message{}
				message.Msg = UpdateHealth(server, s.Secret, backend, healthpost, req)
				messages[server] = message
			}(server)
		}
		wg.Wait()
		r.JSON(w, http.StatusOK, messages)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Service could not be found."))
		return
	}
}

func UpdateHealth(server string, secret string, backend string, healthpost HealthPost, req *http.Request) string {
	conn, err := net.Dial("tcp", server)
	if err != nil {
		log.Println(err)
		return err.Error()
	}
	defer conn.Close()
	err = varnishAuth(server, secret, conn)
	if err != nil {
		log.Println(err)
	}
	conn.Write([]byte("backend.set_health " + backend + " " + healthpost.Set_health + "\n"))
	// again, 64 bytes is enough for this.
	byte_status := make([]byte, 64)
	_, err = conn.Read(byte_status)
	if err != nil {
		log.Printf("Could not read packet : %s", err.Error())
		return err.Error()
	}
	// cast byte to string and only keep the status code (always max 13 char), the rest we dont care.
	status := string(byte_status)[0:12]
	status = strings.Trim(status, " ")
	entry := logrus.WithFields(logrus.Fields{
		"set_health": healthpost.Set_health,
		"backend":    backend,
		"server":     server,
		"status":     status,
	})
	if reqID := req.Header.Get("X-Request-Id"); reqID != "" {
		entry = entry.WithField("request_id", reqID)
	}
	entry.Info("health")
	return "updated with status " + status
}

func StatusHealth(server string, secret string, backend string) Backends {
	backends := Backends{}
	conn, err := net.Dial("tcp", server)
	if err != nil {
		log.Println(err)
		return backends
	}
	defer conn.Close()
	err = varnishAuth(server, secret, conn)
	if err != nil {
		log.Println(err)
	}
	if backend == "" {
		conn.Write([]byte("backend.list\n"))
	} else {
		conn.Write([]byte("backend.list " + backend + "\n"))
	}
	byte_health := make([]byte, 512)
	n, err := conn.Read(byte_health)
	if err != nil {
		log.Printf("Could not read packet : %s", err.Error())
		return backends
	}
	status := string(byte_health[:n])
	for _, line := range strings.Split(status, "\n") {
		rp := regexp.MustCompile("^(\\S+\\))[\\s]+(\\S+)[\\s]+(\\S+)[\\s]+(.+)")
		list := rp.FindStringSubmatch(line)
		if len(list) > 0 {
			hs := HealthStatus{}
			hs.Refs = list[2]
			hs.Admin = list[3]
			hs.Probe = list[4]
			backends[list[1]] = hs
		}
	}
	return backends
}
<commit_msg>increased the bytes read from health status, was not enough for servers with lots of backends<commit_after>package main

import (
	"encoding/json"
	"log"
	"net"
	"net/http"
	"regexp"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/julienschmidt/httprouter"
)

type HealthStatus struct {
	Refs  string
	Admin string
	Probe string
}
type Backends map[string]HealthStatus
type Servers map[string]Backends

type HealthPost struct {
	Set_health string
}

func GetHealth(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	service := ps.ByName("service")
	backend := ps.ByName("backend")
	if s, ok := services[service]; ok {
		// We need the WaitGroup for some awesome Go concurrency
		var wg sync.WaitGroup
		servers := Servers{}
		for _, server := range s.Hosts {
			// Increment the WaitGroup counter.
			wg.Add(1)
			go func(server string) {
				// Decrement the counter when the goroutine completes.
				defer wg.Done()
				backends := Backends{}
				backends = StatusHealth(server, s.Secret, backend)
				servers[server] = backends
			}(server)
		}
		wg.Wait()
		r.JSON(w, http.StatusOK, servers)
	} else {
		w.Write([]byte("Service could not be found."))
		return
	}
}

func PostHealth(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	service := ps.ByName("service")
	backend := ps.ByName("backend")
	healthpost := HealthPost{}
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(&healthpost)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	if healthpost.Set_health == "" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Set_health is required"))
		return
	}
	if s, ok := services[service]; ok {
		// We need the WaitGroup for some awesome Go concurrency
		var wg sync.WaitGroup
		messages := Messages{}
		for _, server := range s.Hosts {
			// Increment the WaitGroup counter.
			wg.Add(1)
			go func(server string) {
				// Decrement the counter when the goroutine completes.
				defer wg.Done()
				message := Message{}
				message.Msg = UpdateHealth(server, s.Secret, backend, healthpost, req)
				messages[server] = message
			}(server)
		}
		wg.Wait()
		r.JSON(w, http.StatusOK, messages)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Service could not be found."))
		return
	}
}

func UpdateHealth(server string, secret string, backend string, healthpost HealthPost, req *http.Request) string {
	conn, err := net.Dial("tcp", server)
	if err != nil {
		log.Println(err)
		return err.Error()
	}
	defer conn.Close()
	err = varnishAuth(server, secret, conn)
	if err != nil {
		log.Println(err)
	}
	conn.Write([]byte("backend.set_health " + backend + " " + healthpost.Set_health + "\n"))
	// again, 64 bytes is enough for this.
	byte_status := make([]byte, 64)
	_, err = conn.Read(byte_status)
	if err != nil {
		log.Printf("Could not read packet : %s", err.Error())
		return err.Error()
	}
	// cast byte to string and only keep the status code (always max 13 char), the rest we dont care.
	status := string(byte_status)[0:12]
	status = strings.Trim(status, " ")
	entry := logrus.WithFields(logrus.Fields{
		"set_health": healthpost.Set_health,
		"backend":    backend,
		"server":     server,
		"status":     status,
	})
	if reqID := req.Header.Get("X-Request-Id"); reqID != "" {
		entry = entry.WithField("request_id", reqID)
	}
	entry.Info("health")
	return "updated with status " + status
}

func StatusHealth(server string, secret string, backend string) Backends {
	backends := Backends{}
	conn, err := net.Dial("tcp", server)
	if err != nil {
		log.Println(err)
		return backends
	}
	defer conn.Close()
	err = varnishAuth(server, secret, conn)
	if err != nil {
		log.Println(err)
	}
	if backend == "" {
		conn.Write([]byte("backend.list\n"))
	} else {
		conn.Write([]byte("backend.list " + backend + "\n"))
	}
	byte_health := make([]byte, 2048)
	n, err := conn.Read(byte_health)
	if err != nil {
		log.Printf("Could not read packet : %s", err.Error())
		return backends
	}
	status := string(byte_health[:n])
	for _, line := range strings.Split(status, "\n") {
		rp := regexp.MustCompile("^(\\S+\\))[\\s]+(\\S+)[\\s]+(\\S+)[\\s]+(.+)")
		list := rp.FindStringSubmatch(line)
		if len(list) > 0 {
			hs := HealthStatus{}
			hs.Refs = list[2]
			hs.Admin = list[3]
			hs.Probe = list[4]
			backends[list[1]] = hs
		}
	}
	return backends
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	mmap "github.com/edsrzf/mmap-go"

	"github.com/nsf/termbox-go"
)

const PROGRAM_NAME = "hecate"

func mainLoop(bytes []byte, style Style) {
	screens := defaultScreensForData(bytes)
	display_screen := screens[DATA_SCREEN_INDEX]
	layoutAndDrawScreen(display_screen, style)
	for {
		event := termbox.PollEvent()
		if event.Type == termbox.EventKey {
			handleSpecialKeys(event.Key)

			new_screen_index := display_screen.handleKeyEvent(event)
			if new_screen_index < len(screens) {
				display_screen = screens[new_screen_index]
				layoutAndDrawScreen(display_screen, style)
			} else {
				break
			}
		}
		if event.Type == termbox.EventResize {
			layoutAndDrawScreen(display_screen, style)
		}
	}
}

func main() {
	var err error

	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s <filename>\n", PROGRAM_NAME)
		os.Exit(1)
	}
	path := os.Args[1]

	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("Error opening file: %q\n", err.Error())
		os.Exit(1)
	}

	fi, err := file.Stat()
	if err != nil {
		fmt.Printf("Error stat'ing file: %q\n", err.Error())
		os.Exit(1)
	}

	if fi.Size() < 8 {
		fmt.Printf("File %s is too short to be edited\n", path)
		os.Exit(1)
	}

	mm, err := mmap.Map(file, mmap.RDONLY, 0)
	if err != nil {
		fmt.Printf("Error mmap'ing file: %q\n", err.Error())
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	style := defaultStyle()
	termbox.SetOutputMode(termbox.Output256)

	mainLoop(mm, style)
}
<commit_msg>respect the platform secific outputmode<commit_after>package main

import (
	"fmt"
	"os"

	mmap "github.com/edsrzf/mmap-go"

	"github.com/nsf/termbox-go"
)

const PROGRAM_NAME = "hecate"

func mainLoop(bytes []byte, style Style) {
	screens := defaultScreensForData(bytes)
	display_screen := screens[DATA_SCREEN_INDEX]
	layoutAndDrawScreen(display_screen, style)
	for {
		event := termbox.PollEvent()
		if event.Type == termbox.EventKey {
			handleSpecialKeys(event.Key)

			new_screen_index := display_screen.handleKeyEvent(event)
			if new_screen_index < len(screens) {
				display_screen = screens[new_screen_index]
				layoutAndDrawScreen(display_screen, style)
			} else {
				break
			}
		}
		if event.Type == termbox.EventResize {
			layoutAndDrawScreen(display_screen, style)
		}
	}
}

func main() {
	var err error

	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s <filename>\n", PROGRAM_NAME)
		os.Exit(1)
	}
	path := os.Args[1]

	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("Error opening file: %q\n", err.Error())
		os.Exit(1)
	}

	fi, err := file.Stat()
	if err != nil {
		fmt.Printf("Error stat'ing file: %q\n", err.Error())
		os.Exit(1)
	}

	if fi.Size() < 8 {
		fmt.Printf("File %s is too short to be edited\n", path)
		os.Exit(1)
	}

	mm, err := mmap.Map(file, mmap.RDONLY, 0)
	if err != nil {
		fmt.Printf("Error mmap'ing file: %q\n", err.Error())
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	style := defaultStyle()
	termbox.SetOutputMode(outputMode)

	mainLoop(mm, style)
}
<|endoftext|>