<commit_before>// Package store provides a distributed SQLite instance.
//
// Distributed consensus is provided via the Raft algorithm.
package store

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
	sql "github.com/otoolep/rqlite/db"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type command struct {
	Tx      bool     `json:"tx,omitempty"`
	Queries []string `json:"queries,omitempty"`
}

// Store is a SQLite database, where all changes are made via Raft consensus.
type Store struct {
	raftDir  string
	raftBind string

	mu sync.Mutex

	raft *raft.Raft // The consensus mechanism
	db   *sql.DB    // The underlying SQLite store

	logger *log.Logger

	SQLiteDB string
}

// New returns a new Store.
func New(db *sql.DB, dir, bind string) *Store {
	return &Store{
		raftDir:  dir,
		raftBind: bind,
		db:       db,
		logger:   log.New(os.Stderr, "[store] ", log.LstdFlags),
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomesthe first node, and therefore leader, of the cluster.
func (s *Store) Open(enableSingle bool) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()

	// Check for any existing peers.
	peers, err := readPeersJSON(filepath.Join(s.raftDir, "peers.json"))
	if err != nil {
		return err
	}

	// Allow the node to entry single-mode, potentially electing itself, if
	// explicitly enabled and there is only 1 node in the cluster already.
	if enableSingle && len(peers) <= 1 {
		s.logger.Println("enabling single-node mode")
		config.EnableSingleNode = true
		config.DisableBootstrapAfterElect = false
	}

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", s.raftBind)
	if err != nil {
		return err
	}
	transport, err := raft.NewTCPTransport(s.raftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}

	// Create peer storage.
	peerStore := raft.NewJSONPeers(s.raftDir, transport)

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(s.raftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return fmt.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore, err := raftboltdb.NewBoltStore(filepath.Join(s.raftDir, "raft.db"))
	if err != nil {
		return fmt.Errorf("new bolt store: %s", err)
	}

	// Instantiate the Raft systems.
	ra, err := raft.NewRaft(config, (*fsm)(s), logStore, logStore, snapshots, peerStore, transport)
	if err != nil {
		return fmt.Errorf("new raft: %s", err)
	}
	s.raft = ra

	return nil
}

func (s *Store) Execute(queries []string, tx bool) ([]*sql.Result, error) {
	if s.raft.State() != raft.Leader {
		return nil, fmt.Errorf("not leader")
	}

	c := &command{
		Tx:      tx,
		Queries: queries,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return nil, err
	}

	f := s.raft.Apply(b, raftTimeout)
	if e := f.(raft.Future); e.Error() != nil {
		return nil, e.Error()
	}

	r := f.Response().(*fsmResponse)
	return r.results, r.error
}

func (s *Store) Query(queries []string, tx bool) ([]*sql.Rows, error) {
	r, err := s.db.Query(queries, tx)
	return r, err
}

// Join joins a node, located at addr, to this store. The node must be ready to
// respond to Raft communications at that address.
func (s *Store) Join(addr string) error {
	s.logger.Printf("received join request for remote node as %s", addr)

	f := s.raft.AddPeer(addr)
	if f.Error() != nil {
		return f.Error()
	}
	s.logger.Printf("node at %s joined successfully", addr)
	return nil
}

type fsm Store

type fsmResponse struct {
	results []*sql.Result
	error   error
}

// Apply applies a Raft log entry to the database.
func (f *fsm) Apply(l *raft.Log) interface{} {
	var c command
	if err := json.Unmarshal(l.Data, &c); err != nil {
		panic(fmt.Sprintf("failed to unmarshal command: %s", err.Error()))
	}

	r, err := f.db.Execute(c.Queries, c.Tx)

	return &fsmResponse{results: r, error: err}
}

// Snapshot returns a snapshot of the database. The caller must ensure that
// no transaction is taking place during this call.
//
// http://sqlite.org/howtocorrupt.html states it is safe to do this
// as long as no transaction is in progress. XXX HOW DO TO DO? RWLock?
func (f *fsm) Snapshot() (raft.FSMSnapshot, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

    b, err := ioutil.ReadFile(f.db.Path()) /// XXX Could Snapshot be used? File copy might be faster.
    if err != nil {
        log.Printf("Failed to generate snapshot: %s", err.Error())
        return nil, err
    }
    return &fsmSnapshot{data: b}, nil
}

// Restore restores the database to a previous state.
func (f *fsm) Restore(rc io.ReadCloser) error {
	// Need to write bytes to SQLite file.
	// Open it with desired DSN params.
	// Implies the Open call above, should always blow away existing database, since
	// it will be restored completely from log, or from log and remaining log entries.
	return nil
}

type fsmSnapshot struct {
	data []byte
}

func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
	err := func() error {
		// Write data to sink.
		if _, err := sink.Write(f.data); err != nil {
			return err
		}

		// Close the sink.
		if err := sink.Close(); err != nil {
			return err
		}

		return nil
	}()

	if err != nil {
		sink.Cancel()
		return err
	}

	return nil
}

func (f *fsmSnapshot) Release() {}

func readPeersJSON(path string) ([]string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	if len(b) == 0 {
		return nil, nil
	}

	var peers []string
	dec := json.NewDecoder(bytes.NewReader(b))
	if err := dec.Decode(&peers); err != nil {
		return nil, err
	}

	return peers, nil
}
<commit_msg>Apply() and Snapshot NOT called concurrently<commit_after>// Package store provides a distributed SQLite instance.
//
// Distributed consensus is provided via the Raft algorithm.
package store

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
	sql "github.com/otoolep/rqlite/db"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type command struct {
	Tx      bool     `json:"tx,omitempty"`
	Queries []string `json:"queries,omitempty"`
}

// Store is a SQLite database, where all changes are made via Raft consensus.
type Store struct {
	raftDir  string
	raftBind string

	mu sync.Mutex

	raft *raft.Raft // The consensus mechanism
	db   *sql.DB    // The underlying SQLite store

	logger *log.Logger

	SQLiteDB string
}

// New returns a new Store.
func New(db *sql.DB, dir, bind string) *Store {
	return &Store{
		raftDir:  dir,
		raftBind: bind,
		db:       db,
		logger:   log.New(os.Stderr, "[store] ", log.LstdFlags),
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomesthe first node, and therefore leader, of the cluster.
func (s *Store) Open(enableSingle bool) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()

	// Check for any existing peers.
	peers, err := readPeersJSON(filepath.Join(s.raftDir, "peers.json"))
	if err != nil {
		return err
	}

	// Allow the node to entry single-mode, potentially electing itself, if
	// explicitly enabled and there is only 1 node in the cluster already.
	if enableSingle && len(peers) <= 1 {
		s.logger.Println("enabling single-node mode")
		config.EnableSingleNode = true
		config.DisableBootstrapAfterElect = false
	}

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", s.raftBind)
	if err != nil {
		return err
	}
	transport, err := raft.NewTCPTransport(s.raftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}

	// Create peer storage.
	peerStore := raft.NewJSONPeers(s.raftDir, transport)

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(s.raftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return fmt.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore, err := raftboltdb.NewBoltStore(filepath.Join(s.raftDir, "raft.db"))
	if err != nil {
		return fmt.Errorf("new bolt store: %s", err)
	}

	// Instantiate the Raft systems.
	ra, err := raft.NewRaft(config, (*fsm)(s), logStore, logStore, snapshots, peerStore, transport)
	if err != nil {
		return fmt.Errorf("new raft: %s", err)
	}
	s.raft = ra

	return nil
}

func (s *Store) Execute(queries []string, tx bool) ([]*sql.Result, error) {
	if s.raft.State() != raft.Leader {
		return nil, fmt.Errorf("not leader")
	}

	c := &command{
		Tx:      tx,
		Queries: queries,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return nil, err
	}

	f := s.raft.Apply(b, raftTimeout)
	if e := f.(raft.Future); e.Error() != nil {
		return nil, e.Error()
	}

	r := f.Response().(*fsmResponse)
	return r.results, r.error
}

func (s *Store) Query(queries []string, tx bool) ([]*sql.Rows, error) {
	r, err := s.db.Query(queries, tx)
	return r, err
}

// Join joins a node, located at addr, to this store. The node must be ready to
// respond to Raft communications at that address.
func (s *Store) Join(addr string) error {
	s.logger.Printf("received join request for remote node as %s", addr)

	f := s.raft.AddPeer(addr)
	if f.Error() != nil {
		return f.Error()
	}
	s.logger.Printf("node at %s joined successfully", addr)
	return nil
}

type fsm Store

type fsmResponse struct {
	results []*sql.Result
	error   error
}

// Apply applies a Raft log entry to the database.
func (f *fsm) Apply(l *raft.Log) interface{} {
	var c command
	if err := json.Unmarshal(l.Data, &c); err != nil {
		panic(fmt.Sprintf("failed to unmarshal command: %s", err.Error()))
	}

	r, err := f.db.Execute(c.Queries, c.Tx)

	return &fsmResponse{results: r, error: err}
}

// Snapshot returns a snapshot of the database. The caller must ensure that
// no transaction is taking place during this call.
//
// http://sqlite.org/howtocorrupt.html states it is safe to do this
// as long as no transaction is in progress.
func (f *fsm) Snapshot() (raft.FSMSnapshot, error) {
    b, err := ioutil.ReadFile(f.db.Path()) /// XXX Could Snapshot be used? File copy might be faster.
    if err != nil {
        log.Printf("Failed to generate snapshot: %s", err.Error())
        return nil, err
    }
    return &fsmSnapshot{data: b}, nil
}

// Restore restores the database to a previous state.
func (f *fsm) Restore(rc io.ReadCloser) error {
	// Need to write bytes to SQLite file.
	// Open it with desired DSN params.
	// Implies the Open call above, should always blow away existing database, since
	// it will be restored completely from log, or from log and remaining log entries.
	return nil
}

type fsmSnapshot struct {
	data []byte
}

func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
	err := func() error {
		// Write data to sink.
		if _, err := sink.Write(f.data); err != nil {
			return err
		}

		// Close the sink.
		if err := sink.Close(); err != nil {
			return err
		}

		return nil
	}()

	if err != nil {
		sink.Cancel()
		return err
	}

	return nil
}

func (f *fsmSnapshot) Release() {}

func readPeersJSON(path string) ([]string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	if len(b) == 0 {
		return nil, nil
	}

	var peers []string
	dec := json.NewDecoder(bytes.NewReader(b))
	if err := dec.Decode(&peers); err != nil {
		return nil, err
	}

	return peers, nil
}
<|endoftext|>


<commit_before>package strkey

import (
	"bytes"
	"encoding/base32"
	"encoding/binary"
	"fmt"
	"log"

	"github.com/stellar/go-stellar-base/crc16"
)

// VersionByte represents one of the possible prefix values for a StrKey base
// string--the string the when encoded using base32 yields a final StrKey.
type VersionByte byte

const (
	//VersionByteAccountID is the version byte used for encoded stellar addresses
	VersionByteAccountID VersionByte = 6 << 3 // Base32-encodes to 'G...'
	//VersionByteSeed is the version byte used for encoded stellar seed
	VersionByteSeed = 18 << 3 // Base32-encodes to 'S...'
)

// Decode decodes the provided StrKey into a raw value, checking the checksum
// and ensuring the expected VersionByte (the version parameter) is the value
// actually encoded into the provided src string.
func Decode(expected VersionByte, src string) ([]byte, error) {
	if err := checkValidVersionByte(expected); err != nil {
		return nil, err
	}

	raw, err := base32.StdEncoding.DecodeString(src)
	if err != nil {
		return nil, err
	}

	if len(raw) < 3 {
		return nil, fmt.Errorf("encoded value is %d bytes; minimum valid length is 3", len(raw))
	}

	// decode into components
	version := VersionByte(raw[0])
	vp := raw[0 : len(raw)-2]
	payload := raw[1 : len(raw)-2]
	checksum := raw[len(raw)-2:]

	log.Println(raw)
	log.Println(payload)
	log.Println(version)
	log.Println(checksum)

	// ensure version byte is expected
	if version != expected {
		return nil, fmt.Errorf(
			"invalid strkey, expected %d for version byte, got %d",
			expected,
			version,
		)
	}

	// ensure checksum is valid
	if err := crc16.Validate(vp, checksum); err != nil {
		return nil, err
	}

	// if we made it through the gaunlet, return the decoded value
	return payload, nil
}

// Encode encodes the provided data to a StrKey, using the provided version
// byte.
func Encode(version VersionByte, src []byte) (string, error) {
	if err := checkValidVersionByte(version); err != nil {
		return "", err
	}

	var raw bytes.Buffer

	// write version byte
	if err := binary.Write(&raw, binary.LittleEndian, version); err != nil {
		return "", err
	}

	// write payload
	if _, err := raw.Write(src); err != nil {
		return "", err
	}

	// calculate and write checksum
	checksum := crc16.Checksum(raw.Bytes())
	if _, err := raw.Write(checksum); err != nil {
		return "", err
	}

	result := base32.StdEncoding.EncodeToString(raw.Bytes())
	return result, nil
}

// checkValidVersionByte returns an error if the provided value
// is not one of the defined valid version byte constants.
func checkValidVersionByte(version VersionByte) error {
	if version == VersionByteAccountID {
		return nil
	}
	if version == VersionByteSeed {
		return nil
	}

	return fmt.Errorf("invalid version byte: %d", version)
}
<commit_msg>Add MustDecode/MustEncode<commit_after>package strkey

import (
	"bytes"
	"encoding/base32"
	"encoding/binary"
	"fmt"
	"log"

	"github.com/stellar/go-stellar-base/crc16"
)

// VersionByte represents one of the possible prefix values for a StrKey base
// string--the string the when encoded using base32 yields a final StrKey.
type VersionByte byte

const (
	//VersionByteAccountID is the version byte used for encoded stellar addresses
	VersionByteAccountID VersionByte = 6 << 3 // Base32-encodes to 'G...'
	//VersionByteSeed is the version byte used for encoded stellar seed
	VersionByteSeed = 18 << 3 // Base32-encodes to 'S...'
)

// Decode decodes the provided StrKey into a raw value, checking the checksum
// and ensuring the expected VersionByte (the version parameter) is the value
// actually encoded into the provided src string.
func Decode(expected VersionByte, src string) ([]byte, error) {
	if err := checkValidVersionByte(expected); err != nil {
		return nil, err
	}

	raw, err := base32.StdEncoding.DecodeString(src)
	if err != nil {
		return nil, err
	}

	if len(raw) < 3 {
		return nil, fmt.Errorf("encoded value is %d bytes; minimum valid length is 3", len(raw))
	}

	// decode into components
	version := VersionByte(raw[0])
	vp := raw[0 : len(raw)-2]
	payload := raw[1 : len(raw)-2]
	checksum := raw[len(raw)-2:]

	log.Println(raw)
	log.Println(payload)
	log.Println(version)
	log.Println(checksum)

	// ensure version byte is expected
	if version != expected {
		return nil, fmt.Errorf(
			"invalid strkey, expected %d for version byte, got %d",
			expected,
			version,
		)
	}

	// ensure checksum is valid
	if err := crc16.Validate(vp, checksum); err != nil {
		return nil, err
	}

	// if we made it through the gaunlet, return the decoded value
	return payload, nil
}

// MustDecode is like Decode, but panics on error
func MustDecode(expected VersionByte, src string) []byte {
	d, err := Decode(expected, src)
	if err != nil {
		panic(err)
	}
	return d
}

// Encode encodes the provided data to a StrKey, using the provided version
// byte.
func Encode(version VersionByte, src []byte) (string, error) {
	if err := checkValidVersionByte(version); err != nil {
		return "", err
	}

	var raw bytes.Buffer

	// write version byte
	if err := binary.Write(&raw, binary.LittleEndian, version); err != nil {
		return "", err
	}

	// write payload
	if _, err := raw.Write(src); err != nil {
		return "", err
	}

	// calculate and write checksum
	checksum := crc16.Checksum(raw.Bytes())
	if _, err := raw.Write(checksum); err != nil {
		return "", err
	}

	result := base32.StdEncoding.EncodeToString(raw.Bytes())
	return result, nil
}

// MustEncode is like Encode, but panics on error
func MustEncode(version VersionByte, src []byte) string {
	e, err := Encode(version, src)
	if err != nil {
		panic(err)
	}
	return e
}

// checkValidVersionByte returns an error if the provided value
// is not one of the defined valid version byte constants.
func checkValidVersionByte(version VersionByte) error {
	if version == VersionByteAccountID {
		return nil
	}
	if version == VersionByteSeed {
		return nil
	}

	return fmt.Errorf("invalid version byte: %d", version)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

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

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

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

package main

import (
	"flag"
	"log"

	"go.uber.org/zap"
	kubeinformers "k8s.io/client-go/informers"
	"k8s.io/client-go/tools/clientcmd"

	sharedinformers "github.com/knative/pkg/client/informers/externalversions"
	"github.com/knative/pkg/configmap"
	"github.com/knative/pkg/controller"
	"github.com/knative/pkg/signals"
	informers "github.com/knative/serving/pkg/client/informers/externalversions"
	"github.com/knative/serving/pkg/logging"
	"github.com/knative/serving/pkg/reconciler"
	"github.com/knative/serving/pkg/reconciler/clusteringress"
)

const (
	threadsPerController = 2
	component            = "controller-clusteringress-istio"
)

var (
	masterURL  = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
	kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
)

func main() {
	flag.Parse()

	// Set up our logger.
	loggingConfigMap, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatal("Error loading logging configuration:", err)
	}
	loggingConfig, err := logging.NewConfigFromMap(loggingConfigMap)
	if err != nil {
		log.Fatal("Error parsing logging configuration:", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(loggingConfig, component)
	defer logger.Sync()

	// Set up signals so we handle the first shutdown signal gracefully.
	stopCh := signals.SetupSignalHandler()

	cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeconfig)
	if err != nil {
		logger.Fatalw("Error building kubeconfig", zap.Error(err))
	}

	opt := reconciler.NewOptionsOrDie(cfg, logger, stopCh)

	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(opt.KubeClientSet, opt.ResyncPeriod)
	servingInformerFactory := informers.NewSharedInformerFactory(opt.ServingClientSet, opt.ResyncPeriod)
	sharedInformerFactory := sharedinformers.NewSharedInformerFactory(opt.SharedClientSet, opt.ResyncPeriod)

	clusterIngressInformer := servingInformerFactory.Networking().V1alpha1().ClusterIngresses()
	virtualServiceInformer := sharedInformerFactory.Networking().V1alpha3().VirtualServices()
	gatewayInformer := sharedInformerFactory.Networking().V1alpha3().Gateways()
	secretInformer := kubeInformerFactory.Core().V1().Secrets()
	configMapInformer := kubeInformerFactory.Core().V1().ConfigMaps()

	// Build our controller
	ciController := clusteringress.NewController(
		opt,
		clusterIngressInformer,
		virtualServiceInformer,
		gatewayInformer,
		secretInformer,
	)

	// Watch the logging config map and dynamically update logging levels.
	opt.ConfigMapWatcher.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))
	if err := opt.ConfigMapWatcher.Start(stopCh); err != nil {
		logger.Fatalw("failed to start configuration manager", zap.Error(err))
	}

	// Wait for the caches to be synced before starting controllers.
	logger.Info("Waiting for informer caches to sync")
	if err := controller.StartInformers(
		stopCh,
		clusterIngressInformer.Informer(),
		virtualServiceInformer.Informer(),
		secretInformer.Informer(),
		gatewayInformer.Informer(),
		configMapInformer.Informer(),
	); err != nil {
		logger.Fatalw("Failed to start informers", zap.Error(err))
	}

	// Start all of the controllers.
	logger.Info("Starting controllers.")
	controller.StartAll(stopCh, ciController)
}
<commit_msg>This pulls in the changes from knative/pkg#425 (#4163)<commit_after>/*
Copyright 2019 The Knative Authors

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

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

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

package main

import (
	"flag"
	"log"

	"go.uber.org/zap"
	kubeinformers "k8s.io/client-go/informers"
	"k8s.io/client-go/tools/clientcmd"

	sharedinformers "github.com/knative/pkg/client/informers/externalversions"
	"github.com/knative/pkg/configmap"
	"github.com/knative/pkg/controller"
	"github.com/knative/pkg/logging"
	"github.com/knative/pkg/signals"
	informers "github.com/knative/serving/pkg/client/informers/externalversions"
	"github.com/knative/serving/pkg/reconciler"
	"github.com/knative/serving/pkg/reconciler/clusteringress"
)

const (
	threadsPerController = 2
	component            = "controller-clusteringress-istio"
)

var (
	masterURL  = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
	kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
)

func main() {
	flag.Parse()

	// Set up our logger.
	loggingConfigMap, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatal("Error loading logging configuration:", err)
	}
	loggingConfig, err := logging.NewConfigFromMap(loggingConfigMap)
	if err != nil {
		log.Fatal("Error parsing logging configuration:", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(loggingConfig, component)
	defer logger.Sync()

	// Set up signals so we handle the first shutdown signal gracefully.
	stopCh := signals.SetupSignalHandler()

	cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeconfig)
	if err != nil {
		logger.Fatalw("Error building kubeconfig", zap.Error(err))
	}

	opt := reconciler.NewOptionsOrDie(cfg, logger, stopCh)

	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(opt.KubeClientSet, opt.ResyncPeriod)
	servingInformerFactory := informers.NewSharedInformerFactory(opt.ServingClientSet, opt.ResyncPeriod)
	sharedInformerFactory := sharedinformers.NewSharedInformerFactory(opt.SharedClientSet, opt.ResyncPeriod)

	clusterIngressInformer := servingInformerFactory.Networking().V1alpha1().ClusterIngresses()
	virtualServiceInformer := sharedInformerFactory.Networking().V1alpha3().VirtualServices()
	gatewayInformer := sharedInformerFactory.Networking().V1alpha3().Gateways()
	secretInformer := kubeInformerFactory.Core().V1().Secrets()
	configMapInformer := kubeInformerFactory.Core().V1().ConfigMaps()

	// Build our controller
	ciController := clusteringress.NewController(
		opt,
		clusterIngressInformer,
		virtualServiceInformer,
		gatewayInformer,
		secretInformer,
	)

	// Watch the logging config map and dynamically update logging levels.
	opt.ConfigMapWatcher.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))
	if err := opt.ConfigMapWatcher.Start(stopCh); err != nil {
		logger.Fatalw("failed to start configuration manager", zap.Error(err))
	}

	// Wait for the caches to be synced before starting controllers.
	logger.Info("Waiting for informer caches to sync")
	if err := controller.StartInformers(
		stopCh,
		clusterIngressInformer.Informer(),
		virtualServiceInformer.Informer(),
		secretInformer.Informer(),
		gatewayInformer.Informer(),
		configMapInformer.Informer(),
	); err != nil {
		logger.Fatalw("Failed to start informers", zap.Error(err))
	}

	// Start all of the controllers.
	logger.Info("Starting controllers.")
	controller.StartAll(stopCh, ciController)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rsa"
	"crypto/sha1"
	"encoding/gob"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/golang/glog"
	"github.com/husobee/peerstore/crypto"
	"github.com/husobee/peerstore/models"
	"github.com/husobee/peerstore/protocol"
	"github.com/pkg/errors"
)

