<commit_before>package gdrive2slack

import (
	"github.com/optionfactory/gdrive2slack/google"
	"github.com/optionfactory/gdrive2slack/google/drive"
	"github.com/optionfactory/gdrive2slack/mailchimp"
	"github.com/optionfactory/gdrive2slack/slack"
	"os"
	"sync"
	"time"
)

func EventLoop(env *Environment) {
	subscriptions, err := LoadSubscriptions("subscriptions.json")
	if err != nil {
		env.Logger.Error("unreadable subscriptions file: %s", err)
		os.Exit(1)
	}

	lastLoopTime := time.Time{}
	waitFor := time.Duration(0)
	for {
		if !lastLoopTime.IsZero() {
			waitFor = time.Duration(30)*time.Second - time.Now().Sub(lastLoopTime)
		}
		if waitFor < 0 {
			waitFor = time.Duration(1) * time.Second
		}
		select {
		case subscriptionAndAccessToken := <-env.RegisterChannel:
			subscription := subscriptionAndAccessToken.Subscription
			subscriptions.Add(subscription, subscriptionAndAccessToken.GoogleAccessToken)
			if subscriptions.Contains(subscription.GoogleUserInfo.Email) {
				env.Logger.Info("*subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
			} else {
				env.Logger.Info("+subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
				go mailchimpRegistrationTask(env, subscription)
			}
		case email := <-env.DiscardChannel:
			subscription := subscriptions.Remove(email)
			env.Logger.Info("-subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
			go mailchimpDeregistrationTask(env, subscription)
		case s := <-env.SignalsChannel:
			env.Logger.Info("Exiting: got signal %v", s)
			os.Exit(0)
		case <-time.After(waitFor):
			lastLoopTime = time.Now()
			var waitGroup sync.WaitGroup
			for k, subscription := range subscriptions.Info {
				waitGroup.Add(1)
				go serveUserTask(env, &waitGroup, subscription, subscriptions.States[k])
			}
			waitGroup.Wait()
			env.Logger.Info("Served %d clients", len(subscriptions.Info))
		}
	}
}

func serveUserTask(env *Environment, waitGroup *sync.WaitGroup, subscription *Subscription, userState *UserState) {
	email := subscription.GoogleUserInfo.Email
	slackUser := subscription.SlackUserInfo.User
	defer func() {
		if r := recover(); r != nil {
			env.Logger.Error("[%s/%s] removing handler. reason: %v", email, slackUser, r)
			env.DiscardChannel <- email

		}
		waitGroup.Done()
	}()
	var err error
	if userState.Gdrive.LargestChangeId == 0 {

		userState.GoogleAccessToken, err = google.DoWithAccessToken(env.Configuration.Google, env.HttpClient, subscription.GoogleRefreshToken, userState.GoogleAccessToken, func(at string) (google.StatusCode, error) {
			return drive.LargestChangeId(env.HttpClient, userState.Gdrive, at)
		})
		if err != nil {
			env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		}
		return
	}

	userState.GoogleAccessToken, err = google.DoWithAccessToken(env.Configuration.Google, env.HttpClient, subscription.GoogleRefreshToken, userState.GoogleAccessToken, func(at string) (google.StatusCode, error) {
		return drive.DetectChanges(env.HttpClient, userState.Gdrive, at)
	})
	if err != nil {
		env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		return
	}
	if len(userState.Gdrive.ChangeSet) > 0 {
		env.Logger.Info("[%s/%s] @%v %v changes", email, slackUser, userState.Gdrive.LargestChangeId, len(userState.Gdrive.ChangeSet))
		message := CreateSlackMessage(subscription, userState, env.Version)
		status, err := slack.PostMessage(env.HttpClient, subscription.SlackAccessToken, message)
		if status == slack.NotAuthed || status == slack.InvalidAuth || status == slack.AccountInactive || status == slack.TokenRevoked {
			panic(err)
		}
		if status != slack.Ok {
			env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		}
		if status == slack.ChannelNotFound {
			status, err = slack.PostMessage(env.HttpClient, subscription.SlackAccessToken, CreateSlackUnknownChannelMessage(subscription, env.Configuration.Google.RedirectUri, message))
			if status == slack.NotAuthed || status == slack.InvalidAuth || status == slack.AccountInactive || status == slack.TokenRevoked {
				panic(err)
			}
			if status != slack.Ok {
				env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
			}
		}
	}
}

func mailchimpRegistrationTask(env *Environment, subscription *Subscription) {
	defer mailchimpRecover(env, subscription, "registration")
	if !env.Configuration.Mailchimp.IsMailchimpConfigured() {
		return
	}
	error := mailchimp.Subscribe(env.Configuration.Mailchimp, env.HttpClient, &mailchimp.SubscriptionRequest{
		Email:     subscription.GoogleUserInfo.Email,
		FirstName: subscription.GoogleUserInfo.GivenName,
		LastName:  subscription.GoogleUserInfo.FamilyName,
	})
	if error != nil {
		env.Logger.Warning("mailchimp/subscribe@%s %s", subscription.GoogleUserInfo.Email, error)
	}
}

func mailchimpDeregistrationTask(env *Environment, subscription *Subscription) {
	defer mailchimpRecover(env, subscription, "deregistration")
	if !env.Configuration.Mailchimp.IsMailchimpConfigured() {
		return
	}
	error := mailchimp.Unsubscribe(env.Configuration.Mailchimp, env.HttpClient, subscription.GoogleUserInfo.Email)
	if error != nil {
		env.Logger.Warning("mailchimp/unsubscribe@%s %s", subscription.GoogleUserInfo.Email, error)
	}
}

func mailchimpRecover(env *Environment, subscription *Subscription, task string) {
	if r := recover(); r != nil {
		env.Logger.Warning("[%s/%s] unexpected error in mailchimp %s task: %v", subscription.GoogleUserInfo.Email, subscription.SlackUserInfo.User, task, r)
	}
}
<commit_msg>fix: mailchimp subscriptions<commit_after>package gdrive2slack

import (
	"github.com/optionfactory/gdrive2slack/google"
	"github.com/optionfactory/gdrive2slack/google/drive"
	"github.com/optionfactory/gdrive2slack/mailchimp"
	"github.com/optionfactory/gdrive2slack/slack"
	"os"
	"sync"
	"time"
)

func EventLoop(env *Environment) {
	subscriptions, err := LoadSubscriptions("subscriptions.json")
	if err != nil {
		env.Logger.Error("unreadable subscriptions file: %s", err)
		os.Exit(1)
	}

	lastLoopTime := time.Time{}
	waitFor := time.Duration(0)
	for {
		if !lastLoopTime.IsZero() {
			waitFor = time.Duration(30)*time.Second - time.Now().Sub(lastLoopTime)
		}
		if waitFor < 0 {
			waitFor = time.Duration(1) * time.Second
		}
		select {
		case subscriptionAndAccessToken := <-env.RegisterChannel:
			subscription := subscriptionAndAccessToken.Subscription
			alreadySubscribed := subscriptions.Contains(subscription.GoogleUserInfo.Email)
			subscriptions.Add(subscription, subscriptionAndAccessToken.GoogleAccessToken)
			if alreadySubscribed {
				env.Logger.Info("*subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
			} else {
				env.Logger.Info("+subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
				go mailchimpRegistrationTask(env, subscription)
			}
		case email := <-env.DiscardChannel:
			subscription := subscriptions.Remove(email)
			env.Logger.Info("-subscription: %s '%s' '%s'", subscription.GoogleUserInfo.Email, subscription.GoogleUserInfo.GivenName, subscription.GoogleUserInfo.FamilyName)
			go mailchimpDeregistrationTask(env, subscription)
		case s := <-env.SignalsChannel:
			env.Logger.Info("Exiting: got signal %v", s)
			os.Exit(0)
		case <-time.After(waitFor):
			lastLoopTime = time.Now()
			var waitGroup sync.WaitGroup
			for k, subscription := range subscriptions.Info {
				waitGroup.Add(1)
				go serveUserTask(env, &waitGroup, subscription, subscriptions.States[k])
			}
			waitGroup.Wait()
			env.Logger.Info("Served %d clients", len(subscriptions.Info))
		}
	}
}

func serveUserTask(env *Environment, waitGroup *sync.WaitGroup, subscription *Subscription, userState *UserState) {
	email := subscription.GoogleUserInfo.Email
	slackUser := subscription.SlackUserInfo.User
	defer func() {
		if r := recover(); r != nil {
			env.Logger.Error("[%s/%s] removing handler. reason: %v", email, slackUser, r)
			env.DiscardChannel <- email

		}
		waitGroup.Done()
	}()
	var err error
	if userState.Gdrive.LargestChangeId == 0 {

		userState.GoogleAccessToken, err = google.DoWithAccessToken(env.Configuration.Google, env.HttpClient, subscription.GoogleRefreshToken, userState.GoogleAccessToken, func(at string) (google.StatusCode, error) {
			return drive.LargestChangeId(env.HttpClient, userState.Gdrive, at)
		})
		if err != nil {
			env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		}
		return
	}

	userState.GoogleAccessToken, err = google.DoWithAccessToken(env.Configuration.Google, env.HttpClient, subscription.GoogleRefreshToken, userState.GoogleAccessToken, func(at string) (google.StatusCode, error) {
		return drive.DetectChanges(env.HttpClient, userState.Gdrive, at)
	})
	if err != nil {
		env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		return
	}
	if len(userState.Gdrive.ChangeSet) > 0 {
		env.Logger.Info("[%s/%s] @%v %v changes", email, slackUser, userState.Gdrive.LargestChangeId, len(userState.Gdrive.ChangeSet))
		message := CreateSlackMessage(subscription, userState, env.Version)
		status, err := slack.PostMessage(env.HttpClient, subscription.SlackAccessToken, message)
		if status == slack.NotAuthed || status == slack.InvalidAuth || status == slack.AccountInactive || status == slack.TokenRevoked {
			panic(err)
		}
		if status != slack.Ok {
			env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
		}
		if status == slack.ChannelNotFound {
			status, err = slack.PostMessage(env.HttpClient, subscription.SlackAccessToken, CreateSlackUnknownChannelMessage(subscription, env.Configuration.Google.RedirectUri, message))
			if status == slack.NotAuthed || status == slack.InvalidAuth || status == slack.AccountInactive || status == slack.TokenRevoked {
				panic(err)
			}
			if status != slack.Ok {
				env.Logger.Warning("[%s/%s] %s", email, slackUser, err)
			}
		}
	}
}

func mailchimpRegistrationTask(env *Environment, subscription *Subscription) {
	defer mailchimpRecover(env, subscription, "registration")
	if !env.Configuration.Mailchimp.IsMailchimpConfigured() {
		return
	}
	error := mailchimp.Subscribe(env.Configuration.Mailchimp, env.HttpClient, &mailchimp.SubscriptionRequest{
		Email:     subscription.GoogleUserInfo.Email,
		FirstName: subscription.GoogleUserInfo.GivenName,
		LastName:  subscription.GoogleUserInfo.FamilyName,
	})
	if error != nil {
		env.Logger.Warning("mailchimp/subscribe@%s %s", subscription.GoogleUserInfo.Email, error)
	}
}

func mailchimpDeregistrationTask(env *Environment, subscription *Subscription) {
	defer mailchimpRecover(env, subscription, "deregistration")
	if !env.Configuration.Mailchimp.IsMailchimpConfigured() {
		return
	}
	error := mailchimp.Unsubscribe(env.Configuration.Mailchimp, env.HttpClient, subscription.GoogleUserInfo.Email)
	if error != nil {
		env.Logger.Warning("mailchimp/unsubscribe@%s %s", subscription.GoogleUserInfo.Email, error)
	}
}

func mailchimpRecover(env *Environment, subscription *Subscription, task string) {
	if r := recover(); r != nil {
		env.Logger.Warning("[%s/%s] unexpected error in mailchimp %s task: %v", subscription.GoogleUserInfo.Email, subscription.SlackUserInfo.User, task, r)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package sets

type String map[string]interface{}

func (s String) Len() int {
	return len(s)
}

func (s String) List() []string {
	var val []string
	for k := range s {
		val = append(val, k)
	}
	return val
}

func (s String) Has(val string) bool {
	_, found := s[val]
	return found
}

func (s String) Insert(vals ...string) {
	for _, val := range vals {
		s[val] = nil
	}
}

func (s String) Difference(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; !found {
			s3.Insert(k)
		}
	}
	return s3
}

func (s String) SymmetricDifference(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; !found {
			s3.Insert(k)
		}
	}
	for k := range s2 {
		if _, found := s[k]; !found {
			s3.Insert(k)
		}
	}
	return s3
}

func (s String) Intersection(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; found {
			s3.Insert(k)
		}
	}
	return s3
}
<commit_msg>kyaml/sets: preallocate memory<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package sets

type String map[string]interface{}

func (s String) Len() int {
	return len(s)
}

func (s String) List() []string {
	val := make([]string, 0, len(s))
	for k := range s {
		val = append(val, k)
	}
	return val
}

func (s String) Has(val string) bool {
	_, found := s[val]
	return found
}

func (s String) Insert(vals ...string) {
	for _, val := range vals {
		s[val] = nil
	}
}

func (s String) Difference(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; !found {
			s3.Insert(k)
		}
	}
	return s3
}

func (s String) SymmetricDifference(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; !found {
			s3.Insert(k)
		}
	}
	for k := range s2 {
		if _, found := s[k]; !found {
			s3.Insert(k)
		}
	}
	return s3
}

func (s String) Intersection(s2 String) String {
	s3 := String{}
	for k := range s {
		if _, found := s2[k]; found {
			s3.Insert(k)
		}
	}
	return s3
}
<|endoftext|>


<commit_before>package ingest

import (
	"io"

	"compress/bzip2"
	"encoding/json"

	"log"

	"github.com/RedisLabs/RediSearchBenchmark/index"
)

type redditDocument struct {
	Author     string  `json:"author"`
	Body       string  `json:"body"`
	Created    int64   `json:"created_utc"`
	Id         string  `json:"id"`
	Score      int64   `json:"score"`
	Ups        int64   `json:"ups"`
	Downs      int64   `json:"downs"`
	Subreddit  string  `json:"subreddit"`
	UvoteRatio float32 `json:"upvote_ratio"`
}

type RedditReader struct{}

func (rr *RedditReader) Read(r io.Reader, ch chan index.Document) error {
	log.Println("Reddit reader opening", r)
	bz := bzip2.NewReader(r)
	jr := json.NewDecoder(bz)

	var rd redditDocument

	//go func() {
	var err error

	for err != io.EOF {

		if err := jr.Decode(&rd); err != nil {
			log.Printf("Error decoding json: %s", err)
			break
		}
		doc := index.NewDocument(rd.Id, float32(rd.Score)).
			Set("body", rd.Body).
			Set("author", rd.Author).
			Set("sub", rd.Subreddit).
			Set("date", rd.Created).
			Set("ups", rd.Ups)

		ch <- doc
	}
	//close(ch)
	//}()
	return nil
}
<commit_msg>changed indexing of date<commit_after>package ingest

import (
	"io"
	"strings"

	"compress/bzip2"
	"encoding/json"

	"log"

	"strconv"

	"github.com/RedisLabs/RediSearchBenchmark/index"
)

type timestamp int64

func (t *timestamp) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	var i int64
	if i, err = strconv.ParseInt(s, 10, 64); err == nil {
		*t = timestamp(i)
	}

	return err
}

type redditDocument struct {
	Author     string    `json:"author"`
	Body       string    `json:"body"`
	Created    timestamp `json:"created_utc"`
	Id         string    `json:"id"`
	Score      int64     `json:"score"`
	Ups        int64     `json:"ups"`
	Downs      int64     `json:"downs"`
	Subreddit  string    `json:"subreddit"`
	UvoteRatio float32   `json:"upvote_ratio"`
}

type RedditReader struct{}

func (rr *RedditReader) Read(r io.Reader, ch chan index.Document) error {
	log.Println("Reddit reader opening", r)
	bz := bzip2.NewReader(r)
	jr := json.NewDecoder(bz)

	var rd redditDocument

	//go func() {
	var err error

	for err != io.EOF {

		if err := jr.Decode(&rd); err != nil {
			log.Printf("Error decoding json: %s", err)
			break
		}
		doc := index.NewDocument(rd.Id, float32(rd.Score)).
			Set("body", rd.Body).
			Set("author", rd.Author).
			Set("sub", rd.Subreddit).
			Set("date", int64(rd.Created)/86400).
			Set("ups", rd.Ups)

		ch <- doc
	}
	//close(ch)
	//}()
	return nil
}
<|endoftext|>


<commit_before>package confdis

import (
	"testing"
	"time"
)

type SampleConfig struct {
	Name  string   `json:"name"`
	Users []string `json:"users"`
	Meta  struct {
		Researcher string `json:"researcher"`
		Grant      int    `json:"grant"`
	} `json:"meta"`
}

func NewConfDis(t *testing.T, rootKey string) *ConfDis {
	c, err := New("localhost:6379", "test:confdis:simple", SampleConfig{})
	if err != nil {
		t.Fatal(err)
	}
	return c
}

func redisDelay() {
	// Allow reasonable delay for network/redis latency in the above
	// save operation.
	time.Sleep(time.Duration(100 * time.Millisecond))
}

func TestSimple(t *testing.T) {
	c := NewConfDis(t, "test:confdis:simple")
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Name = "primates"
		config.Users = []string{"chimp", "bonobo", "lemur"}
		config.Meta.Researcher = "Jane Goodall"
		config.Meta.Grant = 1200
		return nil
	}); err != nil {
		t.Fatal(err)
	}
}

