<commit_before>// Copyright 2015-2019 Bret Jordan, All rights reserved.
//
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file in the root of the source tree.

package indicator

import (
	"fmt"
)

// ----------------------------------------------------------------------
// Public Methods
// ----------------------------------------------------------------------

/*
Compare - This method will compare two indicators to make sure they are the
same. The indicator receiver is object 1 and the indicator passed in is object
2. This method will return an integer that tracks the number of problems and a
slice of strings that contain the detailed results, whether good or bad.
*/
func (o *Indicator) Compare(obj2 *Indicator) (bool, int, []string) {
	return Compare(o, obj2)
}

// ----------------------------------------------------------------------
// Public Functions
// ----------------------------------------------------------------------

/*
Compare - This function will compare two indicators (object 1 and object 2) to
make sure they are the same. This function will return an integer that tracks
the number of problems and a slice of strings that contain the detailed results,
whether good or bad.
*/
func Compare(obj1, obj2 *Indicator) (bool, int, []string) {
	problemsFound := 0
	resultDetails := make([]string, 0)

	// Check common properties
	if valid, problems, d := obj1.CommonObjectProperties.Compare(&obj2.CommonObjectProperties); valid != true {
		problemsFound += problems
		for _, v := range d {
			resultDetails = append(resultDetails, v)
		}
	} else {
		// The Common Properties were good, so lets just capture any details
		// that were returned.
		for _, v := range d {
			resultDetails = append(resultDetails, v)
		}
	}

	// Check Name Value
	if obj1.Name != obj2.Name {
		problemsFound++
		str := fmt.Sprintf("-- Names Do Not Match: %s | %s", obj1.Name, obj2.Name)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Names Match: %s | %s", obj1.Name, obj2.Name)
		resultDetails = append(resultDetails, str)
	}

	// Check Description Value
	if obj1.Description != obj2.Description {
		problemsFound++
		str := fmt.Sprintf("-- Descriptions Do Not Match: %s | %s", obj1.Description, obj2.Description)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Descriptions Match: %s | %s", obj1.Description, obj2.Description)
		resultDetails = append(resultDetails, str)
	}

	// Check Indicator Types Property Length
	if len(obj1.IndicatorTypes) != len(obj2.IndicatorTypes) {
		problemsFound++
		str := fmt.Sprintf("-- Indicator Types Length Do Not Match: %d | %d", len(obj1.IndicatorTypes), len(obj2.IndicatorTypes))
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Indicator Types Length Match: %d | %d", len(obj1.IndicatorTypes), len(obj2.IndicatorTypes))
		resultDetails = append(resultDetails, str)

		// If lengths are the same, then check each value
		for index := range obj1.IndicatorTypes {
			if obj1.IndicatorTypes[index] != obj2.IndicatorTypes[index] {
				problemsFound++
				str := fmt.Sprintf("-- Indicator Types Do Not Match: %s | %s", obj1.IndicatorTypes[index], obj2.IndicatorTypes[index])
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Indicator Types Match: %s | %s", obj1.IndicatorTypes[index], obj2.IndicatorTypes[index])
				resultDetails = append(resultDetails, str)
			}
		}
	}

	// Check Pattern Value
	if obj1.Pattern != obj2.Pattern {
		problemsFound++
		str := fmt.Sprintf("-- Patterns Do Not Match: %s | %s", obj1.Pattern, obj2.Pattern)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Patterns Match: %s | %s", obj1.Pattern, obj2.Pattern)
		resultDetails = append(resultDetails, str)
	}

	// Check ValidFrom Value
	if obj1.ValidFrom != obj2.ValidFrom {
		problemsFound++
		str := fmt.Sprintf("-- ValidFrom Values Do Not Match: %s | %s", obj1.ValidFrom, obj2.ValidFrom)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ ValidFrom Values Match: %s | %s", obj1.ValidFrom, obj2.ValidFrom)
		resultDetails = append(resultDetails, str)
	}

	// Check ValidUntil Value
	if obj1.ValidUntil != obj2.ValidUntil {
		problemsFound++
		str := fmt.Sprintf("-- ValidUntil Values Do Not Match: %s | %s", obj1.ValidUntil, obj2.ValidUntil)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ ValidUntil Values Match: %s | %s", obj1.ValidUntil, obj2.ValidUntil)
		resultDetails = append(resultDetails, str)
	}

	// Check Kill Chain Phases Property Length
	if len(obj1.KillChainPhases) != len(obj2.KillChainPhases) {
		problemsFound++
		str := fmt.Sprintf("-- Kill Chain Phases Length Do Not Match: %d | %d", len(obj1.KillChainPhases), len(obj2.KillChainPhases))
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Kill Chain Phases Length Match: %d | %d", len(obj1.KillChainPhases), len(obj2.KillChainPhases))
		resultDetails = append(resultDetails, str)
		for index := range obj1.KillChainPhases {
			// Check Kill Chain Phases values
			if obj1.KillChainPhases[index].KillChainName != obj2.KillChainPhases[index].KillChainName {
				problemsFound++
				str := fmt.Sprintf("-- Kill Chain Names Do Not Match: %s | %s", obj1.KillChainPhases[index].KillChainName, obj2.KillChainPhases[index].KillChainName)
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Kill Chain Names Match: %s | %s", obj1.KillChainPhases[index].KillChainName, obj2.KillChainPhases[index].KillChainName)
				resultDetails = append(resultDetails, str)
			}

			// Check Kill Chain Phases values
			if obj1.KillChainPhases[index].PhaseName != obj2.KillChainPhases[index].PhaseName {
				problemsFound++
				str := fmt.Sprintf("-- Kill Chain Phases Do Not Match: %s | %s", obj1.KillChainPhases[index].PhaseName, obj2.KillChainPhases[index].PhaseName)
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Kill Chain Phases Match: %s | %s", obj1.KillChainPhases[index].PhaseName, obj2.KillChainPhases[index].PhaseName)
				resultDetails = append(resultDetails, str)
			}
		}
	}

	if problemsFound > 0 {
		return false, problemsFound, resultDetails
	}

	return true, 0, resultDetails
}
<commit_msg>added support for stix 2.1 features<commit_after>// Copyright 2015-2019 Bret Jordan, All rights reserved.
//
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file in the root of the source tree.

package indicator

import (
	"fmt"
)

// ----------------------------------------------------------------------
// Public Methods
// ----------------------------------------------------------------------

/*
Compare - This method will compare two indicators to make sure they are the
same. The indicator receiver is object 1 and the indicator passed in is object
2. This method will return an integer that tracks the number of problems and a
slice of strings that contain the detailed results, whether good or bad.
*/
func (o *Indicator) Compare(obj2 *Indicator) (bool, int, []string) {
	return Compare(o, obj2)
}

// ----------------------------------------------------------------------
// Public Functions
// ----------------------------------------------------------------------