var (
	peerAddr string
	// peerKeyFile - the key file location for a known peer on the network
	peerKeyFile string
	selfKeyFile string
	localPath   string
	operation   string
	filename    string
	filedest    string
)

func init() {
	flag.StringVar(
		&peerAddr, "peerAddr", "",
		"the address of a peer")
	flag.StringVar(
		&operation, "operation", "",
		"choice of operation, backup or getfile.  backup will put localPath in peerstore, getfile will download the file and put it in filedest. specify the file to download by name with -filename flag")
	flag.StringVar(
		&localPath, "localPath", "",
		"the location of the dir you wish to sync")
	flag.StringVar(
		&filename, "filename", "",
		"the name of the file you want to get from peerstore")
	flag.StringVar(
		&filedest, "filedest", "",
		"destination of the file with doing getfile operation")
	flag.StringVar(
		&peerKeyFile, "peerKeyFile", "",
		"the key file location of a known peer on the network")
	flag.StringVar(
		&selfKeyFile, "selfKeyFile", "",
		"the key file location of your private/public key pem file")
	flag.Parse()
}

func validateParams() error {
	if peerAddr == "" {
		return errors.New("peerAddr must be set")
	}
	if operation == "backup" {
		if localPath == "" {
			return errors.New("localPath must be set")
		}
		info, err := os.Stat(localPath)
		if err != nil {
			return errors.Wrap(err, "error attempting to validate localPath: ")
		}
		if !info.IsDir() {
			return errors.New("localPath must be a valid directory")
		}
	} else if operation == "getfile" {
		if filedest == "" {
			return errors.New("filedest must be set")
		}
		if filename == "" {
			return errors.New("filename must be set")
		}

	} else {
		return errors.New("must specify operation flag, either backup or getfile")
	}
	return nil
}