func TestChangeNotification(t *testing.T) {
	// First client, with initial data.
	c := NewConfDis(t, "test:confdis:notify")
	go c.MustReceiveChanges()
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Name = "primates-changes"
		config.Users = []string{"chimp", "bonobo", "lemur"}
		config.Meta.Researcher = "Jane Goodall"
		config.Meta.Grant = 1200
		return nil
	}); err != nil {
		t.Fatal(err)
	}
	redisDelay()

	// Second client
	c2 := NewConfDis(t, "test:confdis:notify")
	go c2.MustReceiveChanges()

	if c2.Config.(*SampleConfig).Meta.Researcher != "Jane Goodall" {
		t.Fatal("different value")
	}

	// Trigger a change via the first client
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Meta.Researcher = "Francine Patterson"
		return nil
	}); err != nil {
		t.Fatal(err)
	}
	redisDelay()

	// Second client must get notified of that change
	if c2.Config.(*SampleConfig).Meta.Researcher != "Francine Patterson" {
		t.Fatal("did not receive change")
	}
}
<commit_msg>test case for AtomicSave<commit_after>package confdis

import (
	"testing"
	"time"
)

type SampleConfig struct {
	Name  string   `json:"name"`
	Users []string `json:"users"`
	Meta  struct {
		Researcher string `json:"researcher"`
		Grant      int    `json:"grant"`
	} `json:"meta"`
}

func NewConfDis(t *testing.T, rootKey string) *ConfDis {
	c, err := New("localhost:6379", "test:confdis:simple", SampleConfig{})
	if err != nil {
		t.Fatal(err)
	}
	return c
}

func redisDelay() {
	// Allow reasonable delay for network/redis latency in the above
	// save operation.
	time.Sleep(time.Duration(100 * time.Millisecond))
}

func TestSimple(t *testing.T) {
	c := NewConfDis(t, "test:confdis:simple")
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Name = "primates"
		config.Users = []string{"chimp", "bonobo", "lemur"}
		config.Meta.Researcher = "Jane Goodall"
		config.Meta.Grant = 1200
		return nil
	}); err != nil {
		t.Fatal(err)
	}
}

func TestChangeNotification(t *testing.T) {
	// First client, with initial data.
	c := NewConfDis(t, "test:confdis:notify")
	go c.MustReceiveChanges()
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Name = "primates-changes"
		config.Users = []string{"chimp", "bonobo", "lemur"}
		config.Meta.Researcher = "Jane Goodall"
		config.Meta.Grant = 1200
		return nil
	}); err != nil {
		t.Fatal(err)
	}
	redisDelay()

	// Second client
	c2 := NewConfDis(t, "test:confdis:notify")
	go c2.MustReceiveChanges()

	if c2.Config.(*SampleConfig).Meta.Researcher != "Jane Goodall" {
		t.Fatal("different value")
	}

	// Trigger a change via the first client
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Meta.Researcher = "Francine Patterson"
		return nil
	}); err != nil {
		t.Fatal(err)
	}
	redisDelay()

	// Second client must get notified of that change
	if c2.Config.(*SampleConfig).Meta.Researcher != "Francine Patterson" {
		t.Fatal("did not receive change")
	}
}

