<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/zerok/statusd/Godeps/_workspace/src/gopkg.in/v2/yaml"
)

const (
	DEFAULT_TIMEOUT = 30
	DEFAULT_DELAY   = 30
	STATUS_OFFLINE  = "offline"
	STATUS_ONLINE   = "online"
)

type ServerConfiguration struct {
	IsAliveUrl string `yaml:"isAliveUrl"`
	Timeout    int    `yaml:"timeout"`
	Delay      int    `yaml:"delay"`
}

type HttpConfiguration struct {
	HostAddr string `yaml:"addr"`
}

type SlackConfiguration struct {
	Token            string              `yaml:"token"`
	Team             string              `yaml:"team"`
	NotifiedChannels map[string][]string `yaml:"channels"`
}

type Configuration struct {
	Servers map[string]ServerConfiguration `yaml:"servers"`
	Slack   SlackConfiguration             `yaml:"slack"`
	Http    HttpConfiguration              `yaml:"http"`
}

var statusRegistryManager StatusRegistryManager = NewStatusRegistryManager()

// NewConfiguration parses YAML data provided through a Reader
// into our configuration object. If any error occurs, no
// Configuration will be returned and an error is generated.
func NewConfiguration(r io.Reader) (*Configuration, error) {
	result := &Configuration{}
	rawData, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(rawData, result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// NewConfigurationFromFile creates a new Configuration struct from
// the file behind the given path.
func NewConfigurationFromFile(filepath string) (*Configuration, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return NewConfiguration(file)
}

// The StatusHandler updates the global server status mapping and triggers notifications
// if a server's status has changed.
func StatusHandler(config Configuration, statusUpdateChannel <-chan StatusUpdate, exitChannel chan struct{}, doneGroup *sync.WaitGroup) {
	notificationChannel := make(chan StatusUpdate, 10)
	notificationDoneGroup := sync.WaitGroup{}
	var notifySlack bool = (len(config.Slack.NotifiedChannels) != 0)
	if notifySlack {
		notificationDoneGroup.Add(1)
		go SlackNotifier(config, notificationChannel, &notificationDoneGroup)
	}
loop:
	for {
		select {
		case status := <-statusUpdateChannel:
			previousStatus := statusRegistryManager.GetStatus(status.ServerName)
			statusRegistryManager.SetStatus(status)
			log.Println(status)
			if notifySlack {
				// If this was the first time the server got a status, don't send out a notification to avoid
				// noise during restarts.
				if previousStatus != "" {
					notificationChannel <- status
				} else {
					log.Println("Skipping first status from entering the notification chain")
				}
			}
			break
		case <-exitChannel:
			break loop
		}
	}
	close(notificationChannel)
	log.Println("Waiting for notification handlers to shut down.")
	notificationDoneGroup.Wait()
	doneGroup.Done()
}

// ServerHandler is responsible for checking a single server periodically and
// reporting any status changes through the statusUpdateChannel.
func ServerHandler(serverName string, serverConfig ServerConfiguration, statusUpdateChannel chan<- StatusUpdate, exitChannel chan struct{}, doneGroup *sync.WaitGroup) {
	client := http.Client{}
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return fmt.Errorf("Received a redirection as response")
	}
	previousStatus := ""
	newStatus := ""
	timeout := serverConfig.Timeout
	delay := serverConfig.Delay
	if timeout == 0 {
		timeout = DEFAULT_TIMEOUT
	}
	if delay == 0 {
		delay = DEFAULT_DELAY
	}
	finalTimeout, err := time.ParseDuration(fmt.Sprintf("%ds", timeout))
	if err != nil {
		finalTimeout = DEFAULT_TIMEOUT * time.Second
	}
	finalDelay, err := time.ParseDuration(fmt.Sprintf("%ds", delay))
	if err != nil {
		finalDelay = DEFAULT_DELAY * time.Second
	}
	client.Timeout = finalTimeout
	log.Printf("Processing server %v with a timeout of %vs\n", serverName, finalTimeout.Seconds())
	var nextPlannedCheck time.Time
loop:
	for {
		select {
		case <-exitChannel:
			log.Printf("Handler for %s received exit signal\n", serverName)
			break loop
		default:
		}

		if time.Now().Before(nextPlannedCheck) {
			time.Sleep(time.Second)
			continue
		}

		log.Printf("Checking %s\n", serverName)
		startTime := time.Now()
		resp, err := client.Get(serverConfig.IsAliveUrl)
		duration := time.Now().Sub(startTime)
		if err != nil {
			newStatus = STATUS_OFFLINE
		} else {
			if resp.StatusCode != 200 {
				newStatus = STATUS_OFFLINE

			} else {
				newStatus = STATUS_ONLINE
			}
			resp.Body.Close()
		}
		if newStatus != previousStatus {
			statusUpdateChannel <- StatusUpdate{ServerName: serverName, Status: newStatus, Duration: duration}
		}
		previousStatus = newStatus

		// Check the server periodically
		if newStatus == STATUS_OFFLINE {
			nextPlannedCheck = time.Now().Add(finalDelay * 2)
		} else {
			nextPlannedCheck = time.Now().Add(finalDelay)
		}
	}
	log.Printf("Shutting down %s worker\n", serverName)
	doneGroup.Done()
}

func main() {
	var configPath string
	// First we have to determine what servers should be checked and how. For that we
	// parse our configuration file.
	flag.StringVar(&configPath, "config", "", "Path to a configuration file")
	flag.Parse()
	if configPath == "" {<commit_msg>Make things work for broken TLS certs<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/zerok/statusd/Godeps/_workspace/src/gopkg.in/v2/yaml"
)

const (
	DEFAULT_TIMEOUT = 30
	DEFAULT_DELAY   = 30
	STATUS_OFFLINE  = "offline"
	STATUS_ONLINE   = "online"
)

type ServerConfiguration struct {
	IsAliveUrl string `yaml:"isAliveUrl"`
	Timeout    int    `yaml:"timeout"`
	Delay      int    `yaml:"delay"`
}

type HttpConfiguration struct {
	HostAddr string `yaml:"addr"`
}

type SlackConfiguration struct {
	Token            string              `yaml:"token"`
	Team             string              `yaml:"team"`
	NotifiedChannels map[string][]string `yaml:"channels"`
}

type Configuration struct {
	Servers map[string]ServerConfiguration `yaml:"servers"`
	Slack   SlackConfiguration             `yaml:"slack"`
	Http    HttpConfiguration              `yaml:"http"`
}

var statusRegistryManager StatusRegistryManager = NewStatusRegistryManager()

// NewConfiguration parses YAML data provided through a Reader
// into our configuration object. If any error occurs, no
// Configuration will be returned and an error is generated.
func NewConfiguration(r io.Reader) (*Configuration, error) {
	result := &Configuration{}
	rawData, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(rawData, result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// NewConfigurationFromFile creates a new Configuration struct from
// the file behind the given path.
func NewConfigurationFromFile(filepath string) (*Configuration, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return NewConfiguration(file)
}

// The StatusHandler updates the global server status mapping and triggers notifications
// if a server's status has changed.
func StatusHandler(config Configuration, statusUpdateChannel <-chan StatusUpdate, exitChannel chan struct{}, doneGroup *sync.WaitGroup) {
	notificationChannel := make(chan StatusUpdate, 10)
	notificationDoneGroup := sync.WaitGroup{}
	var notifySlack bool = (len(config.Slack.NotifiedChannels) != 0)
	if notifySlack {
		notificationDoneGroup.Add(1)
		go SlackNotifier(config, notificationChannel, &notificationDoneGroup)
	}
loop:
	for {
		select {
		case status := <-statusUpdateChannel:
			previousStatus := statusRegistryManager.GetStatus(status.ServerName)
			statusRegistryManager.SetStatus(status)
			log.Println(status)
			if notifySlack {
				// If this was the first time the server got a status, don't send out a notification to avoid
				// noise during restarts.
				if previousStatus != "" {
					notificationChannel <- status
				} else {
					log.Println("Skipping first status from entering the notification chain")
				}
			}
			break
		case <-exitChannel:
			break loop
		}
	}
	close(notificationChannel)
	log.Println("Waiting for notification handlers to shut down.")
	notificationDoneGroup.Wait()
	doneGroup.Done()
}

// ServerHandler is responsible for checking a single server periodically and
// reporting any status changes through the statusUpdateChannel.
func ServerHandler(serverName string, serverConfig ServerConfiguration, statusUpdateChannel chan<- StatusUpdate, exitChannel chan struct{}, doneGroup *sync.WaitGroup) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := http.Client{Transport: tr}
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return fmt.Errorf("Received a redirection as response")
	}
	previousStatus := ""
	newStatus := ""
	timeout := serverConfig.Timeout
	delay := serverConfig.Delay
	if timeout == 0 {
		timeout = DEFAULT_TIMEOUT
	}
	if delay == 0 {
		delay = DEFAULT_DELAY
	}
	finalTimeout, err := time.ParseDuration(fmt.Sprintf("%ds", timeout))
	if err != nil {
		finalTimeout = DEFAULT_TIMEOUT * time.Second
	}
	finalDelay, err := time.ParseDuration(fmt.Sprintf("%ds", delay))
	if err != nil {
		finalDelay = DEFAULT_DELAY * time.Second
	}
	client.Timeout = finalTimeout
	log.Printf("Processing server %v with a timeout of %vs\n", serverName, finalTimeout.Seconds())
	var nextPlannedCheck time.Time
loop:
	for {
		select {
		case <-exitChannel:
			log.Printf("Handler for %s received exit signal\n", serverName)
			break loop
		default:
		}

		if time.Now().Before(nextPlannedCheck) {
			time.Sleep(time.Second)
			continue
		}

		startTime := time.Now()
		resp, err := client.Get(serverConfig.IsAliveUrl)
		duration := time.Now().Sub(startTime)
		if err != nil {
			log.Printf(err.Error())
			newStatus = STATUS_OFFLINE
		} else {
			if resp.StatusCode != 200 {
				log.Printf("Returned status %v", resp.StatusCode)
				newStatus = STATUS_OFFLINE

			} else {
				newStatus = STATUS_ONLINE
			}
			resp.Body.Close()
		}
		if newStatus == STATUS_ONLINE {
			log.Printf("%s is online\n", serverName)
		} else {
			log.Printf("%s is offline\n", serverName)
		}
		if newStatus != previousStatus {
			statusUpdateChannel <- StatusUpdate{ServerName: serverName, Status: newStatus, Duration: duration}
		}
		previousStatus = newStatus

		// Check the server periodically
		if newStatus == STATUS_OFFLINE {
			nextPlannedCheck = time.Now().Add(finalDelay * 2)
		} else {
			nextPlannedCheck = time.Now().Add(finalDelay)
		}
	}
	log.Printf("Shutting down %s worker\n", serverName)
	doneGroup.Done()
}

func main() {
	var configPath string
	// First we have to determine what servers should be checked and how. For that we
	// parse our configuration file.
	flag.StringVar(&configPath, "config", "", "Path to a configuration file")
	flag.Parse()
	if configPath == "" {
{'fraction_non_alphanumeric': 0.10861153603887339, 'fraction_numerical': 0.0026995410780167373, 'mean_word_length': 3.966041108132261, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6447023', 'n_tokens_mistral': 3629, 'n_tokens_neox': 3347, 'n_words': 959}