func main() {

	log.Println("starting client")

	if err := validateParams(); err != nil {
		log.Fatalf("could not validate params: %v\n", err)
	}

	var (
		privateKey *rsa.PrivateKey
		err        error
	)

	if _, err := os.Stat(selfKeyFile); err != nil {
		// generate our public key
		privateKey, err = crypto.GenerateKeyPair()
		if err != nil {
			log.Printf("failed to generate keypair: %s", err)
			return
		}
		// create our keypair file:
		keyFile, err := os.Create(fmt.Sprintf("%s", selfKeyFile))
		if err != nil {
			glog.Infof("failed to create keypair file: %s", err)
			return
		}
		crypto.WritePrivateKeyAsPem(keyFile, privateKey)
		crypto.WritePublicKeyAsPem(keyFile, privateKey.Public().(*rsa.PublicKey))
		keyFile.Close()
	} else {
		keyFile, err := os.Open(fmt.Sprintf("%s", selfKeyFile))
		privateKey, err = crypto.ReadKeypairAsPem(keyFile)
		if err != nil {
			log.Printf("failed to read keypair: %s", err)
			return
		}
	}

	kb, _ := crypto.GobEncodePublicKey(privateKey.Public().(*rsa.PublicKey))
	id := models.Identifier(sha1.Sum(kb))

	// read in our peer's public key
	keyFile, err := os.Open(peerKeyFile) // For read access.
	if err != nil {
		glog.Infof("failed to read initial peer key file: %s", err)
		return
	}

	peerKey, err := crypto.ReadPublicKeyAsPem(keyFile)
	if err != nil {
		glog.Infof("failed to read keypair file: %s", err)
		return
	}

	// register the user with the network
	log.Printf("usertype should be : %d", protocol.UserType)
	rt, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
	if err != nil {
		log.Printf("ERR: %v", err)
	}

	resp, err := rt.RoundTrip(&protocol.Request{
		Header: protocol.Header{
			From:   id,
			Type:   protocol.UserType,
			PubKey: privateKey.Public().(*rsa.PublicKey),
		},
		Method: protocol.UserRegistrationMethod,
	})
	if err != nil {
		log.Printf("Failed to round trip the successor request: %v", err)
		return
	}
	rt.Close()
	log.Printf("response: %+v", resp)

	switch operation {
	case "backup":
		var walkFn = func(path string, fi os.FileInfo, err error) error {
			if !fi.IsDir() {
				log.Printf("file is: %s\n", path)

				// the key for the distributed lookup
				key := sha1.Sum([]byte(path))
				data, err := ioutil.ReadFile(path) // path is the path to the file.

				// figure out where to connect to
				st, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
				if err != nil {
					log.Printf("ERR: %v", err)
				}

				// serialize our get successor request
				var idBuf = new(bytes.Buffer)
				enc := gob.NewEncoder(idBuf)
				enc.Encode(models.SuccessorRequest{
					models.Identifier(key),
				})
				resp, err := st.RoundTrip(&protocol.Request{
					Header: protocol.Header{
						From:   id,
						Type:   protocol.UserType,
						PubKey: privateKey.Public().(*rsa.PublicKey),
					},
					Method: protocol.GetSuccessorMethod,
					Data:   idBuf.Bytes(),
				})
				if err != nil {
					log.Printf("Failed to round trip the successor request: %v", err)
					return errors.Wrap(err, "failed round trip")
				}
				st.Close()

				// connect to that host for this file
				// pull node out of response, and connect to that host
				var node = models.Node{}
				dec := gob.NewDecoder(bytes.NewBuffer(resp.Data))
				err = dec.Decode(&node)
				if err != nil {
					log.Printf("Failed to deserialize the node data: %v", err)
					return errors.Wrap(err, "failed to deserialize")
				}

				// figure out where to connect to
				t, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, node.PublicKey, privateKey)
				if err != nil {
					log.Printf("ERR: %v", err)
				}

				// send the file over
				log.Println("starting request: ", protocol.PostFileMethod)
				response, err := t.RoundTrip(&protocol.Request{
					Header: protocol.Header{
						Key:        key,
						Type:       protocol.UserType,
						From:       id,
						DataLength: uint64(len(data)),
						PubKey:     privateKey.Public().(*rsa.PublicKey),
					},
					Method: protocol.PostFileMethod,
					Data:   data,
				})
				if err != nil {
					log.Printf("ERR: %v\n", err)
				}
				log.Printf("Response: %+v\n", response)

				if err != nil {
					fmt.Println("Fail")
				}
				t.Close()
			}
			return nil
		}

		// Open up directory
		// read each file, and send to peerAddr
		filepath.Walk(localPath, walkFn)

	case "getfile":
		log.Printf("getting file: %s, putting %s", filename, filedest)
		// the key for the distributed lookup
		key := sha1.Sum([]byte(filename))

		// figure out where to connect to
		st, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
		if err != nil {
			log.Printf("ERR: %v", err)
		}

		// serialize our get successor request
		var idBuf = new(bytes.Buffer)
		enc := gob.NewEncoder(idBuf)
		enc.Encode(models.SuccessorRequest{
			models.Identifier(key),
		})
		resp, err := st.RoundTrip(&protocol.Request{
			Header: protocol.Header{
				Type: protocol.UserType,
				From: id,
				Key:  key,
			},
			Method: protocol.GetSuccessorMethod,
			Data:   idBuf.Bytes(),
		})
		if err != nil {
			log.Printf("Failed to round trip the successor request: %v", err)
			return
		}

		log.Printf("found node")

		// connect to that host for this file
		// pull node out of response, and connect to that host
		var node = models.Node{}
		dec := gob.NewDecoder(bytes.NewBuffer(resp.Data))
		err = dec.Decode(&node)
		if err != nil {
			log.Printf("Failed to deserialize the node data: %v", err)
			return
		}

		// figure out where to connect to
		t, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, node.PublicKey, privateKey)
		if err != nil {
			log.Printf("ERR: %v", err)
		}

		log.Printf("performing get")
		resp, err = t.RoundTrip(&protocol.Request{
			Header: protocol.Header{
				Type: protocol.UserType,
				From: id,
				Key:  key,
			},
			Method: protocol.GetFileMethod,
		})
		if err != nil {
			log.Printf("Failed to round trip the successor request: %v", err)
			return
		}
		log.Printf("done get")

		err = ioutil.WriteFile(filedest, resp.Data, 0644)
		if err != nil {
			log.Println(err)
			return
		}
		log.Println("done")
	}
}
<commit_msg>show that there was an error on getfile operation if there was an error<commit_after>package main