func TestAtomicSave(t *testing.T) {
	// First client, with initial data.
	c := NewConfDis(t, "test:confdis:atomicsave")
	go c.MustReceiveChanges()
	if err := c.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		config.Name = "primates-changes"
		config.Users = []string{"chimp", "bonobo", "lemur"}
		config.Meta.Researcher = "Jane Goodall"
		config.Meta.Grant = 1200
		return nil
	}); err != nil {
		t.Fatal(err)
	}
	redisDelay()

	// Trigger a change every 20 milliseconds
	go func() {
		for _ = range time.Tick(20 * time.Millisecond) {
			if err := c.AtomicSave(func (i interface{}) error {
				config := i.(*SampleConfig)
				config.Meta.Grant += 15
				return nil
			}); err != nil {
				t.Fatalf("Error in periodic-saving: %v", err)
			}
		}
	}()

	// Second client
	c2 := NewConfDis(t, "test:confdis:atomicsave")
	go c2.MustReceiveChanges()

	// Trigger a *slow* change, expecting write conflict.
	if err := c2.AtomicSave(func(i interface{}) error {
		config := i.(*SampleConfig)
		// Choose a delay value (50ms) greater than the frequency of
		// change (20ms) from the other client above.
		time.Sleep(50 * time.Millisecond)
		config.Meta.Researcher = "Francine Patterson"
		return nil
	}); err == nil {
		t.Fatal("Expecting this save to fail.")
	}else{
		t.Logf("Failed as expected with: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Anapaya Systems
//
// 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 cs

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	opentracing "github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/config"
	"github.com/scionproto/scion/go/lib/env"
	"github.com/scionproto/scion/go/lib/metrics"
	"github.com/scionproto/scion/go/lib/prom"
	"github.com/scionproto/scion/go/lib/scrypto"
	"github.com/scionproto/scion/go/lib/scrypto/cppki"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet"
	snetmetrics "github.com/scionproto/scion/go/lib/snet/metrics"
	"github.com/scionproto/scion/go/lib/topology"
	"github.com/scionproto/scion/go/pkg/ca/renewal"
	cstrust "github.com/scionproto/scion/go/pkg/cs/trust"
	"github.com/scionproto/scion/go/pkg/discovery"
	"github.com/scionproto/scion/go/pkg/service"
)

// InitTracer initializes the global tracer.
func InitTracer(tracing env.Tracing, id string) (io.Closer, error) {
	tracer, trCloser, err := tracing.NewTracer(id)
	if err != nil {
		return nil, err
	}
	opentracing.SetGlobalTracer(tracer)
	return trCloser, nil
}

// Metrics defines the metrics exposed by the control server.
//
// XXX(roosd): Currently, most counters are created in the packages. The will
// eventually be moved here.
type Metrics struct {
	BeaconDBQueriesTotal                   *prometheus.CounterVec
	BeaconingOriginatedTotal               *prometheus.CounterVec
	BeaconingPropagatedTotal               *prometheus.CounterVec
	BeaconingPropagatorInternalErrorsTotal *prometheus.CounterVec
	BeaconingReceivedTotal                 *prometheus.CounterVec
	BeaconingRegisteredTotal               *prometheus.CounterVec
	BeaconingRegistrarInternalErrorsTotal  *prometheus.CounterVec
	DiscoveryRequestsTotal                 *prometheus.CounterVec
	PathDBQueriesTotal                     *prometheus.CounterVec
	RenewalServerRequestsTotal             *prometheus.CounterVec
	RenewalHandledRequestsTotal            *prometheus.CounterVec
	RenewalRegisteredHandlers              *prometheus.GaugeVec
	SegmentLookupRequestsTotal             *prometheus.CounterVec
	SegmentLookupSegmentsSentTotal         *prometheus.CounterVec
	SegmentRegistrationsTotal              *prometheus.CounterVec
	TrustDBQueriesTotal                    *prometheus.CounterVec
	TrustLatestTRCNotBefore                prometheus.Gauge
	TrustLatestTRCNotAfter                 prometheus.Gauge
	TrustLatestTRCSerial                   prometheus.Gauge
	TrustTRCFileWritesTotal                *prometheus.CounterVec
	SCIONNetworkMetrics                    snet.SCIONNetworkMetrics
	SCIONPacketConnMetrics                 snet.SCIONPacketConnMetrics
	SCMPErrors                             metrics.Counter
	TopoLoader                             topology.LoaderMetrics
}

func NewMetrics() *Metrics {
	scionPacketConnMetrics := snetmetrics.NewSCIONPacketConnMetrics()
	return &Metrics{
		BeaconDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "beacondb_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		BeaconingOriginatedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_originated_beacons_total",
				Help: "Total number of beacons originated.",
			},
			[]string{"egress_interface", prom.LabelResult},
		),
		BeaconingPropagatedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_propagated_beacons_total",
				Help: "Total number of beacons propagated.",
			},
			[]string{"start_isd_as", "ingress_interface", "egress_interface", prom.LabelResult},
		),
		BeaconingPropagatorInternalErrorsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_propagator_internal_errors_total",
				Help: "Total number of internal errors in the beacon propagator.",
			},
			[]string{},
		),
		BeaconingReceivedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_received_beacons_total",
				Help: "Total number of beacons received.",
			},
			[]string{"ingress_interface", prom.LabelNeighIA, prom.LabelResult},
		),
		BeaconingRegisteredTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_registered_segments_total",
				Help: "Total number of segments registered.",
			},
			[]string{"start_isd_as", "ingress_interface", "seg_type", prom.LabelResult},
		),
		BeaconingRegistrarInternalErrorsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_registrar_internal_errors_total",
				Help: "Total number of internal errors in the beacon registrar.",
			},
			[]string{"seg_type"},
		),
		DiscoveryRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "discovery_requests_total",
				Help: "Total number of discovery requests served.",
			},
			discovery.Topology{}.RequestsLabels(),
		),
		PathDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "pathdb_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		RenewalServerRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "renewal_received_requests_total",
				Help: "Total number of renewal requests served.",
			},
			[]string{prom.LabelResult},
		),
		RenewalHandledRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "renewal_handled_requests_total",
				Help: "Total number of renewal requests served by each handler type" +
					" (legacy, in-process, delegating).",
			},
			[]string{prom.LabelResult, "type"},
		),
		RenewalRegisteredHandlers: promauto.NewGaugeVec(
			prometheus.GaugeOpts{
				Name: "renewal_registered_handlers",
				Help: "Exposes which handler type (legacy, in-process, delegating) is registered.",
			},
			[]string{"type"},
		),
		SegmentLookupRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_lookup_requests_total",
				Help: "Total number of path segments requests received.",
			},
			[]string{"dst_isd", "seg_type", prom.LabelResult},
		),
		SegmentLookupSegmentsSentTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_lookup_segments_sent_total",
				Help: "Total number of path segments sent in the replies.",
			},
			[]string{"dst_isd", "seg_type"},
		),
		SegmentRegistrationsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_registry_segments_received_total",
				Help: "Total number of path segments received through registrations.",
			},
			[]string{"src", "seg_type", prom.LabelResult},
		),
		TrustDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "trustengine_db_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		TrustLatestTRCNotBefore: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_not_before_time_seconds",
				Help: "The not_before time of the latest TRC for the local ISD " +
					"in seconds since UNIX epoch.",
			},
		),
		TrustLatestTRCNotAfter: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_not_after_time_seconds",
				Help: "The not_after time of the latest TRC for the local ISD " +
					"in seconds since UNIX epoch.",
			},
		),
		TrustLatestTRCSerial: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_serial_number",
				Help: "The serial number of the latest TRC for the local ISD.",
			},
		),
		TrustTRCFileWritesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "trustengine_trc_file_writes_total",
				Help: "Total TRC filesystem file operations.",
			},
			[]string{prom.LabelResult},
		),
		SCIONNetworkMetrics:    snetmetrics.NewSCIONNetworkMetrics(),
		SCIONPacketConnMetrics: scionPacketConnMetrics,
		SCMPErrors:             scionPacketConnMetrics.SCMPErrors,
		TopoLoader:             loaderMetrics(),
	}
}

// RegisterHTTPEndpoints starts the HTTP endpoints that expose the metrics and
// additional information.
func RegisterHTTPEndpoints(
	elemId string,
	cfg config.Config,
	signer cstrust.RenewingSigner,
	ca renewal.ChainBuilder,
	topo *topology.Loader,
) error {
	statusPages := service.StatusPages{
		"info":      service.NewInfoStatusPage(),
		"config":    service.NewConfigStatusPage(cfg),
		"log/level": service.NewLogLevelStatusPage(),
		"topology":  service.NewTopologyStatusPage(topo),
		"signer":    signerStatusPage(signer),
	}
	if ca != (renewal.ChainBuilder{}) {
		statusPages["ca"] = caStatusPage(ca)
	}
	if err := statusPages.Register(http.DefaultServeMux, elemId); err != nil {
		return serrors.WrapStr("registering status pages", err)
	}
	return nil
}

func signerStatusPage(signer cstrust.RenewingSigner) service.StatusPage {
	handler := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		s, err := signer.SignerGen.Generate(r.Context())
		if err != nil {
			http.Error(w, "Unable to get signer", http.StatusInternalServerError)
			return
		}

		type Subject struct {
			IA addr.IA `json:"isd_as"`
		}
		type TRCID struct {
			ISD    addr.ISD        `json:"isd"`
			Base   scrypto.Version `json:"base_number"`
			Serial scrypto.Version `json:"serial_number"`
		}
		type Validity struct {
			NotBefore time.Time `json:"not_before"`
			NotAfter  time.Time `json:"not_after"`
		}
		rep := struct {
			Subject       Subject   `json:"subject"`
			SubjectKeyID  string    `json:"subject_key_id"`
			Expiration    time.Time `json:"expiration"`
			TRCID         TRCID     `json:"trc_id"`
			ChainValidity Validity  `json:"chain_validity"`
			InGrace       bool      `json:"in_grace_period"`
		}{
			Subject:      Subject{IA: s.IA},
			SubjectKeyID: fmt.Sprintf("% X", s.SubjectKeyID),
			Expiration:   s.Expiration,
			TRCID: TRCID{
				ISD:    s.TRCID.ISD,
				Base:   s.TRCID.Base,
				Serial: s.TRCID.Serial,
			},
			ChainValidity: Validity{
				NotBefore: s.ChainValidity.NotBefore,
				NotAfter:  s.ChainValidity.NotAfter,
			},
			InGrace: s.InGrace,
		}
		enc := json.NewEncoder(w)
		enc.SetIndent("", "    ")
		if err := enc.Encode(rep); err != nil {
			http.Error(w, "Unable to marshal response", http.StatusInternalServerError)
			return
		}
	}
	return service.StatusPage{
		Info:    "SCION signer info",
		Handler: handler,
	}
}

func caStatusPage(signer renewal.ChainBuilder) service.StatusPage {
	handler := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		s, err := signer.PolicyGen.Generate(r.Context())
		if err != nil {
			http.Error(w, "No active signer", http.StatusInternalServerError)
			return
		}

		ia, err := cppki.ExtractIA(s.Certificate.Subject)
		if err != nil {
			http.Error(w, "Unable to get extract ISD-AS", http.StatusInternalServerError)
			return
		}

		type Subject struct {
			IA addr.IA `json:"isd_as"`
		}
		type Validity struct {
			NotBefore time.Time `json:"not_before"`
			NotAfter  time.Time `json:"not_after"`
		}
		type Policy struct {
			ChainLifetime string `json:"chain_lifetime"`
		}
		rep := struct {
			Subject      Subject  `json:"subject"`
			SubjectKeyID string   `json:"subject_key_id"`
			Policy       Policy   `json:"policy"`
			CertValidity Validity `json:"cert_validity"`
		}{
			Subject:      Subject{IA: ia},
			SubjectKeyID: fmt.Sprintf("% X", s.Certificate.SubjectKeyId),
			Policy: Policy{
				ChainLifetime: s.Validity.String(),
			},
			CertValidity: Validity{
				NotBefore: s.Certificate.NotBefore,
				NotAfter:  s.Certificate.NotAfter,
			},
		}
		enc := json.NewEncoder(w)
		enc.SetIndent("", "    ")
		if err := enc.Encode(rep); err != nil {
			http.Error(w, "Unable to marshal response", http.StatusInternalServerError)
			return
		}
	}
	return service.StatusPage{
		Info:    "CA status",
		Handler: handler,
	}
}