/*
Compare - This function will compare two indicators (object 1 and object 2) to
make sure they are the same. This function will return an integer that tracks
the number of problems and a slice of strings that contain the detailed results,
whether good or bad.
*/
func Compare(obj1, obj2 *Indicator) (bool, int, []string) {
	problemsFound := 0
	resultDetails := make([]string, 0)

	// Check common properties
	if valid, problems, d := obj1.CommonObjectProperties.Compare(&obj2.CommonObjectProperties); valid != true {
		problemsFound += problems
		for _, v := range d {
			resultDetails = append(resultDetails, v)
		}
	} else {
		// The Common Properties were good, so lets just capture any details
		// that were returned.
		for _, v := range d {
			resultDetails = append(resultDetails, v)
		}
	}

	// Check Name Value
	if obj1.Name != obj2.Name {
		problemsFound++
		str := fmt.Sprintf("-- Names Do Not Match: %s | %s", obj1.Name, obj2.Name)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Names Match: %s | %s", obj1.Name, obj2.Name)
		resultDetails = append(resultDetails, str)
	}

	// Check Description Value
	if obj1.Description != obj2.Description {
		problemsFound++
		str := fmt.Sprintf("-- Descriptions Do Not Match: %s | %s", obj1.Description, obj2.Description)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Descriptions Match: %s | %s", obj1.Description, obj2.Description)
		resultDetails = append(resultDetails, str)
	}

	// Check Indicator Types Property Length
	if len(obj1.IndicatorTypes) != len(obj2.IndicatorTypes) {
		problemsFound++
		str := fmt.Sprintf("-- Indicator Types Length Do Not Match: %d | %d", len(obj1.IndicatorTypes), len(obj2.IndicatorTypes))
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Indicator Types Length Match: %d | %d", len(obj1.IndicatorTypes), len(obj2.IndicatorTypes))
		resultDetails = append(resultDetails, str)

		// If lengths are the same, then check each value
		for index := range obj1.IndicatorTypes {
			if obj1.IndicatorTypes[index] != obj2.IndicatorTypes[index] {
				problemsFound++
				str := fmt.Sprintf("-- Indicator Types Do Not Match: %s | %s", obj1.IndicatorTypes[index], obj2.IndicatorTypes[index])
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Indicator Types Match: %s | %s", obj1.IndicatorTypes[index], obj2.IndicatorTypes[index])
				resultDetails = append(resultDetails, str)
			}
		}
	}

	// Check Pattern Value
	if obj1.Pattern != obj2.Pattern {
		problemsFound++
		str := fmt.Sprintf("-- Patterns Do Not Match: %s | %s", obj1.Pattern, obj2.Pattern)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Patterns Match: %s | %s", obj1.Pattern, obj2.Pattern)
		resultDetails = append(resultDetails, str)
	}

	// Check PatternType Value
	if obj1.PatternType != obj2.PatternType {
		problemsFound++
		str := fmt.Sprintf("-- Pattern Types Do Not Match: %s | %s", obj1.PatternType, obj2.PatternType)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Pattern Types Match: %s | %s", obj1.PatternType, obj2.PatternType)
		resultDetails = append(resultDetails, str)
	}

	// Check PatternVersion Value
	if obj1.PatternVersion != obj2.PatternVersion {
		problemsFound++
		str := fmt.Sprintf("-- Pattern Versions Do Not Match: %s | %s", obj1.PatternVersion, obj2.PatternVersion)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Pattern Versions Match: %s | %s", obj1.PatternVersion, obj2.PatternVersion)
		resultDetails = append(resultDetails, str)
	}

	// Check ValidFrom Value
	if obj1.ValidFrom != obj2.ValidFrom {
		problemsFound++
		str := fmt.Sprintf("-- ValidFrom Values Do Not Match: %s | %s", obj1.ValidFrom, obj2.ValidFrom)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ ValidFrom Values Match: %s | %s", obj1.ValidFrom, obj2.ValidFrom)
		resultDetails = append(resultDetails, str)
	}

	// Check ValidUntil Value
	if obj1.ValidUntil != obj2.ValidUntil {
		problemsFound++
		str := fmt.Sprintf("-- ValidUntil Values Do Not Match: %s | %s", obj1.ValidUntil, obj2.ValidUntil)
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ ValidUntil Values Match: %s | %s", obj1.ValidUntil, obj2.ValidUntil)
		resultDetails = append(resultDetails, str)
	}

	// Check Kill Chain Phases Property Length
	if len(obj1.KillChainPhases) != len(obj2.KillChainPhases) {
		problemsFound++
		str := fmt.Sprintf("-- Kill Chain Phases Length Do Not Match: %d | %d", len(obj1.KillChainPhases), len(obj2.KillChainPhases))
		resultDetails = append(resultDetails, str)
	} else {
		str := fmt.Sprintf("++ Kill Chain Phases Length Match: %d | %d", len(obj1.KillChainPhases), len(obj2.KillChainPhases))
		resultDetails = append(resultDetails, str)
		for index := range obj1.KillChainPhases {
			// Check Kill Chain Phases values
			if obj1.KillChainPhases[index].KillChainName != obj2.KillChainPhases[index].KillChainName {
				problemsFound++
				str := fmt.Sprintf("-- Kill Chain Names Do Not Match: %s | %s", obj1.KillChainPhases[index].KillChainName, obj2.KillChainPhases[index].KillChainName)
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Kill Chain Names Match: %s | %s", obj1.KillChainPhases[index].KillChainName, obj2.KillChainPhases[index].KillChainName)
				resultDetails = append(resultDetails, str)
			}

			// Check Kill Chain Phases values
			if obj1.KillChainPhases[index].PhaseName != obj2.KillChainPhases[index].PhaseName {
				problemsFound++
				str := fmt.Sprintf("-- Kill Chain Phases Do Not Match: %s | %s", obj1.KillChainPhases[index].PhaseName, obj2.KillChainPhases[index].PhaseName)
				resultDetails = append(resultDetails, str)
			} else {
				str := fmt.Sprintf("++ Kill Chain Phases Match: %s | %s", obj1.KillChainPhases[index].PhaseName, obj2.KillChainPhases[index].PhaseName)
				resultDetails = append(resultDetails, str)
			}
		}
	}

	if problemsFound > 0 {
		return false, problemsFound, resultDetails
	}

	return true, 0, resultDetails
}
<|endoftext|>


<commit_before><commit_msg>nicer markers. fixes #7<commit_after><|endoftext|>


<commit_before>package git

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(hookPayload){
		"push": handlePush,
	}

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for " + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Error("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// TODO: other way around, if we have secret, payload needs signature
	// The payload might have an X-Hub-Signature header, which means the webhook has a secret, so we have to
	// validate the signature contained in the header
	signature := r.Header.Get(SIGNATUREHEADER)
	if signature != "" {
		log.Debug("Received signature \"" + signature + "\" for payload")
	}

	// If we actually received a signature, calculate our own and validate
	// On succesful validation call our handler, else return with an error log message
	if signature != "" {
		log.Debug("Received signature " + signature)
		repoSecret := Auth.RequestSecret(payload.Repository.FullName)
		if len(repoSecret) == 0 {
			log.Error("No secret was configured, cannot verify their signature")
			return
		}
		calculatedSignature := authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if authentication.CompareSignatures([]byte(signature), []byte("sha1="+calculatedSignature)) {
			eventHandler(payload)
		} else {
			log.Error("Signatures didn't match")
			return
		}
	} else {
		eventHandler(payload)
	}
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(payload hookPayload) {
	log.Info("Handling received push event")

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After
	getArchive(repoName, repoOwner, commitId, repoToken)
}

func getArchive(repoName string, repoOwner string, commitId string, repoToken *oauth2.Token) string {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			log.Error("Error while constructing archive URL: ", err)
			return ""
		}
	} else {
		gitClient := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(repoToken)))
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			log.Error("Error while getting archive URL: ", err)
			return ""
		}
	}

	log.Debug("Found archive URL ", archiveUrl)

	return "stub"
}
<commit_msg>Do proper check on payload signature<commit_after>package git

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(hookPayload){
		"push": handlePush,
	}

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for " + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Error("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// The repository that this payload is for might have a secret configured, in which case we expect
	// a signature with the payload. The given signature then needs to match a signature we calculate ourselves.
	// Only then will we call our handler, else we'll log an error and return
	repoSecret := Auth.RequestSecret(payload.Repository.FullName)
	if len(repoSecret) == 0 {
		log.Error("No secret was configured, cannot verify their signature")
	} else {
		signature := r.Header.Get(SIGNATUREHEADER)
		if signature == "" {
			log.Error("Expected signature for payload, but none given")
			return
		}
		log.Debug("Received signature " + signature)
		calculatedSignature := "sha1=" + authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if !authentication.CompareSignatures([]byte(signature), []byte(calculatedSignature)) {
			log.Error("Signatures didn't match")
			return
		}
	}
	eventHandler(payload)
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(payload hookPayload) {
	log.Info("Handling received push event")

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After
	getArchive(repoName, repoOwner, commitId, repoToken)
}

func getArchive(repoName string, repoOwner string, commitId string, repoToken *oauth2.Token) string {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			log.Error("Error while constructing archive URL: ", err)
			return ""
		}
	} else {
		gitClient := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(repoToken)))
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			log.Error("Error while getting archive URL: ", err)
			return ""
		}
	}

	log.Debug("Found archive URL ", archiveUrl)

	return "stub"
}
<|endoftext|>


<commit_before>package cluster

/*
 Copyright 2019 Crunchy Data Solutions, 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.
*/