import (
	"bytes"
	"crypto/rsa"
	"crypto/sha1"
	"encoding/gob"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/golang/glog"
	"github.com/husobee/peerstore/crypto"
	"github.com/husobee/peerstore/models"
	"github.com/husobee/peerstore/protocol"
	"github.com/pkg/errors"
)

var (
	peerAddr string
	// peerKeyFile - the key file location for a known peer on the network
	peerKeyFile string
	selfKeyFile string
	localPath   string
	operation   string
	filename    string
	filedest    string
)

func init() {
	flag.StringVar(
		&peerAddr, "peerAddr", "",
		"the address of a peer")
	flag.StringVar(
		&operation, "operation", "",
		"choice of operation, backup or getfile.  backup will put localPath in peerstore, getfile will download the file and put it in filedest. specify the file to download by name with -filename flag")
	flag.StringVar(
		&localPath, "localPath", "",
		"the location of the dir you wish to sync")
	flag.StringVar(
		&filename, "filename", "",
		"the name of the file you want to get from peerstore")
	flag.StringVar(
		&filedest, "filedest", "",
		"destination of the file with doing getfile operation")
	flag.StringVar(
		&peerKeyFile, "peerKeyFile", "",
		"the key file location of a known peer on the network")
	flag.StringVar(
		&selfKeyFile, "selfKeyFile", "",
		"the key file location of your private/public key pem file")
	flag.Parse()
}

func validateParams() error {
	if peerAddr == "" {
		return errors.New("peerAddr must be set")
	}
	if operation == "backup" {
		if localPath == "" {
			return errors.New("localPath must be set")
		}
		info, err := os.Stat(localPath)
		if err != nil {
			return errors.Wrap(err, "error attempting to validate localPath: ")
		}
		if !info.IsDir() {
			return errors.New("localPath must be a valid directory")
		}
	} else if operation == "getfile" {
		if filedest == "" {
			return errors.New("filedest must be set")
		}
		if filename == "" {
			return errors.New("filename must be set")
		}

	} else {
		return errors.New("must specify operation flag, either backup or getfile")
	}
	return nil
}