func loaderMetrics() topology.LoaderMetrics {
	updates := prom.NewCounterVec("", "",
		"topology_updates_total",
		"The total number of updates.",
		[]string{prom.LabelResult},
	)
	return topology.LoaderMetrics{
		ValidationErrors: metrics.NewPromCounter(updates).With(prom.LabelResult, "err_validate"),
		ReadErrors:       metrics.NewPromCounter(updates).With(prom.LabelResult, "err_read"),
		LastUpdate: metrics.NewPromGauge(
			prom.NewGaugeVec("", "",
				"topology_last_update_time",
				"Timestamp of the last successful update.",
				[]string{},
			),
		),
		Updates: metrics.NewPromCounter(updates).With(prom.LabelResult, prom.Success),
	}
}
<commit_msg>control: make topology status page optional<commit_after>// Copyright 2020 Anapaya Systems
//
// 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 cs

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	opentracing "github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/config"
	"github.com/scionproto/scion/go/lib/env"
	"github.com/scionproto/scion/go/lib/metrics"
	"github.com/scionproto/scion/go/lib/prom"
	"github.com/scionproto/scion/go/lib/scrypto"
	"github.com/scionproto/scion/go/lib/scrypto/cppki"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet"
	snetmetrics "github.com/scionproto/scion/go/lib/snet/metrics"
	"github.com/scionproto/scion/go/lib/topology"
	"github.com/scionproto/scion/go/pkg/ca/renewal"
	cstrust "github.com/scionproto/scion/go/pkg/cs/trust"
	"github.com/scionproto/scion/go/pkg/discovery"
	"github.com/scionproto/scion/go/pkg/service"
)

// InitTracer initializes the global tracer.
func InitTracer(tracing env.Tracing, id string) (io.Closer, error) {
	tracer, trCloser, err := tracing.NewTracer(id)
	if err != nil {
		return nil, err
	}
	opentracing.SetGlobalTracer(tracer)
	return trCloser, nil
}

// Metrics defines the metrics exposed by the control server.
//
// XXX(roosd): Currently, most counters are created in the packages. The will
// eventually be moved here.
type Metrics struct {
	BeaconDBQueriesTotal                   *prometheus.CounterVec
	BeaconingOriginatedTotal               *prometheus.CounterVec
	BeaconingPropagatedTotal               *prometheus.CounterVec
	BeaconingPropagatorInternalErrorsTotal *prometheus.CounterVec
	BeaconingReceivedTotal                 *prometheus.CounterVec
	BeaconingRegisteredTotal               *prometheus.CounterVec
	BeaconingRegistrarInternalErrorsTotal  *prometheus.CounterVec
	DiscoveryRequestsTotal                 *prometheus.CounterVec
	PathDBQueriesTotal                     *prometheus.CounterVec
	RenewalServerRequestsTotal             *prometheus.CounterVec
	RenewalHandledRequestsTotal            *prometheus.CounterVec
	RenewalRegisteredHandlers              *prometheus.GaugeVec
	SegmentLookupRequestsTotal             *prometheus.CounterVec
	SegmentLookupSegmentsSentTotal         *prometheus.CounterVec
	SegmentRegistrationsTotal              *prometheus.CounterVec
	TrustDBQueriesTotal                    *prometheus.CounterVec
	TrustLatestTRCNotBefore                prometheus.Gauge
	TrustLatestTRCNotAfter                 prometheus.Gauge
	TrustLatestTRCSerial                   prometheus.Gauge
	TrustTRCFileWritesTotal                *prometheus.CounterVec
	SCIONNetworkMetrics                    snet.SCIONNetworkMetrics
	SCIONPacketConnMetrics                 snet.SCIONPacketConnMetrics
	SCMPErrors                             metrics.Counter
	TopoLoader                             topology.LoaderMetrics
}

func NewMetrics() *Metrics {
	scionPacketConnMetrics := snetmetrics.NewSCIONPacketConnMetrics()
	return &Metrics{
		BeaconDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "beacondb_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		BeaconingOriginatedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_originated_beacons_total",
				Help: "Total number of beacons originated.",
			},
			[]string{"egress_interface", prom.LabelResult},
		),
		BeaconingPropagatedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_propagated_beacons_total",
				Help: "Total number of beacons propagated.",
			},
			[]string{"start_isd_as", "ingress_interface", "egress_interface", prom.LabelResult},
		),
		BeaconingPropagatorInternalErrorsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_propagator_internal_errors_total",
				Help: "Total number of internal errors in the beacon propagator.",
			},
			[]string{},
		),
		BeaconingReceivedTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_received_beacons_total",
				Help: "Total number of beacons received.",
			},
			[]string{"ingress_interface", prom.LabelNeighIA, prom.LabelResult},
		),
		BeaconingRegisteredTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_registered_segments_total",
				Help: "Total number of segments registered.",
			},
			[]string{"start_isd_as", "ingress_interface", "seg_type", prom.LabelResult},
		),
		BeaconingRegistrarInternalErrorsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_beaconing_registrar_internal_errors_total",
				Help: "Total number of internal errors in the beacon registrar.",
			},
			[]string{"seg_type"},
		),
		DiscoveryRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "discovery_requests_total",
				Help: "Total number of discovery requests served.",
			},
			discovery.Topology{}.RequestsLabels(),
		),
		PathDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "pathdb_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		RenewalServerRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "renewal_received_requests_total",
				Help: "Total number of renewal requests served.",
			},
			[]string{prom.LabelResult},
		),
		RenewalHandledRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "renewal_handled_requests_total",
				Help: "Total number of renewal requests served by each handler type" +
					" (legacy, in-process, delegating).",
			},
			[]string{prom.LabelResult, "type"},
		),
		RenewalRegisteredHandlers: promauto.NewGaugeVec(
			prometheus.GaugeOpts{
				Name: "renewal_registered_handlers",
				Help: "Exposes which handler type (legacy, in-process, delegating) is registered.",
			},
			[]string{"type"},
		),
		SegmentLookupRequestsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_lookup_requests_total",
				Help: "Total number of path segments requests received.",
			},
			[]string{"dst_isd", "seg_type", prom.LabelResult},
		),
		SegmentLookupSegmentsSentTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_lookup_segments_sent_total",
				Help: "Total number of path segments sent in the replies.",
			},
			[]string{"dst_isd", "seg_type"},
		),
		SegmentRegistrationsTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "control_segment_registry_segments_received_total",
				Help: "Total number of path segments received through registrations.",
			},
			[]string{"src", "seg_type", prom.LabelResult},
		),
		TrustDBQueriesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "trustengine_db_queries_total",
				Help: "Total queries to the database",
			},
			[]string{"driver", "operation", prom.LabelResult},
		),
		TrustLatestTRCNotBefore: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_not_before_time_seconds",
				Help: "The not_before time of the latest TRC for the local ISD " +
					"in seconds since UNIX epoch.",
			},
		),
		TrustLatestTRCNotAfter: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_not_after_time_seconds",
				Help: "The not_after time of the latest TRC for the local ISD " +
					"in seconds since UNIX epoch.",
			},
		),
		TrustLatestTRCSerial: promauto.NewGauge(
			prometheus.GaugeOpts{
				Name: "trustengine_latest_trc_serial_number",
				Help: "The serial number of the latest TRC for the local ISD.",
			},
		),
		TrustTRCFileWritesTotal: promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "trustengine_trc_file_writes_total",
				Help: "Total TRC filesystem file operations.",
			},
			[]string{prom.LabelResult},
		),
		SCIONNetworkMetrics:    snetmetrics.NewSCIONNetworkMetrics(),
		SCIONPacketConnMetrics: scionPacketConnMetrics,
		SCMPErrors:             scionPacketConnMetrics.SCMPErrors,
		TopoLoader:             loaderMetrics(),
	}
}

// RegisterHTTPEndpoints starts the HTTP endpoints that expose the metrics and
// additional information.
func RegisterHTTPEndpoints(
	elemId string,
	cfg config.Config,
	signer cstrust.RenewingSigner,
	ca renewal.ChainBuilder,
	topo *topology.Loader,
) error {
	statusPages := service.StatusPages{
		"info":      service.NewInfoStatusPage(),
		"config":    service.NewConfigStatusPage(cfg),
		"log/level": service.NewLogLevelStatusPage(),
		"signer":    signerStatusPage(signer),
	}
	if topo != nil {
		statusPages["topology"] = service.NewTopologyStatusPage(topo)
	}
	if ca != (renewal.ChainBuilder{}) {
		statusPages["ca"] = caStatusPage(ca)
	}
	if err := statusPages.Register(http.DefaultServeMux, elemId); err != nil {
		return serrors.WrapStr("registering status pages", err)
	}
	return nil
}

func signerStatusPage(signer cstrust.RenewingSigner) service.StatusPage {
	handler := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		s, err := signer.SignerGen.Generate(r.Context())
		if err != nil {
			http.Error(w, "Unable to get signer", http.StatusInternalServerError)
			return
		}

		type Subject struct {
			IA addr.IA `json:"isd_as"`
		}
		type TRCID struct {
			ISD    addr.ISD        `json:"isd"`
			Base   scrypto.Version `json:"base_number"`
			Serial scrypto.Version `json:"serial_number"`
		}
		type Validity struct {
			NotBefore time.Time `json:"not_before"`
			NotAfter  time.Time `json:"not_after"`
		}
		rep := struct {
			Subject       Subject   `json:"subject"`
			SubjectKeyID  string    `json:"subject_key_id"`
			Expiration    time.Time `json:"expiration"`
			TRCID         TRCID     `json:"trc_id"`
			ChainValidity Validity  `json:"chain_validity"`
			InGrace       bool      `json:"in_grace_period"`
		}{
			Subject:      Subject{IA: s.IA},
			SubjectKeyID: fmt.Sprintf("% X", s.SubjectKeyID),
			Expiration:   s.Expiration,
			TRCID: TRCID{
				ISD:    s.TRCID.ISD,
				Base:   s.TRCID.Base,
				Serial: s.TRCID.Serial,
			},
			ChainValidity: Validity{
				NotBefore: s.ChainValidity.NotBefore,
				NotAfter:  s.ChainValidity.NotAfter,
			},
			InGrace: s.InGrace,
		}
		enc := json.NewEncoder(w)
		enc.SetIndent("", "    ")
		if err := enc.Encode(rep); err != nil {
			http.Error(w, "Unable to marshal response", http.StatusInternalServerError)
			return
		}
	}
	return service.StatusPage{
		Info:    "SCION signer info",
		Handler: handler,
	}
}

func caStatusPage(signer renewal.ChainBuilder) service.StatusPage {
	handler := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		s, err := signer.PolicyGen.Generate(r.Context())
		if err != nil {
			http.Error(w, "No active signer", http.StatusInternalServerError)
			return
		}

		ia, err := cppki.ExtractIA(s.Certificate.Subject)
		if err != nil {
			http.Error(w, "Unable to get extract ISD-AS", http.StatusInternalServerError)
			return
		}

		type Subject struct {
			IA addr.IA `json:"isd_as"`
		}
		type Validity struct {
			NotBefore time.Time `json:"not_before"`
			NotAfter  time.Time `json:"not_after"`
		}
		type Policy struct {
			ChainLifetime string `json:"chain_lifetime"`
		}
		rep := struct {
			Subject      Subject  `json:"subject"`
			SubjectKeyID string   `json:"subject_key_id"`
			Policy       Policy   `json:"policy"`
			CertValidity Validity `json:"cert_validity"`
		}{
			Subject:      Subject{IA: ia},
			SubjectKeyID: fmt.Sprintf("% X", s.Certificate.SubjectKeyId),
			Policy: Policy{
				ChainLifetime: s.Validity.String(),
			},
			CertValidity: Validity{
				NotBefore: s.Certificate.NotBefore,
				NotAfter:  s.Certificate.NotAfter,
			},
		}
		enc := json.NewEncoder(w)
		enc.SetIndent("", "    ")
		if err := enc.Encode(rep); err != nil {
			http.Error(w, "Unable to marshal response", http.StatusInternalServerError)
			return
		}
	}
	return service.StatusPage{
		Info:    "CA status",
		Handler: handler,
	}
}