import (
	crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
	"github.com/crunchydata/postgres-operator/config"
	"github.com/crunchydata/postgres-operator/kubeapi"
	"github.com/crunchydata/postgres-operator/util"
	log "github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"os"
	"strings"
)

// ProcessPolicies ...
func ProcessPolicies(clientset *kubernetes.Clientset, restclient *rest.RESTClient, stopchan chan struct{}, namespace string) {

	lo := meta_v1.ListOptions{LabelSelector: config.LABEL_PG_CLUSTER + ",primary"}
	fw, err := clientset.Core().Pods(namespace).Watch(lo)
	if err != nil {
		log.Error("fatal error in ProcessPolicies " + err.Error())
		os.Exit(2)
	}

	_, err4 := watch.Until(0, fw, func(event watch.Event) (bool, error) {
		log.Infof("got a processpolicies watch event %v\n", event.Type)

		switch event.Type {
		case watch.Added:
		case watch.Deleted:
		case watch.Error:
			log.Infof("deployment processpolicy error event")
		case watch.Modified:
			pod := event.Object.(*v1.Pod)
			ready, restarts := podReady(pod)
			if restarts > 0 {
				log.Info("restarts > 0, will not apply policies again to " + pod.Name)
			} else if ready {
				clusterName := getClusterName(pod)
				applyPolicies(namespace, clientset, restclient, clusterName)
			}

		default:
			log.Infoln("processpolices unknown watch event %v\n", event.Type)
		}

		return false, nil
	})

	if err4 != nil {
		log.Error("error in ProcessPolicies " + err4.Error())
	}

}

// applyPolicies ...
func applyPolicies(namespace string, clientset *kubernetes.Clientset, restclient *rest.RESTClient, clusterName string) {
	//get the crv1 which holds the requested labels if any
	cl := crv1.Pgcluster{}
	_, err := kubeapi.Getpgcluster(restclient, &cl, clusterName, namespace)
	if err != nil {
		return
	}

	if cl.Spec.Policies == "" {
		log.Debugf("no policies to apply to %s", clusterName)
		return
	}
	log.Debugf("policies to apply to %s are %s", clusterName, cl.Spec.Policies)
	policies := strings.Split(cl.Spec.Policies, ",")

	//apply the policies
	labels := make(map[string]string)

	for _, v := range policies {
		err = util.ExecPolicy(clientset, restclient, namespace, v, cl.Spec.Name)
		if err != nil {
			log.Error(err)
		} else {
			labels[v] = "pgpolicy"
		}

	}

	err = util.UpdatePolicyLabels(clientset, clusterName, namespace, labels)
	if err != nil {
		log.Error(err)
	}
}

// podReady ...
func podReady(pod *v1.Pod) (bool, int32) {
	var restartCount int32
	readyCount := 0
	containerCount := 0
	for _, stat := range pod.Status.ContainerStatuses {
		restartCount = restartCount + stat.RestartCount
		containerCount++
		if stat.Ready {
			readyCount++
		}
	}
	log.Debugf(" %s %d/%d", pod.Name, readyCount, containerCount)
	if readyCount > 0 && readyCount == containerCount {
		return true, restartCount
	}
	return false, restartCount

}

// getClusterName ...
func getClusterName(pod *v1.Pod) string {
	var clusterName string
	labels := pod.ObjectMeta.Labels
	for k, v := range labels {
		if k == config.LABEL_PG_CLUSTER {
			clusterName = v
		}
	}

	return clusterName
}
<commit_msg>Removed the Operator "ProcessPolicies" function<commit_after><|endoftext|>


<commit_before>package api

import (
	"time"
)

// InstanceType represents the type if instance being returned or requested via the API.
type InstanceType string

// InstanceTypeAny defines the instance type value for requesting any instance type.
const InstanceTypeAny = InstanceType("")

// InstanceTypeContainer defines the instance type value for a container.
const InstanceTypeContainer = InstanceType("container")

// InstanceTypeVM defines the instance type value for a virtual-machine.
const InstanceTypeVM = InstanceType("virtual-machine")

// InstancesPost represents the fields available for a new LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancesPost struct {
	InstancePut `yaml:",inline"`

	// Instance name
	// Example: foo
	Name string `json:"name" yaml:"name"`

	// Creation source
	Source InstanceSource `json:"source" yaml:"source"`

	// Cloud instance type (AWS, GCP, Azure, ...) to emulate with limits
	// Example: t1.micro
	InstanceType string `json:"instance_type" yaml:"instance_type"`

	// Type (container or virtual-machine)
	// Example: container
	Type InstanceType `json:"type" yaml:"type"`
}

// InstancesPut represents the fields available for a mass update.
//
// swagger:model
//
// API extension: instance_bulk_state_change
type InstancesPut struct {
	// Desired runtime state
	State *InstanceStatePut `json:"state" yaml:"state"`
}

// InstancePost represents the fields required to rename/move a LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancePost struct {
	// New name for the instance
	// Example: bar
	Name string `json:"name" yaml:"name"`

	// Whether the instance is being migrated to another server
	// Example: false
	Migration bool `json:"migration" yaml:"migration"`

	// Whether to perform a live migration (migration only)
	// Example: false
	Live bool `json:"live" yaml:"live"`

	// Whether snapshots should be discarded (migration only)
	// Example: false
	InstanceOnly bool `json:"instance_only" yaml:"instance_only"`

	// Whether snapshots should be discarded (migration only, deprecated, use instance_only)
	// Example: false
	ContainerOnly bool `json:"container_only" yaml:"container_only"` // Deprecated, use InstanceOnly.

	// Target for the migration, will use pull mode if not set (migration only)
	Target *InstancePostTarget `json:"target" yaml:"target"`

	// Target pool for local cross-pool move
	// Example: baz
	//
	// API extension: instance_pool_move
	Pool string `json:"pool" yaml:"pool"`

	// Target project for local cross-project move
	// Example: foo
	//
	// API extension: instance_project_move
	Project string `json:"project" yaml:"project"`
}

// InstancePostTarget represents the migration target host and operation.
//
// swagger:model
//
// API extension: instances
type InstancePostTarget struct {
	// The certificate of the migration target
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// The operation URL on the remote target
	// Example: https://1.2.3.4:8443/1.0/operations/5e8e1638-5345-4c2d-bac9-2c79c8577292
	Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`

	// Migration websockets credentials
	// Example: {"migration": "random-string", "criu": "random-string"}
	Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
}

// InstancePut represents the modifiable fields of a LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancePut struct {
	// Architecture name
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Instance configuration (see doc/instances.md)
	// Example: {"security.nesting": "true"}
	Config map[string]string `json:"config" yaml:"config"`

	// Instance devices (see doc/instances.md)
	// Example: {"root": {"type": "disk", "pool": "default", "path": "/"}}
	Devices map[string]map[string]string `json:"devices" yaml:"devices"`

	// Whether the instance is ephemeral (deleted on shutdown)
	// Example: false
	Ephemeral bool `json:"ephemeral" yaml:"ephemeral"`

	// List of profiles applied to the instance
	// Example: ["default"]
	Profiles []string `json:"profiles" yaml:"profiles"`

	// If set, instance will be restored to the provided snapshot name
	// Example: snap0
	Restore string `json:"restore,omitempty" yaml:"restore,omitempty"`

	// Whether the instance currently has saved state on disk
	// Example: false
	Stateful bool `json:"stateful" yaml:"stateful"`

	// Instance description
	// Example: My test instance
	Description string `json:"description" yaml:"description"`
}

// Instance represents a LXD instance.
//
// swagger:model
//
// API extension: instances
type Instance struct {
	InstancePut `yaml:",inline"`

	// Instance creation timestamp
	// Example: 2021-03-23T20:00:00-04:00
	CreatedAt time.Time `json:"created_at" yaml:"created_at"`

	// Expanded configuration (all profiles and local config merged)
	// Example: {"security.nesting": "true"}
	ExpandedConfig map[string]string `json:"expanded_config" yaml:"expanded_config"`

	// Expanded devices (all profiles and local devices merged)
	// Example: {"root": {"type": "disk", "pool": "default", "path": "/"}}
	ExpandedDevices map[string]map[string]string `json:"expanded_devices" yaml:"expanded_devices"`

	// Instance name
	// Example: foo
	Name string `json:"name" yaml:"name"`

	// Instance status (see instance_state)
	// Example: Running
	Status string `json:"status" yaml:"status"`

	// Instance status code (see instance_state)
	// Example: 101
	StatusCode StatusCode `json:"status_code" yaml:"status_code"`

	// Last start timestamp
	// Example: 2021-03-23T20:00:00-04:00
	LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`

	// What cluster member this instance is located on
	// Example: lxd01
	Location string `json:"location" yaml:"location"`

	// The type of instance (container or virtual-machine)
	// Example: container
	Type string `json:"type" yaml:"type"`
}