func main() {

	log.Println("starting client")

	if err := validateParams(); err != nil {
		log.Fatalf("could not validate params: %v\n", err)
	}

	var (
		privateKey *rsa.PrivateKey
		err        error
	)

	if _, err := os.Stat(selfKeyFile); err != nil {
		// generate our public key
		privateKey, err = crypto.GenerateKeyPair()
		if err != nil {
			log.Printf("failed to generate keypair: %s", err)
			return
		}
		// create our keypair file:
		keyFile, err := os.Create(fmt.Sprintf("%s", selfKeyFile))
		if err != nil {
			glog.Infof("failed to create keypair file: %s", err)
			return
		}
		crypto.WritePrivateKeyAsPem(keyFile, privateKey)
		crypto.WritePublicKeyAsPem(keyFile, privateKey.Public().(*rsa.PublicKey))
		keyFile.Close()
	} else {
		keyFile, err := os.Open(fmt.Sprintf("%s", selfKeyFile))
		privateKey, err = crypto.ReadKeypairAsPem(keyFile)
		if err != nil {
			log.Printf("failed to read keypair: %s", err)
			return
		}
	}

	kb, _ := crypto.GobEncodePublicKey(privateKey.Public().(*rsa.PublicKey))
	id := models.Identifier(sha1.Sum(kb))

	// read in our peer's public key
	keyFile, err := os.Open(peerKeyFile) // For read access.
	if err != nil {
		glog.Infof("failed to read initial peer key file: %s", err)
		return
	}

	peerKey, err := crypto.ReadPublicKeyAsPem(keyFile)
	if err != nil {
		glog.Infof("failed to read keypair file: %s", err)
		return
	}

	// register the user with the network
	log.Printf("usertype should be : %d", protocol.UserType)
	rt, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
	if err != nil {
		log.Printf("ERR: %v", err)
	}

	resp, err := rt.RoundTrip(&protocol.Request{
		Header: protocol.Header{
			From:   id,
			Type:   protocol.UserType,
			PubKey: privateKey.Public().(*rsa.PublicKey),
		},
		Method: protocol.UserRegistrationMethod,
	})
	if err != nil {
		log.Printf("Failed to round trip the successor request: %v", err)
		return
	}
	rt.Close()
	log.Printf("response: %+v", resp)

	switch operation {
	case "backup":
		var walkFn = func(path string, fi os.FileInfo, err error) error {
			if !fi.IsDir() {
				log.Printf("file is: %s\n", path)

				// the key for the distributed lookup
				key := sha1.Sum([]byte(path))
				data, err := ioutil.ReadFile(path) // path is the path to the file.

				// figure out where to connect to
				st, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
				if err != nil {
					log.Printf("ERR: %v", err)
				}

				// serialize our get successor request
				var idBuf = new(bytes.Buffer)
				enc := gob.NewEncoder(idBuf)
				enc.Encode(models.SuccessorRequest{
					models.Identifier(key),
				})
				resp, err := st.RoundTrip(&protocol.Request{
					Header: protocol.Header{
						From:   id,
						Type:   protocol.UserType,
						PubKey: privateKey.Public().(*rsa.PublicKey),
					},
					Method: protocol.GetSuccessorMethod,
					Data:   idBuf.Bytes(),
				})
				if err != nil {
					log.Printf("Failed to round trip the successor request: %v", err)
					return errors.Wrap(err, "failed round trip")
				}
				st.Close()

				// connect to that host for this file
				// pull node out of response, and connect to that host
				var node = models.Node{}
				dec := gob.NewDecoder(bytes.NewBuffer(resp.Data))
				err = dec.Decode(&node)
				if err != nil {
					log.Printf("Failed to deserialize the node data: %v", err)
					return errors.Wrap(err, "failed to deserialize")
				}

				// figure out where to connect to
				t, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, node.PublicKey, privateKey)
				if err != nil {
					log.Printf("ERR: %v", err)
				}

				// send the file over
				log.Println("starting request: ", protocol.PostFileMethod)
				response, err := t.RoundTrip(&protocol.Request{
					Header: protocol.Header{
						Key:        key,
						Type:       protocol.UserType,
						From:       id,
						DataLength: uint64(len(data)),
						PubKey:     privateKey.Public().(*rsa.PublicKey),
					},
					Method: protocol.PostFileMethod,
					Data:   data,
				})
				if err != nil {
					log.Printf("ERR: %v\n", err)
				}
				log.Printf("Response: %+v\n", response)

				if err != nil {
					fmt.Println("Fail")
				}
				t.Close()
			}
			return nil
		}

		// Open up directory
		// read each file, and send to peerAddr
		filepath.Walk(localPath, walkFn)

	case "getfile":
		log.Printf("getting file: %s, putting %s", filename, filedest)
		// the key for the distributed lookup
		key := sha1.Sum([]byte(filename))

		// figure out where to connect to
		st, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, &peerKey, privateKey)
		if err != nil {
			log.Printf("ERR: %v", err)
		}

		// serialize our get successor request
		var idBuf = new(bytes.Buffer)
		enc := gob.NewEncoder(idBuf)
		enc.Encode(models.SuccessorRequest{
			models.Identifier(key),
		})
		resp, err := st.RoundTrip(&protocol.Request{
			Header: protocol.Header{
				Type: protocol.UserType,
				From: id,
				Key:  key,
			},
			Method: protocol.GetSuccessorMethod,
			Data:   idBuf.Bytes(),
		})
		if err != nil {
			log.Printf("Failed to round trip the successor request: %v", err)
			return
		}

		log.Printf("found node")

		// connect to that host for this file
		// pull node out of response, and connect to that host
		var node = models.Node{}
		dec := gob.NewDecoder(bytes.NewBuffer(resp.Data))
		err = dec.Decode(&node)
		if err != nil {
			log.Printf("Failed to deserialize the node data: %v", err)
			return
		}

		// figure out where to connect to
		t, err := protocol.NewTransport("tcp", peerAddr, protocol.UserType, id, node.PublicKey, privateKey)
		if err != nil {
			log.Printf("ERR: %v", err)
		}

		resp, err = t.RoundTrip(&protocol.Request{
			Header: protocol.Header{
				Type: protocol.UserType,
				From: id,
				Key:  key,
			},
			Method: protocol.GetFileMethod,
		})
		if err != nil {
			log.Printf("Failed to round trip the successor request: %v", err)
			return
		}
		if resp.Status == protocol.Error {
			log.Printf("failed to get resource requested.")
			return
		}

		err = ioutil.WriteFile(filedest, resp.Data, 0644)
		if err != nil {
			log.Println(err)
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

/*
Package wares_test contains tests and examples for package wares. The goal is
100% code coverage.
*/
package wares_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/ursiform/forest"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

const (
	sessionID     = "SOME-SESSION-ID"
	sessionUserID = "SOME-USER-ID"
)

type requested struct {
	body   []byte
	method string
	path   string
}

type wanted struct {
	code    int
	success bool
	data    interface{}
}

func makeRequest(t *testing.T, app *forest.App, params *requested, want *wanted) *http.Response {
	var request *http.Request
	method := params.method
	path := params.path
	body := params.body
	if body != nil {
		request, _ = http.NewRequest(method, path, bytes.NewBuffer(body))
	} else {
		request, _ = http.NewRequest(method, path, nil)
	}
	response := httptest.NewRecorder()
	app.Router.ServeHTTP(response, request)
	responseData := new(forest.Response)
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		t.Error(err)
		return nil
	}
	if err := json.Unmarshal(responseBody, responseData); err != nil {
		t.Errorf("unmarshal error: %v when attempting to read: %s", err, string(responseBody))
		return nil
	}
	if response.Code != want.code {
		t.Errorf("%s %s want: %d (%s) got: %d %s, body: %s", method, path,
			want.code, http.StatusText(want.code), response.Code, http.StatusText(response.Code), string(responseBody))
		return nil
	}
	if responseData.Success != want.success {
		t.Errorf("%s %s should return success: %t", method, path, want.success)
		return nil
	}
	return &http.Response{Header: response.Header()}
}

func TestAuthenticateFailure(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/authenticate/failure"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusUnauthorized, success: false}
	makeRequest(t, app, params, want)
}