func loaderMetrics() topology.LoaderMetrics {
	updates := prom.NewCounterVec("", "",
		"topology_updates_total",
		"The total number of updates.",
		[]string{prom.LabelResult},
	)
	return topology.LoaderMetrics{
		ValidationErrors: metrics.NewPromCounter(updates).With(prom.LabelResult, "err_validate"),
		ReadErrors:       metrics.NewPromCounter(updates).With(prom.LabelResult, "err_read"),
		LastUpdate: metrics.NewPromGauge(
			prom.NewGaugeVec("", "",
				"topology_last_update_time",
				"Timestamp of the last successful update.",
				[]string{},
			),
		),
		Updates: metrics.NewPromCounter(updates).With(prom.LabelResult, prom.Success),
	}
}
<|endoftext|>


<commit_before>package gobusterdir

import (
	"bytes"
	"fmt"
	"log"

	"github.com/OJ/gobuster/libgobuster"
	"github.com/google/uuid"
)

// GobusterDir is the main type to implement the interface
type GobusterDir struct{}

// Setup is the setup implementation of gobusterdir
func (d GobusterDir) Setup(g *libgobuster.Gobuster) error {
	_, _, err := g.GetRequest(g.Opts.URL)
	if err != nil {
		return fmt.Errorf("unable to connect to %s: %v", g.Opts.URL, err)
	}

	guid := uuid.New()
	url := fmt.Sprintf("%s%s", g.Opts.URL, guid)
	wildcardResp, _, err := g.GetRequest(url)

	if err != nil {
		return err
	}

	if g.Opts.StatusCodesParsed.Contains(*wildcardResp) {
		g.IsWildcard = true
		log.Printf("[-] Wildcard response found: %s => %d", url, *wildcardResp)
		if !g.Opts.WildcardForced {
			return fmt.Errorf("To force processing of Wildcard responses, specify the '-fw' switch.")
		}
	}

	return nil
}

// Process is the process implementation of gobusterdir
func (d GobusterDir) Process(g *libgobuster.Gobuster, word string) ([]libgobuster.Result, error) {
	suffix := ""
	if g.Opts.UseSlash {
		suffix = "/"
	}

	// Try the DIR first
	url := fmt.Sprintf("%s%s%s", g.Opts.URL, word, suffix)
	dirResp, dirSize, err := g.GetRequest(url)
	if err != nil {
		return nil, err
	}
	var ret []libgobuster.Result
	if dirResp != nil {
		ret = append(ret, libgobuster.Result{
			Entity: fmt.Sprintf("%s%s", word, suffix),
			Status: *dirResp,
			Size:   dirSize,
		})
	}

	// Follow up with files using each ext.
	for ext := range g.Opts.ExtensionsParsed.Set {
		file := fmt.Sprintf("%s.%s", word, ext)
		url = fmt.Sprintf("%s%s", g.Opts.URL, file)
		fileResp, fileSize, err := g.GetRequest(url)
		if err != nil {
			return nil, err
		}

		if fileResp != nil {
			ret = append(ret, libgobuster.Result{
				Entity: file,
				Status: *fileResp,
				Size:   fileSize,
			})
		}
	}
	return ret, nil
}

// ResultToString is the to string implementation of gobusterdir
func (d GobusterDir) ResultToString(g *libgobuster.Gobuster, r *libgobuster.Result) (*string, error) {
	buf := &bytes.Buffer{}

	// Prefix if we're in verbose mode
	if g.Opts.Verbose {
		if g.Opts.StatusCodesParsed.Contains(r.Status) {
			if _, err := fmt.Fprintf(buf, "Found: "); err != nil {
				return nil, err
			}
		} else {
			if _, err := fmt.Fprintf(buf, "Missed: "); err != nil {
				return nil, err
			}
		}
	}

	if g.Opts.StatusCodesParsed.Contains(r.Status) || g.Opts.Verbose {
		if g.Opts.Expanded {
			if _, err := fmt.Fprintf(buf, "%s", g.Opts.URL); err != nil {
				return nil, err
			}
		} else {
			if _, err := fmt.Fprintf(buf, "/"); err != nil {
				return nil, err
			}
		}
		if _, err := fmt.Fprintf(buf, "%s", r.Entity); err != nil {
			return nil, err
		}

		if !g.Opts.NoStatus {
			if _, err := fmt.Fprintf(buf, " (Status: %d)", r.Status); err != nil {
				return nil, err
			}
		}

		if r.Size != nil {
			if _, err := fmt.Fprintf(buf, " [Size: %d]", *r.Size); err != nil {
				return nil, err
			}
		}
		if _, err := fmt.Fprintf(buf, "\n"); err != nil {
			return nil, err
		}
	}
	s := buf.String()
	return &s, nil
}
<commit_msg>Revert "Merge #99, fix classic format string vulnerability"<commit_after>package gobusterdir

import (
	"bytes"
	"fmt"
	"log"

	"github.com/OJ/gobuster/libgobuster"
	"github.com/google/uuid"
)

// GobusterDir is the main type to implement the interface
type GobusterDir struct{}

// Setup is the setup implementation of gobusterdir
func (d GobusterDir) Setup(g *libgobuster.Gobuster) error {
	_, _, err := g.GetRequest(g.Opts.URL)
	if err != nil {
		return fmt.Errorf("unable to connect to %s: %v", g.Opts.URL, err)
	}

	guid := uuid.New()
	url := fmt.Sprintf("%s%s", g.Opts.URL, guid)
	wildcardResp, _, err := g.GetRequest(url)

	if err != nil {
		return err
	}

	if g.Opts.StatusCodesParsed.Contains(*wildcardResp) {
		g.IsWildcard = true
		log.Printf("[-] Wildcard response found: %s => %d", url, *wildcardResp)
		if !g.Opts.WildcardForced {
			return fmt.Errorf("To force processing of Wildcard responses, specify the '-fw' switch.")
		}
	}

	return nil
}

// Process is the process implementation of gobusterdir
func (d GobusterDir) Process(g *libgobuster.Gobuster, word string) ([]libgobuster.Result, error) {
	suffix := ""
	if g.Opts.UseSlash {
		suffix = "/"
	}

	// Try the DIR first
	url := fmt.Sprintf("%s%s%s", g.Opts.URL, word, suffix)
	dirResp, dirSize, err := g.GetRequest(url)
	if err != nil {
		return nil, err
	}
	var ret []libgobuster.Result
	if dirResp != nil {
		ret = append(ret, libgobuster.Result{
			Entity: fmt.Sprintf("%s%s", word, suffix),
			Status: *dirResp,
			Size:   dirSize,
		})
	}

	// Follow up with files using each ext.
	for ext := range g.Opts.ExtensionsParsed.Set {
		file := fmt.Sprintf("%s.%s", word, ext)
		url = fmt.Sprintf("%s%s", g.Opts.URL, file)
		fileResp, fileSize, err := g.GetRequest(url)
		if err != nil {
			return nil, err
		}

		if fileResp != nil {
			ret = append(ret, libgobuster.Result{
				Entity: file,
				Status: *fileResp,
				Size:   fileSize,
			})
		}
	}
	return ret, nil
}

// ResultToString is the to string implementation of gobusterdir
func (d GobusterDir) ResultToString(g *libgobuster.Gobuster, r *libgobuster.Result) (*string, error) {
	buf := &bytes.Buffer{}

	// Prefix if we're in verbose mode
	if g.Opts.Verbose {
		if g.Opts.StatusCodesParsed.Contains(r.Status) {
			if _, err := fmt.Fprintf(buf, "Found: "); err != nil {
				return nil, err
			}
		} else {
			if _, err := fmt.Fprintf(buf, "Missed: "); err != nil {
				return nil, err
			}
		}
	}

	if g.Opts.StatusCodesParsed.Contains(r.Status) || g.Opts.Verbose {
		if g.Opts.Expanded {
			if _, err := fmt.Fprintf(buf, g.Opts.URL); err != nil {
				return nil, err
			}
		} else {
			if _, err := fmt.Fprintf(buf, "/"); err != nil {
				return nil, err
			}
		}
		if _, err := fmt.Fprintf(buf, r.Entity); err != nil {
			return nil, err
		}

		if !g.Opts.NoStatus {
			if _, err := fmt.Fprintf(buf, " (Status: %d)", r.Status); err != nil {
				return nil, err
			}
		}

		if r.Size != nil {
			if _, err := fmt.Fprintf(buf, " [Size: %d]", *r.Size); err != nil {
				return nil, err
			}
		}
		if _, err := fmt.Fprintf(buf, "\n"); err != nil {
			return nil, err
		}
	}
	s := buf.String()
	return &s, nil
}
<|endoftext|>


<commit_before><commit_msg>Ref #9 Use fmt.Print+os.Exit to prevent looking like we crashed<commit_after><|endoftext|>


<commit_before>package main

import "fmt"

func hammingDistance(x int, y int) int {
	var d int
	xor := x ^ y
	for xor != 0 {
		d += xor & 1
		xor /= 2
	}
	return d
}

func main() {
	var x, y = 1, 4

	fmt.Printf("Input: x = %d, y = %d\n", x, y)
	fmt.Printf("Output: %d\n", hammingDistance(x, y))
}
<commit_msg>refactoring code<commit_after>package main

import "fmt"

func hammingDistance(x int, y int) int {
	d := 0
	xor := x ^ y
	for xor != 0 {
		d += xor & 1
		xor /= 2
	}
	return d
}

func main() {
	var x, y = 1, 4

	fmt.Printf("Input: x = %d, y = %d\n", x, y)
	fmt.Printf("Output: %d\n", hammingDistance(x, y))
}
<|endoftext|>


<commit_before>// Copyright 2018 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package ir

import (
	"fmt"
	"io"
	"strings"
)

// Pretty writes a human-readable representation of an IR object to w.
func Pretty(w io.Writer, x interface{}) {

	pp := &prettyPrinter{
		depth: -1,
		w:     w,
	}
	Walk(pp, x)
}

type prettyPrinter struct {
	depth int
	w     io.Writer
}

func (pp *prettyPrinter) Before(x interface{}) {
	pp.depth++
}

func (pp *prettyPrinter) After(x interface{}) {
	pp.depth--
}

func (pp *prettyPrinter) Visit(x interface{}) (Visitor, error) {
	pp.writeIndent("%T %v", x, x)
	return pp, nil
}