// InstanceFull is a combination of Instance, InstanceBackup, InstanceState and InstanceSnapshot.
//
// swagger:model
//
// API extension: instances
type InstanceFull struct {
	Instance `yaml:",inline"`

	// List of backups.
	Backups []InstanceBackup `json:"backups" yaml:"backups"`

	// Current state.
	State *InstanceState `json:"state" yaml:"state"`

	// List of snapshots.
	Snapshots []InstanceSnapshot `json:"snapshots" yaml:"snapshots"`
}

// Writable converts a full Instance struct into a InstancePut struct (filters read-only fields).
//
// API extension: instances
func (c *Instance) Writable() InstancePut {
	return c.InstancePut
}

// IsActive checks whether the instance state indicates the instance is active.
//
// API extension: instances
func (c Instance) IsActive() bool {
	switch c.StatusCode {
	case Stopped:
		return false
	case Error:
		return false
	default:
		return true
	}
}

// InstanceSource represents the creation source for a new instance.
//
// swagger:model
//
// API extension: instances
type InstanceSource struct {
	// Source type
	// Example: image
	Type string `json:"type" yaml:"type"`

	// Certificate (for remote images or migration)
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Image alias name (for image source)
	// Example: ubuntu/20.04
	Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`

	// Image fingerprint (for image source)
	// Example: ed56997f7c5b48e8d78986d2467a26109be6fb9f2d92e8c7b08eb8b6cec7629a
	Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`

	// Image filters (for image source)
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`

	// Remote server URL (for remote images)
	// Example: https://images.linuxcontainers.org
	Server string `json:"server,omitempty" yaml:"server,omitempty"`

	// Remote server secret (for remote private images)
	// Example: RANDOM-STRING
	Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`

	// Protocol name (for remote image)
	// Example: simplestreams
	Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`

	// Base image fingerprint (for faster migration)
	// Example: ed56997f7c5b48e8d78986d2467a26109be6fb9f2d92e8c7b08eb8b6cec7629a
	BaseImage string `json:"base-image,omitempty" yaml:"base-image,omitempty"`

	// Whether to use pull or push mode (for migration)
	// Example: pull
	Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`

	// Remote operation URL (for migration)
	// Example: https://1.2.3.4:8443/1.0/operations/1721ae08-b6a8-416a-9614-3f89302466e1
	Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`

	// Map of migration websockets (for migration)
	// Example: {"criu": "RANDOM-STRING", "rsync": "RANDOM-STRING"}
	Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`

	// Existing instance name or snapshot (for copy)
	// Example: foo/snap0
	Source string `json:"source,omitempty" yaml:"source,omitempty"`

	// Whether this is a live migration (for migration)
	// Example: false
	Live bool `json:"live,omitempty" yaml:"live,omitempty"`

	// Whether the copy should skip the snapshots (for copy)
	// Example: false
	InstanceOnly bool `json:"instance_only,omitempty" yaml:"instance_only,omitempty"`

	// Whether the copy should skip the snapshots (for copy, deprecated, use instance_only)
	// Example: false
	ContainerOnly bool `json:"container_only,omitempty" yaml:"container_only,omitempty"` // Deprecated, use InstanceOnly.

	// Whether this is refreshing an existing instance (for migration and copy)
	// Example: false
	Refresh bool `json:"refresh,omitempty" yaml:"refresh,omitempty"`

	// Source project name (for copy and local image)
	// Example: blah
	Project string `json:"project,omitempty" yaml:"project,omitempty"`
}
<commit_msg>shared/api: Add Project field to Instance<commit_after>package api

import (
	"time"
)

// InstanceType represents the type if instance being returned or requested via the API.
type InstanceType string

// InstanceTypeAny defines the instance type value for requesting any instance type.
const InstanceTypeAny = InstanceType("")

// InstanceTypeContainer defines the instance type value for a container.
const InstanceTypeContainer = InstanceType("container")

// InstanceTypeVM defines the instance type value for a virtual-machine.
const InstanceTypeVM = InstanceType("virtual-machine")

// InstancesPost represents the fields available for a new LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancesPost struct {
	InstancePut `yaml:",inline"`

	// Instance name
	// Example: foo
	Name string `json:"name" yaml:"name"`

	// Creation source
	Source InstanceSource `json:"source" yaml:"source"`

	// Cloud instance type (AWS, GCP, Azure, ...) to emulate with limits
	// Example: t1.micro
	InstanceType string `json:"instance_type" yaml:"instance_type"`

	// Type (container or virtual-machine)
	// Example: container
	Type InstanceType `json:"type" yaml:"type"`
}

// InstancesPut represents the fields available for a mass update.
//
// swagger:model
//
// API extension: instance_bulk_state_change
type InstancesPut struct {
	// Desired runtime state
	State *InstanceStatePut `json:"state" yaml:"state"`
}

// InstancePost represents the fields required to rename/move a LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancePost struct {
	// New name for the instance
	// Example: bar
	Name string `json:"name" yaml:"name"`

	// Whether the instance is being migrated to another server
	// Example: false
	Migration bool `json:"migration" yaml:"migration"`

	// Whether to perform a live migration (migration only)
	// Example: false
	Live bool `json:"live" yaml:"live"`

	// Whether snapshots should be discarded (migration only)
	// Example: false
	InstanceOnly bool `json:"instance_only" yaml:"instance_only"`

	// Whether snapshots should be discarded (migration only, deprecated, use instance_only)
	// Example: false
	ContainerOnly bool `json:"container_only" yaml:"container_only"` // Deprecated, use InstanceOnly.

	// Target for the migration, will use pull mode if not set (migration only)
	Target *InstancePostTarget `json:"target" yaml:"target"`

	// Target pool for local cross-pool move
	// Example: baz
	//
	// API extension: instance_pool_move
	Pool string `json:"pool" yaml:"pool"`

	// Target project for local cross-project move
	// Example: foo
	//
	// API extension: instance_project_move
	Project string `json:"project" yaml:"project"`
}

// InstancePostTarget represents the migration target host and operation.
//
// swagger:model
//
// API extension: instances
type InstancePostTarget struct {
	// The certificate of the migration target
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// The operation URL on the remote target
	// Example: https://1.2.3.4:8443/1.0/operations/5e8e1638-5345-4c2d-bac9-2c79c8577292
	Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`

	// Migration websockets credentials
	// Example: {"migration": "random-string", "criu": "random-string"}
	Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
}

// InstancePut represents the modifiable fields of a LXD instance.
//
// swagger:model
//
// API extension: instances
type InstancePut struct {
	// Architecture name
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Instance configuration (see doc/instances.md)
	// Example: {"security.nesting": "true"}
	Config map[string]string `json:"config" yaml:"config"`

	// Instance devices (see doc/instances.md)
	// Example: {"root": {"type": "disk", "pool": "default", "path": "/"}}
	Devices map[string]map[string]string `json:"devices" yaml:"devices"`

	// Whether the instance is ephemeral (deleted on shutdown)
	// Example: false
	Ephemeral bool `json:"ephemeral" yaml:"ephemeral"`

	// List of profiles applied to the instance
	// Example: ["default"]
	Profiles []string `json:"profiles" yaml:"profiles"`

	// If set, instance will be restored to the provided snapshot name
	// Example: snap0
	Restore string `json:"restore,omitempty" yaml:"restore,omitempty"`

	// Whether the instance currently has saved state on disk
	// Example: false
	Stateful bool `json:"stateful" yaml:"stateful"`

	// Instance description
	// Example: My test instance
	Description string `json:"description" yaml:"description"`
}

// Instance represents a LXD instance.
//
// swagger:model
//
// API extension: instances
type Instance struct {
	InstancePut `yaml:",inline"`

	// Instance creation timestamp
	// Example: 2021-03-23T20:00:00-04:00
	CreatedAt time.Time `json:"created_at" yaml:"created_at"`

	// Expanded configuration (all profiles and local config merged)
	// Example: {"security.nesting": "true"}
	ExpandedConfig map[string]string `json:"expanded_config" yaml:"expanded_config"`

	// Expanded devices (all profiles and local devices merged)
	// Example: {"root": {"type": "disk", "pool": "default", "path": "/"}}
	ExpandedDevices map[string]map[string]string `json:"expanded_devices" yaml:"expanded_devices"`

	// Instance name
	// Example: foo
	Name string `json:"name" yaml:"name"`

	// Instance status (see instance_state)
	// Example: Running
	Status string `json:"status" yaml:"status"`

	// Instance status code (see instance_state)
	// Example: 101
	StatusCode StatusCode `json:"status_code" yaml:"status_code"`

	// Last start timestamp
	// Example: 2021-03-23T20:00:00-04:00
	LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`

	// What cluster member this instance is located on
	// Example: lxd01
	Location string `json:"location" yaml:"location"`

	// The type of instance (container or virtual-machine)
	// Example: container
	Type string `json:"type" yaml:"type"`

	// Instance project name
	// Example: foo
	//
	// API extension: instance_all_projects
	Project string `json:"project" yaml:"project"`
}