func TestAuthenticateSuccess(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/authenticate/success"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestBadRequest(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/bad-request"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserFailureBodyNil(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/success"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserFailureNoInit(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/failure/no-init"
	body := []byte("{\"foo\": \"bar\"}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusInternalServerError, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserSuccess(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/success"
	body := []byte("{\"foo\": \"bar\"}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestConflict(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/conflict"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusConflict, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyNil(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyParse(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte("{BAD JSON}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyTooShort(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte("{")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureWrongSessionID(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte(fmt.Sprintf("{\"sessionid\": \"WRONG-SESSION-ID\"}"))
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFSuccess(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte(fmt.Sprintf("{\"sessionid\": \"%s\"}", sessionID))
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestMethodNotAllowed(t *testing.T) {
	debug := false
	method := "OPTIONS"
	root := "/foo"
	path := "/foo"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusMethodNotAllowed, success: false}
	makeRequest(t, app, params, want)
}

func TestNotFound(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/not-found"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusNotFound, success: false}
	makeRequest(t, app, params, want)
}

func TestServerError(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/server-error"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusInternalServerError, success: false}
	makeRequest(t, app, params, want)
}

func TestUnauthorized(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/unauthorized"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusUnauthorized, success: false}
	makeRequest(t, app, params, want)
}
<commit_msg>moving Populater to forest-wares, uninitialized BodyParser should be 500 error, adding some BodyParser tests<commit_after>// Copyright 2015 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

/*
Package wares_test contains tests and examples for package wares. The goal is
100% code coverage.
*/
package wares_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/ursiform/forest"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

const (
	sessionID     = "SOME-SESSION-ID"
	sessionUserID = "SOME-USER-ID"
)

type requested struct {
	body   []byte
	method string
	path   string
}

type wanted struct {
	code    int
	success bool
	data    interface{}
}

func makeRequest(t *testing.T, app *forest.App, params *requested, want *wanted) *http.Response {
	var request *http.Request
	method := params.method
	path := params.path
	body := params.body
	if body != nil {
		request, _ = http.NewRequest(method, path, bytes.NewBuffer(body))
	} else {
		request, _ = http.NewRequest(method, path, nil)
	}
	response := httptest.NewRecorder()
	app.Router.ServeHTTP(response, request)
	responseData := new(forest.Response)
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		t.Error(err)
		return nil
	}
	if err := json.Unmarshal(responseBody, responseData); err != nil {
		t.Errorf("unmarshal error: %v when attempting to read: %s", err, string(responseBody))
		return nil
	}
	if response.Code != want.code {
		t.Errorf("%s %s want: %d (%s) got: %d %s, body: %s", method, path,
			want.code, http.StatusText(want.code), response.Code, http.StatusText(response.Code), string(responseBody))
		return nil
	}
	if responseData.Success != want.success {
		t.Errorf("%s %s should return success: %t", method, path, want.success)
		return nil
	}
	return &http.Response{Header: response.Header()}
}

func TestAuthenticateFailure(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/authenticate/failure"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusUnauthorized, success: false}
	makeRequest(t, app, params, want)
}

func TestAuthenticateSuccess(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/authenticate/success"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestBadRequest(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/bad-request"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserFailureBadInput(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/success"
	body := []byte("{BAD JSON}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserFailureBodyNil(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/success"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserFailureNoInit(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/failure/no-init"
	body := []byte("{\"foo\": \"bar\"}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusInternalServerError, success: false}
	makeRequest(t, app, params, want)
}

func TestBodyParserSuccess(t *testing.T) {
	debug := false
	method := "POST"
	root := "/foo"
	path := "/foo/body-parser/success"
	body := []byte("{\"foo\": \"bar\"}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestConflict(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/conflict"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusConflict, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyNil(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyParse(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte("{BAD JSON}")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureBodyTooShort(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte("{")
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFFailureWrongSessionID(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte(fmt.Sprintf("{\"sessionid\": \"WRONG-SESSION-ID\"}"))
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusBadRequest, success: false}
	makeRequest(t, app, params, want)
}

func TestCSRFSuccess(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/csrf"
	body := []byte(fmt.Sprintf("{\"sessionid\": \"%s\"}", sessionID))
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{body: body, method: method, path: path}
	want := &wanted{code: http.StatusOK, success: true}
	makeRequest(t, app, params, want)
}

func TestMethodNotAllowed(t *testing.T) {
	debug := false
	method := "OPTIONS"
	root := "/foo"
	path := "/foo"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusMethodNotAllowed, success: false}
	makeRequest(t, app, params, want)
}

func TestNotFound(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/not-found"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusNotFound, success: false}
	makeRequest(t, app, params, want)
}

func TestServerError(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/server-error"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusInternalServerError, success: false}
	makeRequest(t, app, params, want)
}

func TestUnauthorized(t *testing.T) {
	debug := false
	method := "GET"
	root := "/foo"
	path := "/foo/unauthorized"
	app := forest.New(debug)
	app.RegisterRoute(root, newRouter(app))
	params := &requested{method: method, path: path}
	want := &wanted{code: http.StatusUnauthorized, success: false}
	makeRequest(t, app, params, want)
}
<|endoftext|>


<commit_before>package httplog

import "net/http"

// The ResponseRecorder interface is implemented by ResponseWriters that
// can record the response status code and bytes written to the client.
type ResponseRecorder interface {
	Code() int  // Response status code
	Bytes() int // Bytes written to the client
}

// ResponseWriter is an http.ResponseWriter + ResponseRecorder.
type ResponseWriter struct {
	http.ResponseWriter
	http.Hijacker
	http.Flusher
	http.CloseNotifier
	ResponseRecorder
	bytes, code int
}

// NewResponseWriter creates and initializes a new ResponseWriter.
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
	return &ResponseWriter{
		ResponseWriter: w,
		code:           http.StatusOK,
	}
}

// Header implements the http.ResponseWriter interface.
func (w *ResponseWriter) Header() http.Header {
	return w.ResponseWriter.Header()
}

// Write implements the http.ResponseWriter interface.
func (w *ResponseWriter) Write(b []byte) (int, error) {
	n, err := w.ResponseWriter.Write(b)
	w.bytes += n
	return n, err
}

// WriteHeader implements the http.ResponseWriter interface.
func (w *ResponseWriter) WriteHeader(code int) {
	w.code = code
	w.ResponseWriter.WriteHeader(code)
}

// Code implements the ResponseRecorder interface.
func (w *ResponseWriter) Code() int { return w.code }

// Bytes implements the ResponseRecorder interface.
func (w *ResponseWriter) Bytes() int { return w.bytes }
<commit_msg>Implement auxiliary http interfaces<commit_after>package httplog

import (
	"bufio"
	"errors"
	"net"
	"net/http"
)

// The ResponseRecorder interface is implemented by ResponseWriters that
// can record the response status code and bytes written to the client.
type ResponseRecorder interface {
	Code() int  // Response status code
	Bytes() int // Bytes written to the client
}

// ResponseWriter is an http.ResponseWriter + ResponseRecorder.
type ResponseWriter struct {
	http.ResponseWriter
	http.Hijacker
	http.Flusher
	http.CloseNotifier
	ResponseRecorder
	bytes, code int
}

// NewResponseWriter creates and initializes a new ResponseWriter.
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
	return &ResponseWriter{
		ResponseWriter: w,
		code:           http.StatusOK,
	}
}

// Header implements the http.ResponseWriter interface.
func (w *ResponseWriter) Header() http.Header {
	return w.ResponseWriter.Header()
}

// Write implements the http.ResponseWriter interface.
func (w *ResponseWriter) Write(b []byte) (int, error) {
	n, err := w.ResponseWriter.Write(b)
	w.bytes += n
	return n, err
}

// WriteHeader implements the http.ResponseWriter interface.
func (w *ResponseWriter) WriteHeader(code int) {
	w.code = code
	w.ResponseWriter.WriteHeader(code)
}

// Code implements the ResponseRecorder interface.
func (w *ResponseWriter) Code() int { return w.code }

// Bytes implements the ResponseRecorder interface.
func (w *ResponseWriter) Bytes() int { return w.bytes }

// Hijack implements the http.Hijacker interface.
func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	f, ok := w.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, errors.New("hijacker not supported")
	}
	return f.Hijack()
}

// Flush implements the http.Flusher interface.
func (w *ResponseWriter) Flush() {
	f, ok := w.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

// CloseNotify implements the http.CloseNotififer interface.
func (w *ResponseWriter) CloseNotify() <-chan bool {
	f, ok := w.ResponseWriter.(http.CloseNotifier)
	if ok {
		return f.CloseNotify()
	}
	return make(chan bool) // ugh.
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"
	"sort"
	"sync"

	"github.com/litl/galaxy/log"
	"github.com/litl/galaxy/shuttle/client"
)

var (
	ErrNoService        = fmt.Errorf("service does not exist")
	ErrNoBackend        = fmt.Errorf("backend does not exist")
	ErrDuplicateService = fmt.Errorf("service already exists")
	ErrDuplicateBackend = fmt.Errorf("backend already exists")
)

type VirtualHost struct {
	sync.Mutex
	Name string
	// All services registered under this vhost name.
	services []*Service
	// The last one we returned so we can RoundRobin them.
	last int
}

func (v *VirtualHost) Len() int {
	v.Lock()
	defer v.Unlock()
	return len(v.services)
}

// Insert a service
// do nothing if the service already is registered
func (v *VirtualHost) Add(svc *Service) {
	v.Lock()
	defer v.Unlock()
	for _, s := range v.services {
		if s.Name == svc.Name {
			log.Debugf("Service %s already registered in VirtualHost %s", svc.Name, v.Name)
			return
		}
	}

	// TODO: is this the best place to log these?
	svcCfg := svc.Config()
	for _, backend := range svcCfg.Backends {
		log.Printf("Adding HTTP endpoint http://%s to %s", backend.Addr, v.Name)
	}
	v.services = append(v.services, svc)
}

func (v *VirtualHost) Remove(svc *Service) {
	v.Lock()
	defer v.Unlock()

	found := -1
	for i, s := range v.services {
		if s.Name == svc.Name {
			found = i
			break
		}
	}

	if found < 0 {
		log.Debugf("Service %s not found under VirtualHost %s", svc.Name, v.Name)
		return
	}

	// safe way to get the backends info for logging
	svcCfg := svc.Config()

	// Now removing this Service
	for _, backend := range svcCfg.Backends {
		log.Printf("Removing HTTP endpoint http://%s from %s", backend.Addr, v.Name)
	}

	v.services = append(v.services[:found], v.services[found+1:]...)
}

// Return a *Service for this VirtualHost
func (v *VirtualHost) Service() *Service {
	v.Lock()
	defer v.Unlock()

	if len(v.services) == 0 {
		log.Warnf("No Services registered for VirtualHost %s", v.Name)
		return nil
	}

	// start cycling through the services in case one has no backends available
	for i := 1; i <= len(v.services); i++ {
		idx := (v.last + i) % len(v.services)
		if v.services[idx].Available() > 0 {
			v.last = idx
			return v.services[idx]
		}
	}
	log.Warnf("No Available backends for VirtualHost %s", v.Name)
	return nil
}

//TODO: notify or prevent vhost name conflicts between services.
// ServiceRegistry is a global container for all configured services.
type ServiceRegistry struct {
	sync.Mutex
	svcs map[string]*Service
	// Multiple services may respond from a single vhost
	vhosts map[string]*VirtualHost
}

// Return a service by name.
func (s *ServiceRegistry) GetService(name string) *Service {
	s.Lock()
	defer s.Unlock()
	return s.svcs[name]
}

// Return a service that handles a particular vhost by name.
func (s *ServiceRegistry) GetVHostService(name string) *Service {
	s.Lock()
	defer s.Unlock()

	if vhost := s.vhosts[name]; vhost != nil {
		return vhost.Service()
	}
	return nil
}

func (s *ServiceRegistry) VHostsLen() int {
	s.Lock()
	defer s.Unlock()
	return len(s.vhosts)
}

// Add a new service to the Registry.
// Do not replace an existing service.
func (s *ServiceRegistry) AddService(cfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	log.Debug("Adding service:", cfg.Name)
	if _, ok := s.svcs[cfg.Name]; ok {
		log.Debug("Service already exists:", cfg.Name)
		return ErrDuplicateService
	}

	service := NewService(cfg)
	s.svcs[service.Name] = service

	for _, name := range cfg.VirtualHosts {
		vhost := s.vhosts[name]
		if vhost == nil {
			vhost = &VirtualHost{Name: name}
			s.vhosts[name] = vhost
		}
		vhost.Add(service)
	}

	return service.start()
}

// Replace the service's configuration, or update its list of backends.
// Replacing a configuration will shutdown the existing service, and start a
// new one, which will cause the listening socket to be temporarily
// unavailable.
func (s *ServiceRegistry) UpdateService(newCfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	log.Debug("Updating Service:", newCfg.Name)
	service, ok := s.svcs[newCfg.Name]
	if !ok {
		log.Debug("Service not found:", newCfg.Name)
		return ErrNoService
	}

	currentCfg := service.Config()

	// Lots of looping here (including fetching the Config, but the cardinality
	// of Backends shouldn't be very large, and the default RoundRobin balancing
	// is much simpler with a slice.

	// we're going to update just the backends for this config
	// get a map of what's already running
	currentBackends := make(map[string]client.BackendConfig)
	for _, backendCfg := range currentCfg.Backends {
		currentBackends[backendCfg.Name] = backendCfg
	}

	// Keep existing backends when they have equivalent config.
	// Update changed backends, and add new ones.
	for _, newBackend := range newCfg.Backends {
		current, ok := currentBackends[newBackend.Name]
		if ok && current.Equal(newBackend) {
			log.Debugf("Backend %s/%s unchanged", service.Name, current.Name)
			// no change for this one
			delete(currentBackends, current.Name)
			continue
		}

		// we need to remove and re-add this backend
		log.Debugf("Updating Backend %s/%s", service.Name, newBackend.Name)
		service.remove(newBackend.Name)
		service.add(NewBackend(newBackend))

		delete(currentBackends, newBackend.Name)
	}

	// remove any left over backends
	for name := range currentBackends {
		log.Debugf("Removing Backend %s/%s", service.Name, name)
		service.remove(name)
	}

	if currentCfg.Equal(newCfg) {
		log.Debugf("Service Unchanged %s", service.Name)
		return nil
	}

	// replace error pages if there's any change
	if !reflect.DeepEqual(service.errPagesCfg, newCfg.ErrorPages) {
		log.Debugf("Updating ErrorPages")
		service.errPagesCfg = newCfg.ErrorPages
		service.errorPages.Update(newCfg.ErrorPages)
	}

	s.updateVHosts(service, newCfg.VirtualHosts)

	return nil
}

// update the VirtualHost entries for this service
// only to be called from UpdateService.
func (s *ServiceRegistry) updateVHosts(service *Service, newHosts []string) {
	// We could just clear the vhosts and the new list since we're doing
	// this all while the registry is locked, but because we want sane log
	// messages about adding remove endpoints, we have to diff the slices
	// anyway.

	oldHosts := service.VirtualHosts
	sort.Strings(oldHosts)
	sort.Strings(newHosts)

	// find the relative compliments of each set of hostnames
	var remove, add []string
	i, j := 0, 0
	for i < len(oldHosts) && j < len(newHosts) {
		if oldHosts[i] != newHosts[j] {
			if oldHosts[i] < newHosts[j] {
				// oldHosts[i] can't be in newHosts
				remove = append(remove, oldHosts[i])
				i++
				continue
			} else {
				// newHosts[j] can't be in oldHosts
				add = append(add, newHosts[j])
				j++
				continue
			}
		}
		i++
		j++
	}
	if i < len(oldHosts) {
		// there's more!
		remove = append(remove, oldHosts[i:]...)
	}
	if j < len(newHosts) {
		add = append(add, newHosts[j:]...)
	}

	// remove existing vhost entries for this service, and add new ones
	for _, name := range remove {
		vhost := s.vhosts[name]
		if vhost != nil {
			vhost.Remove(service)
		}
		if vhost.Len() == 0 {
			log.Println("Removing empty VirtualHost", name)
			delete(s.vhosts, name)
		}
	}

	for _, name := range add {
		vhost := s.vhosts[name]
		if vhost == nil {
			vhost = &VirtualHost{Name: name}
			s.vhosts[name] = vhost
		}
		vhost.Add(service)
	}

	// and replace the list
	service.VirtualHosts = newHosts
}

func (s *ServiceRegistry) RemoveService(name string) error {
	s.Lock()
	defer s.Unlock()

	svc, ok := s.svcs[name]
	if ok {
		log.Debugf("Removing Service %s", svc.Name)
		delete(s.svcs, name)
		svc.stop()

		for host := range s.vhosts {
			delete(s.vhosts, host)
		}

		return nil
	}
	return ErrNoService
}

func (s *ServiceRegistry) ServiceStats(serviceName string) (ServiceStat, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return ServiceStat{}, ErrNoService
	}
	return service.Stats(), nil
}

func (s *ServiceRegistry) ServiceConfig(serviceName string) (client.ServiceConfig, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return client.ServiceConfig{}, ErrNoService
	}
	return service.Config(), nil
}

func (s *ServiceRegistry) BackendStats(serviceName, backendName string) (BackendStat, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return BackendStat{}, ErrNoService
	}

	for _, backend := range service.Backends {
		if backendName == backend.Name {
			return backend.Stats(), nil
		}
	}
	return BackendStat{}, ErrNoBackend
}

// Add or update a Backend on an existing Service.
func (s *ServiceRegistry) AddBackend(svcName string, backendCfg client.BackendConfig) error {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	log.Debugf("Adding Backend %s/%s", service.Name, backendCfg.Name)
	service.add(NewBackend(backendCfg))
	return nil
}

// Remove a Backend from an existing Service.
func (s *ServiceRegistry) RemoveBackend(svcName, backendName string) error {
	s.Lock()
	defer s.Unlock()

	log.Debugf("Removing Backend %s/%s", svcName, backendName)
	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	if !service.remove(backendName) {
		return ErrNoBackend
	}
	return nil
}

func (s *ServiceRegistry) Stats() []ServiceStat {
	s.Lock()
	defer s.Unlock()

	stats := []ServiceStat{}
	for _, service := range s.svcs {
		stats = append(stats, service.Stats())
	}

	return stats
}

func (s *ServiceRegistry) Config() []client.ServiceConfig {
	s.Lock()
	defer s.Unlock()

	var configs []client.ServiceConfig
	for _, service := range s.svcs {
		configs = append(configs, service.Config())
	}

	return configs
}

func (s *ServiceRegistry) String() string {
	return string(marshal(s.Config()))
}
<commit_msg>Return a service even if all backends are down<commit_after>package main

import (
	"fmt"
	"reflect"
	"sort"
	"sync"

	"github.com/litl/galaxy/log"
	"github.com/litl/galaxy/shuttle/client"
)

var (
	ErrNoService        = fmt.Errorf("service does not exist")
	ErrNoBackend        = fmt.Errorf("backend does not exist")
	ErrDuplicateService = fmt.Errorf("service already exists")
	ErrDuplicateBackend = fmt.Errorf("backend already exists")
)

type VirtualHost struct {
	sync.Mutex
	Name string
	// All services registered under this vhost name.
	services []*Service
	// The last one we returned so we can RoundRobin them.
	last int
}

func (v *VirtualHost) Len() int {
	v.Lock()
	defer v.Unlock()
	return len(v.services)
}

// Insert a service
// do nothing if the service already is registered
func (v *VirtualHost) Add(svc *Service) {
	v.Lock()
	defer v.Unlock()
	for _, s := range v.services {
		if s.Name == svc.Name {
			log.Debugf("Service %s already registered in VirtualHost %s", svc.Name, v.Name)
			return
		}
	}

	// TODO: is this the best place to log these?
	svcCfg := svc.Config()
	for _, backend := range svcCfg.Backends {
		log.Printf("Adding HTTP endpoint http://%s to %s", backend.Addr, v.Name)
	}
	v.services = append(v.services, svc)
}

func (v *VirtualHost) Remove(svc *Service) {
	v.Lock()
	defer v.Unlock()

	found := -1
	for i, s := range v.services {
		if s.Name == svc.Name {
			found = i
			break
		}
	}

	if found < 0 {
		log.Debugf("Service %s not found under VirtualHost %s", svc.Name, v.Name)
		return
	}

	// safe way to get the backends info for logging
	svcCfg := svc.Config()

	// Now removing this Service
	for _, backend := range svcCfg.Backends {
		log.Printf("Removing HTTP endpoint http://%s from %s", backend.Addr, v.Name)
	}

	v.services = append(v.services[:found], v.services[found+1:]...)
}

// Return a *Service for this VirtualHost
func (v *VirtualHost) Service() *Service {
	v.Lock()
	defer v.Unlock()

	if len(v.services) == 0 {
		log.Warnf("No Services registered for VirtualHost %s", v.Name)
		return nil
	}

	// start cycling through the services in case one has no backends available
	for i := 1; i <= len(v.services); i++ {
		idx := (v.last + i) % len(v.services)
		if v.services[idx].Available() > 0 {
			v.last = idx
			return v.services[idx]
		}
	}

	// even if all backends are down, return a service so that the request can
	// be processed normally (we may have a custom 502 error page for this)
	return v.services[v.last]
}

//TODO: notify or prevent vhost name conflicts between services.
// ServiceRegistry is a global container for all configured services.
type ServiceRegistry struct {
	sync.Mutex
	svcs map[string]*Service
	// Multiple services may respond from a single vhost
	vhosts map[string]*VirtualHost
}

// Return a service by name.
func (s *ServiceRegistry) GetService(name string) *Service {
	s.Lock()
	defer s.Unlock()
	return s.svcs[name]
}

// Return a service that handles a particular vhost by name.
func (s *ServiceRegistry) GetVHostService(name string) *Service {
	s.Lock()
	defer s.Unlock()

	if vhost := s.vhosts[name]; vhost != nil {
		return vhost.Service()
	}
	return nil
}

func (s *ServiceRegistry) VHostsLen() int {
	s.Lock()
	defer s.Unlock()
	return len(s.vhosts)
}

// Add a new service to the Registry.
// Do not replace an existing service.
func (s *ServiceRegistry) AddService(cfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	log.Debug("Adding service:", cfg.Name)
	if _, ok := s.svcs[cfg.Name]; ok {
		log.Debug("Service already exists:", cfg.Name)
		return ErrDuplicateService
	}

	service := NewService(cfg)
	s.svcs[service.Name] = service

	for _, name := range cfg.VirtualHosts {
		vhost := s.vhosts[name]
		if vhost == nil {
			vhost = &VirtualHost{Name: name}
			s.vhosts[name] = vhost
		}
		vhost.Add(service)
	}

	return service.start()
}

// Replace the service's configuration, or update its list of backends.
// Replacing a configuration will shutdown the existing service, and start a
// new one, which will cause the listening socket to be temporarily
// unavailable.
func (s *ServiceRegistry) UpdateService(newCfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	log.Debug("Updating Service:", newCfg.Name)
	service, ok := s.svcs[newCfg.Name]
	if !ok {
		log.Debug("Service not found:", newCfg.Name)
		return ErrNoService
	}

	currentCfg := service.Config()

	// Lots of looping here (including fetching the Config, but the cardinality
	// of Backends shouldn't be very large, and the default RoundRobin balancing
	// is much simpler with a slice.

	// we're going to update just the backends for this config
	// get a map of what's already running
	currentBackends := make(map[string]client.BackendConfig)
	for _, backendCfg := range currentCfg.Backends {
		currentBackends[backendCfg.Name] = backendCfg
	}

	// Keep existing backends when they have equivalent config.
	// Update changed backends, and add new ones.
	for _, newBackend := range newCfg.Backends {
		current, ok := currentBackends[newBackend.Name]
		if ok && current.Equal(newBackend) {
			log.Debugf("Backend %s/%s unchanged", service.Name, current.Name)
			// no change for this one
			delete(currentBackends, current.Name)
			continue
		}

		// we need to remove and re-add this backend
		log.Debugf("Updating Backend %s/%s", service.Name, newBackend.Name)
		service.remove(newBackend.Name)
		service.add(NewBackend(newBackend))

		delete(currentBackends, newBackend.Name)
	}

	// remove any left over backends
	for name := range currentBackends {
		log.Debugf("Removing Backend %s/%s", service.Name, name)
		service.remove(name)
	}

	if currentCfg.Equal(newCfg) {
		log.Debugf("Service Unchanged %s", service.Name)
		return nil
	}

	// replace error pages if there's any change
	if !reflect.DeepEqual(service.errPagesCfg, newCfg.ErrorPages) {
		log.Debugf("Updating ErrorPages")
		service.errPagesCfg = newCfg.ErrorPages
		service.errorPages.Update(newCfg.ErrorPages)
	}

	s.updateVHosts(service, newCfg.VirtualHosts)

	return nil
}

// update the VirtualHost entries for this service
// only to be called from UpdateService.
func (s *ServiceRegistry) updateVHosts(service *Service, newHosts []string) {
	// We could just clear the vhosts and the new list since we're doing
	// this all while the registry is locked, but because we want sane log
	// messages about adding remove endpoints, we have to diff the slices
	// anyway.

	oldHosts := service.VirtualHosts
	sort.Strings(oldHosts)
	sort.Strings(newHosts)

	// find the relative compliments of each set of hostnames
	var remove, add []string
	i, j := 0, 0
	for i < len(oldHosts) && j < len(newHosts) {
		if oldHosts[i] != newHosts[j] {
			if oldHosts[i] < newHosts[j] {
				// oldHosts[i] can't be in newHosts
				remove = append(remove, oldHosts[i])
				i++
				continue
			} else {
				// newHosts[j] can't be in oldHosts
				add = append(add, newHosts[j])
				j++
				continue
			}
		}
		i++
		j++
	}
	if i < len(oldHosts) {
		// there's more!
		remove = append(remove, oldHosts[i:]...)
	}
	if j < len(newHosts) {
		add = append(add, newHosts[j:]...)
	}

	// remove existing vhost entries for this service, and add new ones
	for _, name := range remove {
		vhost := s.vhosts[name]
		if vhost != nil {
			vhost.Remove(service)
		}
		if vhost.Len() == 0 {
			log.Println("Removing empty VirtualHost", name)
			delete(s.vhosts, name)
		}
	}

	for _, name := range add {
		vhost := s.vhosts[name]
		if vhost == nil {
			vhost = &VirtualHost{Name: name}
			s.vhosts[name] = vhost
		}
		vhost.Add(service)
	}

	// and replace the list
	service.VirtualHosts = newHosts
}

func (s *ServiceRegistry) RemoveService(name string) error {
	s.Lock()
	defer s.Unlock()

	svc, ok := s.svcs[name]
	if ok {
		log.Debugf("Removing Service %s", svc.Name)
		delete(s.svcs, name)
		svc.stop()

		for host := range s.vhosts {
			delete(s.vhosts, host)
		}

		return nil
	}
	return ErrNoService
}

func (s *ServiceRegistry) ServiceStats(serviceName string) (ServiceStat, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return ServiceStat{}, ErrNoService
	}
	return service.Stats(), nil
}

func (s *ServiceRegistry) ServiceConfig(serviceName string) (client.ServiceConfig, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return client.ServiceConfig{}, ErrNoService
	}
	return service.Config(), nil
}

func (s *ServiceRegistry) BackendStats(serviceName, backendName string) (BackendStat, error) {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[serviceName]
	if !ok {
		return BackendStat{}, ErrNoService
	}

	for _, backend := range service.Backends {
		if backendName == backend.Name {
			return backend.Stats(), nil
		}
	}
	return BackendStat{}, ErrNoBackend
}

// Add or update a Backend on an existing Service.
func (s *ServiceRegistry) AddBackend(svcName string, backendCfg client.BackendConfig) error {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	log.Debugf("Adding Backend %s/%s", service.Name, backendCfg.Name)
	service.add(NewBackend(backendCfg))
	return nil
}

// Remove a Backend from an existing Service.
func (s *ServiceRegistry) RemoveBackend(svcName, backendName string) error {
	s.Lock()
	defer s.Unlock()

	log.Debugf("Removing Backend %s/%s", svcName, backendName)
	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	if !service.remove(backendName) {
		return ErrNoBackend
	}
	return nil
}

func (s *ServiceRegistry) Stats() []ServiceStat {
	s.Lock()
	defer s.Unlock()

	stats := []ServiceStat{}
	for _, service := range s.svcs {
		stats = append(stats, service.Stats())
	}

	return stats
}

func (s *ServiceRegistry) Config() []client.ServiceConfig {
	s.Lock()
	defer s.Unlock()

	var configs []client.ServiceConfig
	for _, service := range s.svcs {
		configs = append(configs, service.Config())
	}

	return configs
}

func (s *ServiceRegistry) String() string {
	return string(marshal(s.Config()))
}
<|endoftext|>