func (pp *prettyPrinter) writeIndent(f string, a ...interface{}) {
	pad := strings.Repeat(" ", pp.depth)
	fmt.Fprintf(pp.w, pad+f+"\n", a...)
}
<commit_msg>ir: Use %+v when pretty printing IR<commit_after>// Copyright 2018 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package ir

import (
	"fmt"
	"io"
	"strings"
)

// Pretty writes a human-readable representation of an IR object to w.
func Pretty(w io.Writer, x interface{}) {

	pp := &prettyPrinter{
		depth: -1,
		w:     w,
	}
	Walk(pp, x)
}

type prettyPrinter struct {
	depth int
	w     io.Writer
}

func (pp *prettyPrinter) Before(x interface{}) {
	pp.depth++
}

func (pp *prettyPrinter) After(x interface{}) {
	pp.depth--
}

func (pp *prettyPrinter) Visit(x interface{}) (Visitor, error) {
	pp.writeIndent("%T %+v", x, x)
	return pp, nil
}

func (pp *prettyPrinter) writeIndent(f string, a ...interface{}) {
	pad := strings.Repeat(" ", pp.depth)
	fmt.Fprintf(pp.w, pad+f+"\n", a...)
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.

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

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

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

package esxcli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
)

type esxcli struct {
	*flags.HostSystemFlag

	hints bool
}

func init() {
	cli.Register("host.esxcli", &esxcli{})
}

func (cmd *esxcli) Usage() string {
	return "COMMAND [ARG]..."
}

func (cmd *esxcli) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
	cmd.HostSystemFlag.Register(ctx, f)

	f.BoolVar(&cmd.hints, "hints", true, "Use command info hints when formatting output")
}

func (cmd *esxcli) Description() string {
	return `Invoke esxcli command on HOST.

Output is rendered in table form when possible, unless disabled with '-hints=false'.

Examples:
  govc host.esxcli network ip connection list
  govc host.esxcli system settings advanced set -o /Net/GuestIPHack -i 1
  govc host.esxcli network firewall ruleset set -r remoteSerialPort -e true
  govc host.esxcli network firewall set -e false`
}

func (cmd *esxcli) Process(ctx context.Context) error {
	if err := cmd.HostSystemFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *esxcli) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return nil
	}

	host, err := cmd.HostSystem()
	if err != nil {
		return err
	}

	e, err := NewExecutor(c, host)
	if err != nil {
		return err
	}

	res, err := e.Run(f.Args())
	if err != nil {
		return err
	}

	if len(res.Values) == 0 {
		return nil
	}

	return cmd.WriteResult(&result{res, cmd})
}

type result struct {
	*Response
	cmd *esxcli
}

func (r *result) Write(w io.Writer) error {
	var formatType string
	if r.cmd.hints {
		formatType = r.Info.Hints.Formatter()
	}

	switch formatType {
	case "table":
		r.cmd.formatTable(w, r.Response)
	default:
		r.cmd.formatSimple(w, r.Response)
	}

	return nil
}

func (cmd *esxcli) formatSimple(w io.Writer, res *Response) {
	var keys []string
	for key := range res.Values[0] {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)

	for i, rv := range res.Values {
		if i > 0 {
			fmt.Fprintln(tw)
			_ = tw.Flush()
		}
		for _, key := range keys {
			fmt.Fprintf(tw, "%s:\t%s\n", key, strings.Join(rv[key], ", "))
		}
	}

	_ = tw.Flush()
}

func (cmd *esxcli) formatTable(w io.Writer, res *Response) {
	fields := res.Info.Hints.Fields()

	tw := tabwriter.NewWriter(w, len(fields), 0, 2, ' ', 0)

	var hr []string
	for _, name := range fields {
		hr = append(hr, strings.Repeat("-", len(name)))
	}

	fmt.Fprintln(tw, strings.Join(fields, "\t"))
	fmt.Fprintln(tw, strings.Join(hr, "\t"))

	for _, vals := range res.Values {
		var row []string

		for _, name := range fields {
			key := strings.Replace(name, " ", "", -1)
			if val, ok := vals[key]; ok {
				row = append(row, strings.Join(val, ", "))
			} else {
				row = append(row, "")
			}
		}

		fmt.Fprintln(tw, strings.Join(row, "\t"))
	}

	_ = tw.Flush()
}
<commit_msg>govc: fix host.esxcli error handling<commit_after>/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.

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

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

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

package esxcli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
)

type esxcli struct {
	*flags.HostSystemFlag

	hints bool
}

func init() {
	cli.Register("host.esxcli", &esxcli{})
}

func (cmd *esxcli) Usage() string {
	return "COMMAND [ARG]..."
}

func (cmd *esxcli) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
	cmd.HostSystemFlag.Register(ctx, f)

	f.BoolVar(&cmd.hints, "hints", true, "Use command info hints when formatting output")
}

func (cmd *esxcli) Description() string {
	return `Invoke esxcli command on HOST.

Output is rendered in table form when possible, unless disabled with '-hints=false'.

Examples:
  govc host.esxcli network ip connection list
  govc host.esxcli system settings advanced set -o /Net/GuestIPHack -i 1
  govc host.esxcli network firewall ruleset set -r remoteSerialPort -e true
  govc host.esxcli network firewall set -e false`
}

func (cmd *esxcli) Process(ctx context.Context) error {
	if err := cmd.HostSystemFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *esxcli) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return err
	}

	host, err := cmd.HostSystem()
	if err != nil {
		return err
	}

	e, err := NewExecutor(c, host)
	if err != nil {
		return err
	}

	res, err := e.Run(f.Args())
	if err != nil {
		return err
	}

	if len(res.Values) == 0 {
		return nil
	}

	return cmd.WriteResult(&result{res, cmd})
}

type result struct {
	*Response
	cmd *esxcli
}

func (r *result) Write(w io.Writer) error {
	var formatType string
	if r.cmd.hints {
		formatType = r.Info.Hints.Formatter()
	}

	switch formatType {
	case "table":
		r.cmd.formatTable(w, r.Response)
	default:
		r.cmd.formatSimple(w, r.Response)
	}

	return nil
}

func (cmd *esxcli) formatSimple(w io.Writer, res *Response) {
	var keys []string
	for key := range res.Values[0] {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)

	for i, rv := range res.Values {
		if i > 0 {
			fmt.Fprintln(tw)
			_ = tw.Flush()
		}
		for _, key := range keys {
			fmt.Fprintf(tw, "%s:\t%s\n", key, strings.Join(rv[key], ", "))
		}
	}

	_ = tw.Flush()
}

func (cmd *esxcli) formatTable(w io.Writer, res *Response) {
	fields := res.Info.Hints.Fields()

	tw := tabwriter.NewWriter(w, len(fields), 0, 2, ' ', 0)

	var hr []string
	for _, name := range fields {
		hr = append(hr, strings.Repeat("-", len(name)))
	}

	fmt.Fprintln(tw, strings.Join(fields, "\t"))
	fmt.Fprintln(tw, strings.Join(hr, "\t"))

	for _, vals := range res.Values {
		var row []string

		for _, name := range fields {
			key := strings.Replace(name, " ", "", -1)
			if val, ok := vals[key]; ok {
				row = append(row, strings.Join(val, ", "))
			} else {
				row = append(row, "")
			}
		}

		fmt.Fprintln(tw, strings.Join(row, "\t"))
	}

	_ = tw.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

func maybeFatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func setupDb(u string) {
	req, err := http.NewRequest("PUT", u, nil)
	maybeFatal(err)
	res, err := http.DefaultClient.Do(req)
	maybeFatal(err)
	res.Body.Close()
}

func sendOne(u, k string, body []byte) {
	resp, err := http.DefaultClient.Post(u+"?ts="+k,
		"application/json", bytes.NewReader(body))
	maybeFatal(err)
	defer resp.Body.Close()
	if resp.StatusCode >= 300 || resp.StatusCode < 200 {
		log.Fatalf("HTTP Error on %v: %v", k, err)
	}
}

func main() {
	u := os.Args[1]
	setupDb(u)

	t := time.Tick(5 * time.Second)
	i := 0

	d := json.NewDecoder(os.Stdin)
	for {
		kv := map[string]*json.RawMessage{}

		err := d.Decode(&kv)
		if err == io.EOF {
			log.Printf("Done!")
			break
		}
		maybeFatal(err)

		for k, v := range kv {
			body := []byte(*v)
			sendOne(u, k, body)
		}

		i++
		select {
		case <-t:
			var k string
			for k = range kv {
			}
			log.Printf("Processed %v items, latest was %v", i, k)
		default:
		}
	}
}
<commit_msg>Only loop once per batch while loading<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

func maybeFatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func setupDb(u string) {
	req, err := http.NewRequest("PUT", u, nil)
	maybeFatal(err)
	res, err := http.DefaultClient.Do(req)
	maybeFatal(err)
	res.Body.Close()
}

func sendOne(u, k string, body []byte) {
	resp, err := http.DefaultClient.Post(u+"?ts="+k,
		"application/json", bytes.NewReader(body))
	maybeFatal(err)
	defer resp.Body.Close()
	if resp.StatusCode >= 300 || resp.StatusCode < 200 {
		log.Fatalf("HTTP Error on %v: %v", k, err)
	}
}

func main() {
	u := os.Args[1]
	setupDb(u)

	t := time.Tick(5 * time.Second)
	i := 0

	d := json.NewDecoder(os.Stdin)
	for {
		kv := map[string]*json.RawMessage{}

		err := d.Decode(&kv)
		if err == io.EOF {
			log.Printf("Done!")
			break
		}
		maybeFatal(err)

		var latestKey string
		for k, v := range kv {
			body := []byte(*v)
			sendOne(u, k, body)
			latestKey = k
		}

		i++
		select {
		case <-t:
			log.Printf("Processed %v items, latest was %v", i, latestKey)
		default:
		}
	}
}
<|endoftext|>


<commit_before>/*
kasper is a lightweight Kafka stream processing library.
 */
package kasper

import (
	"log"
	"github.com/Shopify/sarama"
	"time"
)

type TopicProcessor struct {
	config              *TopicProcessorConfig
	containerId         ContainerId
	client              sarama.Client
	offsetManager       sarama.OffsetManager
	partitionProcessors []*partitionProcessor
	inputTopics         []Topic
	partitions          []Partition
}

func partitionsOfTopics(topics []string, client sarama.Client) []int32 {
	partitionsSet := make(map[int32]struct{})
	for _, topic := range topics {
		partitions, err := client.Partitions(topic)
		if err != nil {
			log.Fatal(err)
		}
		for _, partition := range partitions {
			partitionsSet[partition] = struct{}{}
		}
	}
	i := 0
	partitions := make([]int32, len(partitionsSet))
	for partition := range partitionsSet {
		partitions[i] = partition
		i++
	}
	return partitions
}

// NewTopicProcessor creates a new TopicProcessor with the given config.
// It requires a factory function that creates MessageProcessor instances and a container id.
// The container id must be a number between 0 and config.ContainerCount - 1.
func NewTopicProcessor(config *TopicProcessorConfig, makeProcessor func() MessageProcessor, cid ContainerId) *TopicProcessor {
	// TODO: check all input topics are covered by a Serde
	// TODO: check all input partitions and make sure PartitionAssignment is valid
	// TODO: check cid is within [0, ContainerCount)
	inputTopics := config.InputTopics
	brokerList := config.BrokerList
	client, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		log.Fatal(err)
	}
	partitions := config.partitionsForContainer(cid)
	offsetManager, err := sarama.NewOffsetManagerFromClient(config.kafkaConsumerGroup(), client)
	if err != nil {
		log.Fatal(err)
	}
	partitionProcessors := make([]*partitionProcessor, len(partitions))
	topicProcessor := TopicProcessor{
		config,
		cid,
		client,
		offsetManager,
		partitionProcessors,
		inputTopics,
		partitions,
	}
	for i, partition := range partitions {
		processor := makeProcessor()
		partitionProcessors[i] = newPartitionProcessor(&topicProcessor, processor, partition)
	}
	return &topicProcessor
}