// InstanceFull is a combination of Instance, InstanceBackup, InstanceState and InstanceSnapshot.
//
// swagger:model
//
// API extension: instances
type InstanceFull struct {
	Instance `yaml:",inline"`

	// List of backups.
	Backups []InstanceBackup `json:"backups" yaml:"backups"`

	// Current state.
	State *InstanceState `json:"state" yaml:"state"`

	// List of snapshots.
	Snapshots []InstanceSnapshot `json:"snapshots" yaml:"snapshots"`
}

// Writable converts a full Instance struct into a InstancePut struct (filters read-only fields).
//
// API extension: instances
func (c *Instance) Writable() InstancePut {
	return c.InstancePut
}

// IsActive checks whether the instance state indicates the instance is active.
//
// API extension: instances
func (c Instance) IsActive() bool {
	switch c.StatusCode {
	case Stopped:
		return false
	case Error:
		return false
	default:
		return true
	}
}

// InstanceSource represents the creation source for a new instance.
//
// swagger:model
//
// API extension: instances
type InstanceSource struct {
	// Source type
	// Example: image
	Type string `json:"type" yaml:"type"`

	// Certificate (for remote images or migration)
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Image alias name (for image source)
	// Example: ubuntu/20.04
	Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`

	// Image fingerprint (for image source)
	// Example: ed56997f7c5b48e8d78986d2467a26109be6fb9f2d92e8c7b08eb8b6cec7629a
	Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`

	// Image filters (for image source)
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`

	// Remote server URL (for remote images)
	// Example: https://images.linuxcontainers.org
	Server string `json:"server,omitempty" yaml:"server,omitempty"`

	// Remote server secret (for remote private images)
	// Example: RANDOM-STRING
	Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`

	// Protocol name (for remote image)
	// Example: simplestreams
	Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`

	// Base image fingerprint (for faster migration)
	// Example: ed56997f7c5b48e8d78986d2467a26109be6fb9f2d92e8c7b08eb8b6cec7629a
	BaseImage string `json:"base-image,omitempty" yaml:"base-image,omitempty"`

	// Whether to use pull or push mode (for migration)
	// Example: pull
	Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`

	// Remote operation URL (for migration)
	// Example: https://1.2.3.4:8443/1.0/operations/1721ae08-b6a8-416a-9614-3f89302466e1
	Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`

	// Map of migration websockets (for migration)
	// Example: {"criu": "RANDOM-STRING", "rsync": "RANDOM-STRING"}
	Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`

	// Existing instance name or snapshot (for copy)
	// Example: foo/snap0
	Source string `json:"source,omitempty" yaml:"source,omitempty"`

	// Whether this is a live migration (for migration)
	// Example: false
	Live bool `json:"live,omitempty" yaml:"live,omitempty"`

	// Whether the copy should skip the snapshots (for copy)
	// Example: false
	InstanceOnly bool `json:"instance_only,omitempty" yaml:"instance_only,omitempty"`

	// Whether the copy should skip the snapshots (for copy, deprecated, use instance_only)
	// Example: false
	ContainerOnly bool `json:"container_only,omitempty" yaml:"container_only,omitempty"` // Deprecated, use InstanceOnly.

	// Whether this is refreshing an existing instance (for migration and copy)
	// Example: false
	Refresh bool `json:"refresh,omitempty" yaml:"refresh,omitempty"`

	// Source project name (for copy and local image)
	// Example: blah
	Project string `json:"project,omitempty" yaml:"project,omitempty"`
}
<|endoftext|>


<commit_before>package version

// Version contains the LXD version number
var Version = "3.0.0.beta3"
<commit_msg>Release LXD 3.0.0.beta4<commit_after>package version

// Version contains the LXD version number
var Version = "3.0.0.beta4"
<|endoftext|>


<commit_before>package version

// Version contains the LXD version number
var Version = "5.2"
<commit_msg>Release LXD 5.3<commit_after>package version

// Version contains the LXD version number
var Version = "5.3"
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

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

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

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

package errors

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
)

var _ error = ErrUnexpectedStatus{}

// ErrUnexpectedStatus is returned if a registry API request returned with unexpected HTTP status
type ErrUnexpectedStatus struct {
	Status     string
	StatusCode int
	Body       []byte
}

func (e ErrUnexpectedStatus) Error() string {
	return fmt.Sprintf("unexpected status: %s", e.Status)
}