func (tp *TopicProcessor) Run() {
	consumerMessagesChan := tp.getConsumerMessageChan()
	producerSuccessesChan := tp.getProducerMessagesChan()
	producerErrorsChan := tp.getProducerErrorsChan()
	/* TODO: call Stop() on this ticker when implementing proper shutdown */
	markOffsetsTicker := time.NewTicker(tp.config.AutoMarkOffsetsInterval) /* TODO: handle AutoMarkOffsetsInterval <= 0 */
	for {
		select {
		case consumerMessage := <-consumerMessagesChan:
			pp := tp.partitionProcessors[consumerMessage.Partition]
			if pp.isReadyForMessage(consumerMessage) {
				pp.processConsumerMessage(consumerMessage)
			} else {
				checkReadinessTicker := time.NewTicker(50 * time.Millisecond) // TODO: make this configurable
				for {
					select {
					case <-checkReadinessTicker.C:
						pp := tp.partitionProcessors[consumerMessage.Partition]
						if pp.isReadyForMessage(consumerMessage) {
							pp.processConsumerMessage(consumerMessage)
							break
						}
					case msg := <-producerSuccessesChan:
						tp.processProducerMessageSuccess(msg)
					case err := <-producerErrorsChan:
						tp.processProducerError(err)
					case <-markOffsetsTicker.C:
						tp.processMarkOffsetsTick()
					}
				}
				checkReadinessTicker.Stop()
			}
		case msg := <-producerSuccessesChan:
			tp.processProducerMessageSuccess(msg)
		case err := <-producerErrorsChan:
			tp.processProducerError(err)
		case <-markOffsetsTicker.C:
			tp.processMarkOffsetsTick()
		}
	}
}

func (tp *TopicProcessor) getProducerErrorsChan() chan *sarama.ProducerError {
	producerErrorsChan := make(chan *sarama.ProducerError)
	for _, ch := range tp.producerErrorsChannels() {
		go func(c <-chan *sarama.ProducerError) {
			for msg := range c {
				producerErrorsChan <- msg
			}
		}(ch)
	}
	return producerErrorsChan
}

func (tp *TopicProcessor) getProducerMessagesChan() chan *sarama.ProducerMessage {
	producerSuccessesChan := make(chan *sarama.ProducerMessage)
	for _, ch := range tp.producerSuccessesChannels() {
		go func(c <-chan *sarama.ProducerMessage) {
			for msg := range c {
				producerSuccessesChan <- msg
			}
		}(ch)
	}
	return producerSuccessesChan
}

func (tp *TopicProcessor) getConsumerMessageChan() chan *sarama.ConsumerMessage {
	consumerMessagesChan := make(chan *sarama.ConsumerMessage)
	for _, ch := range tp.consumerMessageChannels() {
		go func(c <-chan *sarama.ConsumerMessage) {
			for msg := range c {
				consumerMessagesChan <- msg
			}
		}(ch)
	}
	return consumerMessagesChan
}

func (tp *TopicProcessor) processProducerError(error *sarama.ProducerError) {
	log.Fatal(error) /* FIXME Handle this gracefully with a retry count / backoff period */
}

func (tp *TopicProcessor) processMarkOffsetsTick() {
	for _, pp := range tp.partitionProcessors {
		pp.markOffsets()
	}
}

func (tp *TopicProcessor) processProducerMessageSuccess(producerMessage *sarama.ProducerMessage) {
	pp := tp.partitionProcessors[producerMessage.Partition]
	pp.processProducerMessageSuccess(producerMessage)
}

func (tp *TopicProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	var chans []<-chan *sarama.ConsumerMessage
	for _, partitionProcessor := range tp.partitionProcessors {
		partitionChannels := partitionProcessor.consumerMessageChannels()
		for _, ch := range partitionChannels {
			chans = append(chans, ch)
		}
	}
	return chans
}

func (tp *TopicProcessor) producerSuccessesChannels() []<-chan *sarama.ProducerMessage {
	var chans []<-chan *sarama.ProducerMessage
	for _, partitionProcessor := range tp.partitionProcessors {
		ch := partitionProcessor.producer.Successes()
		chans = append(chans, ch)
	}
	return chans
}

func (tp *TopicProcessor) producerErrorsChannels() []<-chan *sarama.ProducerError {
	var chans []<-chan *sarama.ProducerError
	for _, partitionProcessor := range tp.partitionProcessors {
		ch := partitionProcessor.producer.Errors()
		chans = append(chans, ch)
	}
	return chans
}
<commit_msg>Rename function<commit_after>/*
kasper is a lightweight Kafka stream processing library.
 */
package kasper

import (
	"log"
	"github.com/Shopify/sarama"
	"time"
)

type TopicProcessor struct {
	config              *TopicProcessorConfig
	containerId         ContainerId
	client              sarama.Client
	offsetManager       sarama.OffsetManager
	partitionProcessors []*partitionProcessor
	inputTopics         []Topic
	partitions          []Partition
}

func partitionsOfTopics(topics []string, client sarama.Client) []int32 {
	partitionsSet := make(map[int32]struct{})
	for _, topic := range topics {
		partitions, err := client.Partitions(topic)
		if err != nil {
			log.Fatal(err)
		}
		for _, partition := range partitions {
			partitionsSet[partition] = struct{}{}
		}
	}
	i := 0
	partitions := make([]int32, len(partitionsSet))
	for partition := range partitionsSet {
		partitions[i] = partition
		i++
	}
	return partitions
}

// NewTopicProcessor creates a new TopicProcessor with the given config.
// It requires a factory function that creates MessageProcessor instances and a container id.
// The container id must be a number between 0 and config.ContainerCount - 1.
func NewTopicProcessor(config *TopicProcessorConfig, makeProcessor func() MessageProcessor, cid ContainerId) *TopicProcessor {
	// TODO: check all input topics are covered by a Serde
	// TODO: check all input partitions and make sure PartitionAssignment is valid
	// TODO: check cid is within [0, ContainerCount)
	inputTopics := config.InputTopics
	brokerList := config.BrokerList
	client, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		log.Fatal(err)
	}
	partitions := config.partitionsForContainer(cid)
	offsetManager, err := sarama.NewOffsetManagerFromClient(config.kafkaConsumerGroup(), client)
	if err != nil {
		log.Fatal(err)
	}
	partitionProcessors := make([]*partitionProcessor, len(partitions))
	topicProcessor := TopicProcessor{
		config,
		cid,
		client,
		offsetManager,
		partitionProcessors,
		inputTopics,
		partitions,
	}
	for i, partition := range partitions {
		processor := makeProcessor()
		partitionProcessors[i] = newPartitionProcessor(&topicProcessor, processor, partition)
	}
	return &topicProcessor
}

func (tp *TopicProcessor) Run() {
	consumerMessagesChan := tp.getConsumerMessagesChan()
	producerSuccessesChan := tp.getProducerMessagesChan()
	producerErrorsChan := tp.getProducerErrorsChan()
	/* TODO: call Stop() on this ticker when implementing proper shutdown */
	markOffsetsTicker := time.NewTicker(tp.config.AutoMarkOffsetsInterval) /* TODO: handle AutoMarkOffsetsInterval <= 0 */
	for {
		select {
		case consumerMessage := <-consumerMessagesChan:
			pp := tp.partitionProcessors[consumerMessage.Partition]
			if pp.isReadyForMessage(consumerMessage) {
				pp.processConsumerMessage(consumerMessage)
			} else {
				checkReadinessTicker := time.NewTicker(50 * time.Millisecond) // TODO: make this configurable
				for {
					select {
					case <-checkReadinessTicker.C:
						pp := tp.partitionProcessors[consumerMessage.Partition]
						if pp.isReadyForMessage(consumerMessage) {
							pp.processConsumerMessage(consumerMessage)
							break
						}
					case msg := <-producerSuccessesChan:
						tp.processProducerMessageSuccess(msg)
					case err := <-producerErrorsChan:
						tp.processProducerError(err)
					case <-markOffsetsTicker.C:
						tp.processMarkOffsetsTick()
					}
				}
				checkReadinessTicker.Stop()
			}
		case msg := <-producerSuccessesChan:
			tp.processProducerMessageSuccess(msg)
		case err := <-producerErrorsChan:
			tp.processProducerError(err)
		case <-markOffsetsTicker.C:
			tp.processMarkOffsetsTick()
		}
	}
}

func (tp *TopicProcessor) getProducerErrorsChan() chan *sarama.ProducerError {
	producerErrorsChan := make(chan *sarama.ProducerError)
	for _, ch := range tp.producerErrorsChannels() {
		go func(c <-chan *sarama.ProducerError) {
			for msg := range c {
				producerErrorsChan <- msg
			}
		}(ch)
	}
	return producerErrorsChan
}

func (tp *TopicProcessor) getProducerMessagesChan() chan *sarama.ProducerMessage {
	producerSuccessesChan := make(chan *sarama.ProducerMessage)
	for _, ch := range tp.producerSuccessesChannels() {
		go func(c <-chan *sarama.ProducerMessage) {
			for msg := range c {
				producerSuccessesChan <- msg
			}
		}(ch)
	}
	return producerSuccessesChan
}

func (tp *TopicProcessor) getConsumerMessagesChan() chan *sarama.ConsumerMessage {
	consumerMessagesChan := make(chan *sarama.ConsumerMessage)
	for _, ch := range tp.consumerMessageChannels() {
		go func(c <-chan *sarama.ConsumerMessage) {
			for msg := range c {
				consumerMessagesChan <- msg
			}
		}(ch)
	}
	return consumerMessagesChan
}

func (tp *TopicProcessor) processProducerError(error *sarama.ProducerError) {
	log.Fatal(error) /* FIXME Handle this gracefully with a retry count / backoff period */
}

func (tp *TopicProcessor) processMarkOffsetsTick() {
	for _, pp := range tp.partitionProcessors {
		pp.markOffsets()
	}
}

func (tp *TopicProcessor) processProducerMessageSuccess(producerMessage *sarama.ProducerMessage) {
	pp := tp.partitionProcessors[producerMessage.Partition]
	pp.processProducerMessageSuccess(producerMessage)
}

func (tp *TopicProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	var chans []<-chan *sarama.ConsumerMessage
	for _, partitionProcessor := range tp.partitionProcessors {
		partitionChannels := partitionProcessor.consumerMessageChannels()
		for _, ch := range partitionChannels {
			chans = append(chans, ch)
		}
	}
	return chans
}