// NewUnexpectedStatusErr creates an ErrUnexpectedStatus from HTTP response
func NewUnexpectedStatusErr(resp *http.Response) error {
	var b []byte
	if resp.Body != nil {
		b, _ = ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
	}
	return ErrUnexpectedStatus{Status: resp.Status, StatusCode: resp.StatusCode, Body: b}
}
<commit_msg>Include URL and method in `ErrUnexpectedStatus`<commit_after>/*
   Copyright The containerd Authors.

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

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

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

package errors

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
)

var _ error = ErrUnexpectedStatus{}

// ErrUnexpectedStatus is returned if a registry API request returned with unexpected HTTP status
type ErrUnexpectedStatus struct {
	Status                    string
	StatusCode                int
	Body                      []byte
	RequestURL, RequestMethod string
}

func (e ErrUnexpectedStatus) Error() string {
	return fmt.Sprintf("unexpected status: %s", e.Status)
}

// NewUnexpectedStatusErr creates an ErrUnexpectedStatus from HTTP response
func NewUnexpectedStatusErr(resp *http.Response) error {
	var b []byte
	if resp.Body != nil {
		b, _ = ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
	}
	err := ErrUnexpectedStatus{
		Body:          b,
		Status:        resp.Status,
		StatusCode:    resp.StatusCode,
		RequestMethod: resp.Request.Method,
	}
	if resp.Request.URL != nil {
		err.RequestURL = resp.Request.URL.String()
	}
	return err
}
<|endoftext|>


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

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/version"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/ini.v1"

	"github.com/prometheus/mysqld_exporter/collector"
)

var (
	listenAddress = kingpin.Flag(
		"web.listen-address",
		"Address to listen on for web interface and telemetry.",
	).Default(":9104").String()
	metricPath = kingpin.Flag(
		"web.telemetry-path",
		"Path under which to expose metrics.",
	).Default("/metrics").String()
	timeoutOffset = kingpin.Flag(
		"timeout-offset",
		"Offset to subtract from timeout in seconds.",
	).Default("0.25").Float64()
	configMycnf = kingpin.Flag(
		"config.my-cnf",
		"Path to .my.cnf file to read MySQL credentials from.",
	).Default(path.Join(os.Getenv("HOME"), ".my.cnf")).String()
	dsn string
)

// scrapers lists all possible collection methods and if they should be enabled by default.
var scrapers = map[collector.Scraper]bool{
	collector.ScrapeGlobalStatus{}:                        true,
	collector.ScrapeGlobalVariables{}:                     true,
	collector.ScrapeSlaveStatus{}:                         true,
	collector.ScrapeProcesslist{}:                         false,
	collector.ScrapeUser{}:                                false,
	collector.ScrapeTableSchema{}:                         false,
	collector.ScrapeInfoSchemaInnodbTablespaces{}:         false,
	collector.ScrapeInnodbMetrics{}:                       false,
	collector.ScrapeAutoIncrementColumns{}:                false,
	collector.ScrapeBinlogSize{}:                          false,
	collector.ScrapePerfTableIOWaits{}:                    false,
	collector.ScrapePerfIndexIOWaits{}:                    false,
	collector.ScrapePerfTableLockWaits{}:                  false,
	collector.ScrapePerfEventsStatements{}:                false,
	collector.ScrapePerfEventsStatementsSum{}:             false,
	collector.ScrapePerfEventsWaits{}:                     false,
	collector.ScrapePerfFileEvents{}:                      false,
	collector.ScrapePerfFileInstances{}:                   false,
	collector.ScrapePerfReplicationGroupMemberStats{}:     false,
	collector.ScrapePerfReplicationApplierStatsByWorker{}: false,
	collector.ScrapeUserStat{}:                            false,
	collector.ScrapeClientStat{}:                          false,
	collector.ScrapeTableStat{}:                           false,
	collector.ScrapeSchemaStat{}:                          false,
	collector.ScrapeInnodbCmp{}:                           true,
	collector.ScrapeInnodbCmpMem{}:                        true,
	collector.ScrapeQueryResponseTime{}:                   true,
	collector.ScrapeEngineTokudbStatus{}:                  false,
	collector.ScrapeEngineInnodbStatus{}:                  false,
	collector.ScrapeHeartbeat{}:                           false,
	collector.ScrapeSlaveHosts{}:                          false,
}

func parseMycnf(config interface{}) (string, error) {
	var dsn string
	opts := ini.LoadOptions{
		// MySQL ini file can have boolean keys.
		AllowBooleanKeys: true,
	}
	cfg, err := ini.LoadSources(opts, config)
	if err != nil {
		return dsn, fmt.Errorf("failed reading ini file: %s", err)
	}
	user := cfg.Section("client").Key("user").String()
	password := cfg.Section("client").Key("password").String()
	if (user == "") || (password == "") {
		return dsn, fmt.Errorf("no user or password specified under [client] in %s", config)
	}
	host := cfg.Section("client").Key("host").MustString("localhost")
	port := cfg.Section("client").Key("port").MustUint(3306)
	socket := cfg.Section("client").Key("socket").String()
	if socket != "" {
		dsn = fmt.Sprintf("%s:%s@unix(%s)/", user, password, socket)
	} else {
		dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/", user, password, host, port)
	}
	sslCA := cfg.Section("client").Key("ssl-ca").String()
	sslCert := cfg.Section("client").Key("ssl-cert").String()
	sslKey := cfg.Section("client").Key("ssl-key").String()
	if sslCA != "" {
		if tlsErr := customizeTLS(sslCA, sslCert, sslKey); tlsErr != nil {
			tlsErr = fmt.Errorf("failed to register a custom TLS configuration for mysql dsn: %s", tlsErr)
			return dsn, tlsErr
		}
		dsn = fmt.Sprintf("%s?tls=custom", dsn)
	}

	log.Debugln(dsn)
	return dsn, nil
}

func customizeTLS(sslCA string, sslCert string, sslKey string) error {
	var tlsCfg tls.Config
	caBundle := x509.NewCertPool()
	pemCA, err := ioutil.ReadFile(sslCA)
	if err != nil {
		return err
	}
	if ok := caBundle.AppendCertsFromPEM(pemCA); ok {
		tlsCfg.RootCAs = caBundle
	} else {
		return fmt.Errorf("failed parse pem-encoded CA certificates from %s", sslCA)
	}
	if sslCert != "" && sslKey != "" {
		certPairs := make([]tls.Certificate, 0, 1)
		keypair, err := tls.LoadX509KeyPair(sslCert, sslKey)
		if err != nil {
			return fmt.Errorf("failed to parse pem-encoded SSL cert %s or SSL key %s: %s",
				sslCert, sslKey, err)
		}
		certPairs = append(certPairs, keypair)
		tlsCfg.Certificates = certPairs
	}
	mysql.RegisterTLSConfig("custom", &tlsCfg)
	return nil
}

func init() {
	prometheus.MustRegister(version.NewCollector("mysqld_exporter"))
}

func newHandler(metrics collector.Metrics, scrapers []collector.Scraper) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		filteredScrapers := scrapers
		params := r.URL.Query()["collect[]"]
		// Use request context for cancellation when connection gets closed.
		ctx := r.Context()
		// If a timeout is configured via the Prometheus header, add it to the context.
		if v := r.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" {
			timeoutSeconds, err := strconv.ParseFloat(v, 64)
			if err != nil {
				log.Errorf("Failed to parse timeout from Prometheus header: %s", err)
			} else {
				if *timeoutOffset >= timeoutSeconds {
					// Ignore timeout offset if it doesn't leave time to scrape.
					log.Errorf(
						"Timeout offset (--timeout-offset=%.2f) should be lower than prometheus scrape time (X-Prometheus-Scrape-Timeout-Seconds=%.2f).",
						*timeoutOffset,
						timeoutSeconds,
					)
				} else {
					// Subtract timeout offset from timeout.
					timeoutSeconds -= *timeoutOffset
				}
				// Create new timeout context with request context as parent.
				var cancel context.CancelFunc
				ctx, cancel = context.WithTimeout(ctx, time.Duration(timeoutSeconds*float64(time.Second)))
				defer cancel()
				// Overwrite request with timeout context.
				r = r.WithContext(ctx)
			}
		}
		log.Debugln("collect query:", params)

		// Check if we have some "collect[]" query parameters.
		if len(params) > 0 {
			filters := make(map[string]bool)
			for _, param := range params {
				filters[param] = true
			}

			filteredScrapers = nil
			for _, scraper := range scrapers {
				if filters[scraper.Name()] {
					filteredScrapers = append(filteredScrapers, scraper)
				}
			}
		}

		registry := prometheus.NewRegistry()
		registry.MustRegister(collector.New(ctx, dsn, metrics, filteredScrapers))

		gatherers := prometheus.Gatherers{
			prometheus.DefaultGatherer,
			registry,
		}
		// Delegate http serving to Prometheus client library, which will call collector.Collect.
		h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{})
		h.ServeHTTP(w, r)
	}
}

func main() {
	// Generate ON/OFF flags for all scrapers.
	scraperFlags := map[collector.Scraper]*bool{}
	for scraper, enabledByDefault := range scrapers {
		defaultOn := "false"
		if enabledByDefault {
			defaultOn = "true"
		}

		f := kingpin.Flag(
			"collect."+scraper.Name(),
			scraper.Help(),
		).Default(defaultOn).Bool()

		scraperFlags[scraper] = f
	}

	// Parse flags.
	log.AddFlags(kingpin.CommandLine)
	kingpin.Version(version.Print("mysqld_exporter"))
	kingpin.HelpFlag.Short('h')
	kingpin.Parse()

	// landingPage contains the HTML served at '/'.
	// TODO: Make this nicer and more informative.
	var landingPage = []byte(`
<title>MySQLd exporter</title>

MySQLd exporter

<a href='` + *metricPath + `'>Metrics

`)

	log.Infoln("Starting mysqld_exporter", version.Info())
	log.Infoln("Build context", version.BuildContext())

	dsn = os.Getenv("DATA_SOURCE_NAME")
	if len(dsn) == 0 {
		var err error
		if dsn, err = parseMycnf(*configMycnf); err != nil {
			log.Fatal(err)
		}
	}

	// Register only scrapers enabled by flag.
	log.Infof("Enabled scrapers:")
	enabledScrapers := []collector.Scraper{}
	for scraper, enabled := range scraperFlags {
		if *enabled {
			log.Infof(" --collect.%s", scraper.Name())
			enabledScrapers = append(enabledScrapers, scraper)
		}
	}
	handlerFunc := newHandler(collector.NewMetrics(), enabledScrapers)
	http.Handle(*metricPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handlerFunc))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write(landingPage)
	})

	log.Infoln("Listening on", *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Add option to skip tls verification<commit_after>// Copyright 2018 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/version"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/ini.v1"

	"github.com/prometheus/mysqld_exporter/collector"
)

var (
	listenAddress = kingpin.Flag(
		"web.listen-address",
		"Address to listen on for web interface and telemetry.",
	).Default(":9104").String()
	metricPath = kingpin.Flag(
		"web.telemetry-path",
		"Path under which to expose metrics.",
	).Default("/metrics").String()
	timeoutOffset = kingpin.Flag(
		"timeout-offset",
		"Offset to subtract from timeout in seconds.",
	).Default("0.25").Float64()
	configMycnf = kingpin.Flag(
		"config.my-cnf",
		"Path to .my.cnf file to read MySQL credentials from.",
	).Default(path.Join(os.Getenv("HOME"), ".my.cnf")).String()
	tlsSkipVerify = kingpin.Flag(
		"tls.skip-verify",
		"Ignore hostname verification when using a tls connection.",
	).Bool()
	dsn string
)

// scrapers lists all possible collection methods and if they should be enabled by default.
var scrapers = map[collector.Scraper]bool{
	collector.ScrapeGlobalStatus{}:                        true,
	collector.ScrapeGlobalVariables{}:                     true,
	collector.ScrapeSlaveStatus{}:                         true,
	collector.ScrapeProcesslist{}:                         false,
	collector.ScrapeUser{}:                                false,
	collector.ScrapeTableSchema{}:                         false,
	collector.ScrapeInfoSchemaInnodbTablespaces{}:         false,
	collector.ScrapeInnodbMetrics{}:                       false,
	collector.ScrapeAutoIncrementColumns{}:                false,
	collector.ScrapeBinlogSize{}:                          false,
	collector.ScrapePerfTableIOWaits{}:                    false,
	collector.ScrapePerfIndexIOWaits{}:                    false,
	collector.ScrapePerfTableLockWaits{}:                  false,
	collector.ScrapePerfEventsStatements{}:                false,
	collector.ScrapePerfEventsStatementsSum{}:             false,
	collector.ScrapePerfEventsWaits{}:                     false,
	collector.ScrapePerfFileEvents{}:                      false,
	collector.ScrapePerfFileInstances{}:                   false,
	collector.ScrapePerfReplicationGroupMemberStats{}:     false,
	collector.ScrapePerfReplicationApplierStatsByWorker{}: false,
	collector.ScrapeUserStat{}:                            false,
	collector.ScrapeClientStat{}:                          false,
	collector.ScrapeTableStat{}:                           false,
	collector.ScrapeSchemaStat{}:                          false,
	collector.ScrapeInnodbCmp{}:                           true,
	collector.ScrapeInnodbCmpMem{}:                        true,
	collector.ScrapeQueryResponseTime{}:                   true,
	collector.ScrapeEngineTokudbStatus{}:                  false,
	collector.ScrapeEngineInnodbStatus{}:                  false,
	collector.ScrapeHeartbeat{}:                           false,
	collector.ScrapeSlaveHosts{}:                          false,
}

func parseMycnf(config interface{}) (string, error) {
	var dsn string
	opts := ini.LoadOptions{
		// MySQL ini file can have boolean keys.
		AllowBooleanKeys: true,
	}
	cfg, err := ini.LoadSources(opts, config)
	if err != nil {
		return dsn, fmt.Errorf("failed reading ini file: %s", err)
	}
	user := cfg.Section("client").Key("user").String()
	password := cfg.Section("client").Key("password").String()
	if (user == "") || (password == "") {
		return dsn, fmt.Errorf("no user or password specified under [client] in %s", config)
	}
	host := cfg.Section("client").Key("host").MustString("localhost")
	port := cfg.Section("client").Key("port").MustUint(3306)
	socket := cfg.Section("client").Key("socket").String()
	if socket != "" {
		dsn = fmt.Sprintf("%s:%s@unix(%s)/", user, password, socket)
	} else {
		dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/", user, password, host, port)
	}
	sslCA := cfg.Section("client").Key("ssl-ca").String()
	sslCert := cfg.Section("client").Key("ssl-cert").String()
	sslKey := cfg.Section("client").Key("ssl-key").String()
	if sslCA != "" {
		if tlsErr := customizeTLS(sslCA, sslCert, sslKey); tlsErr != nil {
			tlsErr = fmt.Errorf("failed to register a custom TLS configuration for mysql dsn: %s", tlsErr)
			return dsn, tlsErr
		}
		dsn = fmt.Sprintf("%s?tls=custom", dsn)
	}

	log.Debugln(dsn)
	return dsn, nil
}

func customizeTLS(sslCA string, sslCert string, sslKey string) error {
	var tlsCfg tls.Config
	caBundle := x509.NewCertPool()
	pemCA, err := ioutil.ReadFile(sslCA)
	if err != nil {
		return err
	}
	if ok := caBundle.AppendCertsFromPEM(pemCA); ok {
		tlsCfg.RootCAs = caBundle
	} else {
		return fmt.Errorf("failed parse pem-encoded CA certificates from %s", sslCA)
	}
	if sslCert != "" && sslKey != "" {
		certPairs := make([]tls.Certificate, 0, 1)
		keypair, err := tls.LoadX509KeyPair(sslCert, sslKey)
		if err != nil {
			return fmt.Errorf("failed to parse pem-encoded SSL cert %s or SSL key %s: %s",
				sslCert, sslKey, err)
		}
		certPairs = append(certPairs, keypair)
		tlsCfg.Certificates = certPairs

		if *tlsSkipVerify {
			tlsCfg.InsecureSkipVerify = true
		}
	}
	mysql.RegisterTLSConfig("custom", &tlsCfg)
	return nil
}

func init() {
	prometheus.MustRegister(version.NewCollector("mysqld_exporter"))
}

func newHandler(metrics collector.Metrics, scrapers []collector.Scraper) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		filteredScrapers := scrapers
		params := r.URL.Query()["collect[]"]
		// Use request context for cancellation when connection gets closed.
		ctx := r.Context()
		// If a timeout is configured via the Prometheus header, add it to the context.
		if v := r.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" {
			timeoutSeconds, err := strconv.ParseFloat(v, 64)
			if err != nil {
				log.Errorf("Failed to parse timeout from Prometheus header: %s", err)
			} else {
				if *timeoutOffset >= timeoutSeconds {
					// Ignore timeout offset if it doesn't leave time to scrape.
					log.Errorf(
						"Timeout offset (--timeout-offset=%.2f) should be lower than prometheus scrape time (X-Prometheus-Scrape-Timeout-Seconds=%.2f).",
						*timeoutOffset,
						timeoutSeconds,
					)
				} else {
					// Subtract timeout offset from timeout.
					timeoutSeconds -= *timeoutOffset
				}
				// Create new timeout context with request context as parent.
				var cancel context.CancelFunc
				ctx, cancel = context.WithTimeout(ctx, time.Duration(timeoutSeconds*float64(time.Second)))
				defer cancel()
				// Overwrite request with timeout context.
				r = r.WithContext(ctx)
			}
		}
		log.Debugln("collect query:", params)

		// Check if we have some "collect[]" query parameters.
		if len(params) > 0 {
			filters := make(map[string]bool)
			for _, param := range params {
				filters[param] = true
			}

			filteredScrapers = nil
			for _, scraper := range scrapers {
				if filters[scraper.Name()] {
					filteredScrapers = append(filteredScrapers, scraper)
				}
			}
		}

		registry := prometheus.NewRegistry()
		registry.MustRegister(collector.New(ctx, dsn, metrics, filteredScrapers))

		gatherers := prometheus.Gatherers{
			prometheus.DefaultGatherer,
			registry,
		}
		// Delegate http serving to Prometheus client library, which will call collector.Collect.
		h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{})
		h.ServeHTTP(w, r)
	}
}

func main() {
	// Generate ON/OFF flags for all scrapers.
	scraperFlags := map[collector.Scraper]*bool{}
	for scraper, enabledByDefault := range scrapers {
		defaultOn := "false"
		if enabledByDefault {
			defaultOn = "true"
		}

		f := kingpin.Flag(
			"collect."+scraper.Name(),
			scraper.Help(),
		).Default(defaultOn).Bool()

		scraperFlags[scraper] = f
	}

	// Parse flags.
	log.AddFlags(kingpin.CommandLine)
	kingpin.Version(version.Print("mysqld_exporter"))
	kingpin.HelpFlag.Short('h')
	kingpin.Parse()

	// landingPage contains the HTML served at '/'.
	// TODO: Make this nicer and more informative.
	var landingPage = []byte(`
<title>MySQLd exporter</title>

MySQLd exporter

<a href='` + *metricPath + `'>Metrics

`)

	log.Infoln("Starting mysqld_exporter", version.Info())
	log.Infoln("Build context", version.BuildContext())

	dsn = os.Getenv("DATA_SOURCE_NAME")
	if len(dsn) == 0 {
		var err error
		if dsn, err = parseMycnf(*configMycnf); err != nil {
			log.Fatal(err)
		}
	}

	// Register only scrapers enabled by flag.
	log.Infof("Enabled scrapers:")
	enabledScrapers := []collector.Scraper{}
	for scraper, enabled := range scraperFlags {
		if *enabled {
			log.Infof(" --collect.%s", scraper.Name())
			enabledScrapers = append(enabledScrapers, scraper)
		}
	}
	handlerFunc := newHandler(collector.NewMetrics(), enabledScrapers)
	http.Handle(*metricPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handlerFunc))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write(landingPage)
	})

	log.Infoln("Listening on", *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package errors_test

// This file demonstrates how if you forget to name your return value, no
// matter how you handle errors from deferred calls, you have a bug only
// advanced testing will point out.

import (
	"io"
	"sethwklein.net/go/errors"
	"testing"
)

var mockOS = struct {
	Create func(string) (io.WriteCloser, error)
}{
	func(_ string) (io.WriteCloser, error) {
		return mockFile{}, nil
	},
}

type mockFile struct{}

var mockError = errors.New("mock error")

func (_ mockFile) Close() error {
	return mockError
}

func (_ mockFile) Write(data []byte) (n int, err error) {
	return len(data), nil
}

func brokenAppendWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer func() {
		err = errors.Append(err, f.Close())
	}()
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func brokenCallWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer errors.AppendCall(&err, f.Close)
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func brokenManualWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer func() {
		e := f.Close()
		if e != nil {
			// this is simplistic because it assumes err == nil
			err = e
		}
	}()
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func TestBrokenAppend(t *testing.T) {
	err := brokenAppendWriteFile("example.txt", []byte("example!"))
	if err == nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}

func TestBrokenCall(t *testing.T) {
	err := brokenCallWriteFile("example.txt", []byte("example!"))
	if err == nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}

func TestBrokenManual(t *testing.T) {
	err := brokenManualWriteFile("example.txt", []byte("example!"))
	if err == nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}
<commit_msg>Clarify and correct silently broken demonstration<commit_after>package errors_test

// This file demonstrates how if you forget to name your return value, no
// matter how you handle errors from deferred calls, you have a bug only
// advanced testing will point out.
//
// All the broken* functions below should return the error from mockFile.Close,
// but they don't because they forget to use a named return value.

import (
	"io"
	"sethwklein.net/go/errors"
	"testing"
)

var mockOS = struct {
	Create func(string) (io.WriteCloser, error)
}{
	func(_ string) (io.WriteCloser, error) {
		return mockFile{}, nil
	},
}

type mockFile struct{}

var mockError = errors.New("mock error")

func (_ mockFile) Close() error {
	return mockError
}

func (_ mockFile) Write(data []byte) (n int, err error) {
	return len(data), nil
}

func brokenAppendWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer func() {
		err = errors.Append(err, f.Close())
	}()
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func brokenCallWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer errors.AppendCall(&err, f.Close)
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func brokenManualWriteFile(filename string, data []byte) error {
	f, err := mockOS.Create(filename)
	if err != nil {
		return err
	}
	defer func() {
		e := f.Close()
		if e != nil && err == nil {
			// This is assumes both errors can't be meaningful
			// at the same time which is not guaranteed by the
			// method signatures or documentation, although it
			// may be guaranteed by the implementation.
			err = e
		}
	}()
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	return err
}

func TestBrokenAppend(t *testing.T) {
	err := brokenAppendWriteFile("example.txt", []byte("example!"))
	// expected fail
	if err != nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}

func TestBrokenCall(t *testing.T) {
	err := brokenCallWriteFile("example.txt", []byte("example!"))
	// expected fail
	if err != nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}

func TestBrokenManual(t *testing.T) {
	err := brokenManualWriteFile("example.txt", []byte("example!"))
	// expected fail
	if err != nil { // err != mockError {
		t.Errorf("%v != %v", err, mockError)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"errors"
	"github.com/hacksoc-manchester/www/helpers/crypto"
	"github.com/hacksoc-manchester/www/services/databaseService"
	"github.com/hacksoc-manchester/www/services/emailService"
	"net/http"
	"os"
)

func signUp(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates["sign-up"].ExecuteTemplate(w, "layout", reCaptchaSiteKey)

	case "POST":
		firstName := r.PostFormValue("first-name")
		lastName := r.PostFormValue("last-name")
		email := r.PostFormValue("email")
		subscribedToArticles := r.PostFormValue("subscribe-to-articles") == "on"
		subscribedToEvents := r.PostFormValue("subscribe-to-events") == "on"

		var response string

		if reCaptcha.Verify(*r) {
			err := registerUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

			if err == nil {
				response = "Welcome! You are now part of our mailing list."
			} else {
				response = err.Error()
			}
		} else {
			response = "Turing test failed. Please try again."
		}

		templates["message"].ExecuteTemplate(w, "layout", messageModel{"Sign Up", response})

	default:
		errorHandler(w, r, http.StatusBadRequest)
	}
}

func registerUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !subscribedToArticles && !subscribedToEvents {
		errors.New("Please select at least one subscription.")
	}

	err := databaseService.CreateUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

	if err != nil {
		return err
	}

	senderName := "HackSoc"
	senderEmail := os.Getenv("NOREPLY_EMAIL")
	receiverName := firstName + " " + lastName
	subject := "Welcome to HackSoc!"
	message := "You are now part of our mailing list. \n\nTo unsubscribe, click: " + getUnsubscribeLink(email)

	return emailService.Send(senderName, senderEmail, receiverName, email, subject, message)
}

func getUnsubscribeLink(email string) string {
	token, _ := crypto.Encrypt(email)

	return "http://hacksoc.com/unsubscribe?token=" + token
}
<commit_msg>Can no longer sign up without any subscription<commit_after>package handlers

import (
	"errors"
	"github.com/hacksoc-manchester/www/helpers/crypto"
	"github.com/hacksoc-manchester/www/services/databaseService"
	"github.com/hacksoc-manchester/www/services/emailService"
	"net/http"
	"os"
)

func signUp(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates["sign-up"].ExecuteTemplate(w, "layout", reCaptchaSiteKey)

	case "POST":
		firstName := r.PostFormValue("first-name")
		lastName := r.PostFormValue("last-name")
		email := r.PostFormValue("email")
		subscribedToArticles := r.PostFormValue("subscribe-to-articles") == "on"
		subscribedToEvents := r.PostFormValue("subscribe-to-events") == "on"

		var response string

		if reCaptcha.Verify(*r) {
			err := registerUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

			if err == nil {
				response = "Welcome! You are now part of our mailing list."
			} else {
				response = err.Error()
			}
		} else {
			response = "Turing test failed. Please try again."
		}

		templates["message"].ExecuteTemplate(w, "layout", messageModel{"Sign Up", response})

	default:
		errorHandler(w, r, http.StatusBadRequest)
	}
}

func registerUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !subscribedToArticles && !subscribedToEvents {
		return errors.New("Please select at least one subscription.")
	}

	err := databaseService.CreateUser(firstName, lastName, email, subscribedToArticles, subscribedToEvents)

	if err != nil {
		return err
	}

	senderName := "HackSoc"
	senderEmail := os.Getenv("NOREPLY_EMAIL")
	receiverName := firstName + " " + lastName
	subject := "Welcome to HackSoc!"
	message := "You are now part of our mailing list. \n\nTo unsubscribe, click: " + getUnsubscribeLink(email)

	return emailService.Send(senderName, senderEmail, receiverName, email, subject, message)
}

func getUnsubscribeLink(email string) string {
	token, _ := crypto.Encrypt(email)

	return "http://hacksoc.com/unsubscribe?token=" + token
}
<|endoftext|>