func (tp *TopicProcessor) producerSuccessesChannels() []<-chan *sarama.ProducerMessage {
	var chans []<-chan *sarama.ProducerMessage
	for _, partitionProcessor := range tp.partitionProcessors {
		ch := partitionProcessor.producer.Successes()
		chans = append(chans, ch)
	}
	return chans
}

func (tp *TopicProcessor) producerErrorsChannels() []<-chan *sarama.ProducerError {
	var chans []<-chan *sarama.ProducerError
	for _, partitionProcessor := range tp.partitionProcessors {
		ch := partitionProcessor.producer.Errors()
		chans = append(chans, ch)
	}
	return chans
}
<|endoftext|>


<commit_before>package iptables

import (
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/godbus/dbus"
)

// IPV defines the table string
type IPV string

const (
	// Iptables point ipv4 table
	Iptables IPV = "ipv4"
	// IP6Tables point to ipv6 table
	IP6Tables IPV = "ipv6"
	// Ebtables point to bridge table
	Ebtables IPV = "eb"
)
const (
	dbusInterface = "org.fedoraproject.FirewallD1"
	dbusPath      = "/org/fedoraproject/FirewallD1"
)

// Conn is a connection to firewalld dbus endpoint.
type Conn struct {
	sysconn *dbus.Conn
	sysobj  dbus.BusObject
	signal  chan *dbus.Signal
}

var (
	connection       *Conn
	firewalldRunning bool      // is Firewalld service running
	onReloaded       []*func() // callbacks when Firewalld has been reloaded
)

// FirewalldInit initializes firewalld management code.
func FirewalldInit() error {
	var err error

	if connection, err = newConnection(); err != nil {
		return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
	}
	firewalldRunning = checkRunning()
	if !firewalldRunning {
		connection.sysconn.Close()
		connection = nil
	}
	if connection != nil {
		go signalHandler()
	}

	return nil
}

// New() establishes a connection to the system bus.
func newConnection() (*Conn, error) {
	c := new(Conn)
	if err := c.initConnection(); err != nil {
		return nil, err
	}

	return c, nil
}

// Innitialize D-Bus connection.
func (c *Conn) initConnection() error {
	var err error

	c.sysconn, err = dbus.SystemBus()
	if err != nil {
		return err
	}

	// This never fails, even if the service is not running atm.
	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))

	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
		dbusPath, dbusInterface, dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
		dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	c.signal = make(chan *dbus.Signal, 10)
	c.sysconn.Signal(c.signal)

	return nil
}

func signalHandler() {
	for signal := range connection.signal {
		if strings.Contains(signal.Name, "NameOwnerChanged") {
			firewalldRunning = checkRunning()
			dbusConnectionChanged(signal.Body)
		} else if strings.Contains(signal.Name, "Reloaded") {
			reloaded()
		}
	}
}

func dbusConnectionChanged(args []interface{}) {
	name := args[0].(string)
	oldOwner := args[1].(string)
	newOwner := args[2].(string)

	if name != dbusInterface {
		return
	}

	if len(newOwner) > 0 {
		connectionEstablished()
	} else if len(oldOwner) > 0 {
		connectionLost()
	}
}

func connectionEstablished() {
	reloaded()
}

func connectionLost() {
	// Doesn't do anything for now. Libvirt also doesn't react to this.
}

// call all callbacks
func reloaded() {
	for _, pf := range onReloaded {
		(*pf)()
	}
}

// OnReloaded add callback
func OnReloaded(callback func()) {
	for _, pf := range onReloaded {
		if pf == &callback {
			return
		}
	}
	onReloaded = append(onReloaded, &callback)
}

// Call some remote method to see whether the service is actually running.
func checkRunning() bool {
	var zone string
	var err error

	if connection != nil {
		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
		logrus.Infof("Firewalld running: %t", err == nil)
		return err == nil
	}
	return false
}

// Passthrough method simply passes args through to iptables/ip6tables
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
	var output string
	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
	if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
		return nil, err
	}
	return []byte(output), nil
}
<commit_msg>Remove firewalld running log<commit_after>package iptables

import (
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/godbus/dbus"
)

// IPV defines the table string
type IPV string

const (
	// Iptables point ipv4 table
	Iptables IPV = "ipv4"
	// IP6Tables point to ipv6 table
	IP6Tables IPV = "ipv6"
	// Ebtables point to bridge table
	Ebtables IPV = "eb"
)
const (
	dbusInterface = "org.fedoraproject.FirewallD1"
	dbusPath      = "/org/fedoraproject/FirewallD1"
)

// Conn is a connection to firewalld dbus endpoint.
type Conn struct {
	sysconn *dbus.Conn
	sysobj  dbus.BusObject
	signal  chan *dbus.Signal
}

var (
	connection       *Conn
	firewalldRunning bool      // is Firewalld service running
	onReloaded       []*func() // callbacks when Firewalld has been reloaded
)

// FirewalldInit initializes firewalld management code.
func FirewalldInit() error {
	var err error

	if connection, err = newConnection(); err != nil {
		return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
	}
	firewalldRunning = checkRunning()
	if !firewalldRunning {
		connection.sysconn.Close()
		connection = nil
	}
	if connection != nil {
		go signalHandler()
	}

	return nil
}

// New() establishes a connection to the system bus.
func newConnection() (*Conn, error) {
	c := new(Conn)
	if err := c.initConnection(); err != nil {
		return nil, err
	}

	return c, nil
}

// Innitialize D-Bus connection.
func (c *Conn) initConnection() error {
	var err error

	c.sysconn, err = dbus.SystemBus()
	if err != nil {
		return err
	}

	// This never fails, even if the service is not running atm.
	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))

	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
		dbusPath, dbusInterface, dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
		dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	c.signal = make(chan *dbus.Signal, 10)
	c.sysconn.Signal(c.signal)

	return nil
}

func signalHandler() {
	for signal := range connection.signal {
		if strings.Contains(signal.Name, "NameOwnerChanged") {
			firewalldRunning = checkRunning()
			dbusConnectionChanged(signal.Body)
		} else if strings.Contains(signal.Name, "Reloaded") {
			reloaded()
		}
	}
}

func dbusConnectionChanged(args []interface{}) {
	name := args[0].(string)
	oldOwner := args[1].(string)
	newOwner := args[2].(string)

	if name != dbusInterface {
		return
	}

	if len(newOwner) > 0 {
		connectionEstablished()
	} else if len(oldOwner) > 0 {
		connectionLost()
	}
}

func connectionEstablished() {
	reloaded()
}

func connectionLost() {
	// Doesn't do anything for now. Libvirt also doesn't react to this.
}

// call all callbacks
func reloaded() {
	for _, pf := range onReloaded {
		(*pf)()
	}
}

// OnReloaded add callback
func OnReloaded(callback func()) {
	for _, pf := range onReloaded {
		if pf == &callback {
			return
		}
	}
	onReloaded = append(onReloaded, &callback)
}

// Call some remote method to see whether the service is actually running.
func checkRunning() bool {
	var zone string
	var err error

	if connection != nil {
		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
		return err == nil
	}
	return false
}

// Passthrough method simply passes args through to iptables/ip6tables
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
	var output string
	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
	if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
		return nil, err
	}
	return []byte(output), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Netflix, Inc.
//
// 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 tracker provides an entry point for instantiating Trackers
package tracker

import (
	"github.com/netflix/chaosmonkey"
	"github.com/netflix/chaosmonkey/config"
	"github.com/netflix/chaosmonkey/deps"
	"github.com/pkg/errors"
)

func init() {
	deps.GetTrackers = noSupportedTrackers
}

// No trackers have been implemented yet

// noSupportedTrackers will return an error unless cfg.Trackers() is empty
// It is a placeholder function until the open-source version implements other
// trackers
func noSupportedTrackers(cfg *config.Monkey) ([]chaosmonkey.Tracker, error) {
	kinds, err := cfg.Trackers()
	if err != nil {
		return nil, err
	}

	for _, tracker := range kinds {
		return nil, errors.Errorf("unsupported tracker: %s", tracker)
	}

	return nil, nil
}
<commit_msg>Add placeholder code for trackers<commit_after>// Copyright 2016 Netflix, Inc.
//
// 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 tracker provides an entry point for instantiating Trackers
package tracker

import (
	"github.com/netflix/chaosmonkey"
	"github.com/netflix/chaosmonkey/config"
	"github.com/netflix/chaosmonkey/deps"
	"github.com/pkg/errors"
)

func init() {
	deps.GetTrackers = getTrackers
}

// No trackers have been implemented yet

// getTrackers returns a list of trackers specified in the configuration
func getTrackers(cfg *config.Monkey) ([]chaosmonkey.Tracker, error) {
	var result []chaosmonkey.Tracker

	kinds, err := cfg.Trackers()
	if err != nil {
		return nil, err
	}

	for _, kind := range kinds {
		tr, err := getTracker(kind, cfg)
		if err != nil {
			return nil, err
		}
		result = append(result, tr)
	}
	return result, nil
}

// getTracker returns a tracker by name
func getTracker(kind string, cfg *config.Monkey) (chaosmonkey.Tracker, error) {
	switch kind {
	// Currently, no trackers have been implemented.
	// As trackers are contributed to the open source project, they should
	// be instantiated here
	default:
		return nil, errors.Errorf("unsupported tracker: %s", kind)
	}
}
<|endoftext|>


<commit_before>package glug

import (
	"net/http"
	"net/url"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	children map[string][]Plug
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// Init will initialize new router.
func Init() *Router {
	return &Router{children: make(map[string][]Plug)}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	router.children[path] = append(router.children[path], plugs...)
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/favicon.ico" {
		logRequest("GET", r.URL.Path)
	}

	r.ParseForm()

	conn := Conn{Writer: w, Request: r, Params: r.Form, halted: false}
	plugs := router.children[r.URL.Path]

	for _, plug := range plugs {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}
<commit_msg>Change router hash to tree<commit_after>package glug

import (
	"net/http"
	"net/url"
	"strings"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	node node
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

type node struct {
	path     string
	methods  map[string][]Plug
	children map[string]*node
}

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// Init will initialize new router.
func Init() *Router {
	return &Router{node: node{children: make(map[string]*node)}}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	var curr *node

	parts := strings.Split(path, "/")[1:]
	depth := len(parts)
	prev := &router.node

	for index, part := range parts {
		if child, ok := prev.children[part]; ok {
			curr = child
		} else {
			curr = &node{path: part, children: make(map[string]*node)}
		}

		if depth == index+1 {
			curr.methods = make(map[string][]Plug)
			curr.methods[method] = plugs
		}

		prev.children[part] = curr
		prev = curr
	}
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/favicon.ico" {
		logRequest(r.Method, r.URL.Path)
	}

	r.ParseForm()

	parts := strings.Split(r.URL.Path, "/")[1:]
	depth := len(parts)
	curr := &router.node

	for index, part := range parts {
		curr = curr.children[part]

		if depth == index+1 {
			break
		}
	}

	conn := Conn{Writer: w, Request: r, Params: r.Form, halted: false}

	for _, plug := range curr.methods[r.Method] {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}
<|endoftext|>