<commit_before>package main

import (
	"errors"
	"fmt"
	"encoding/json"
	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type Chaincode struct { }

type ChaincodeFunctions struct {
	stub shim.ChaincodeStubInterface
}

func main() {
	err := shim.Start(new(Chaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t Chaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	_ = stub.PutState("_companies", []byte("[]"))
	return nil, nil
}

func (t Chaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "declareDoc" {
		docType := args[0]
		return fns.DeclareDoc(docType)
	}
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (t Chaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "ping" {
		return fns.Ping()
	} else if function == "getDocs" {
		company := args[0]
		return fns.GetDocsFor(company)
	} else if function == "getDocsForAllCompanies" {
		return fns.GetDocsForAllCompanies()
	}
	return nil, errors.New("Received unknown function query: " + function)
}

// Public Functions

func (c ChaincodeFunctions) Ping() ([]byte, error) {
    return []byte("pong"), nil
}

func (c ChaincodeFunctions) DeclareDoc(docType string) ([]byte, error)  {
	companyBytes, _ := c.stub.ReadCertAttribute("company")
	company := string(companyBytes)
	docs := c.getDocsFromBlockChain(company)
	docs = append(docs, docType)
	c.saveDocsToBlockChain(company, docs)
	c.stub.SetEvent("New Doc Registered", []byte("{\"docType\":\"" + docType + "\"}"))
	return nil, nil
}

func (c ChaincodeFunctions) GetDocsFor(company string) ([]byte, error) {
	docs := c.getDocsFromBlockChain(company)
	docsJson, _ := json.Marshal(docs)
	return docsJson, nil
}

func (c ChaincodeFunctions) GetDocsForAllCompanies() ([]byte, error) {
	allDocs := make(map[string][]string)
	companiesJson, _ := c.stub.GetState("_companies")
	var companies []string
	_ = json.Unmarshal(companiesJson, &companies)
	for _, company := range companies {
		docs := c.getDocsFromBlockChain(company)
		allDocs[company] = docs
	}
	allDocsJson, _ := json.Marshal(allDocs)
	return allDocsJson, nil
}

// Private Functions

func (c ChaincodeFunctions) getDocsFromBlockChain(company string) []string {
	docsJson, _ := c.stub.GetState(company)
	if docsJson == nil {
		return make([]string, 0)
	}
	var docs []string
	_ = json.Unmarshal(docsJson, &docs)
	return docs
}

func (c ChaincodeFunctions) saveDocsToBlockChain(company string, docs []string) {
	companiesJson, _ := c.stub.GetState("_companies")
	var companies []string
	_ = json.Unmarshal(companiesJson, &companies)
	companies = append(companies, company)
	companiesJson, _ = json.Marshal(companies)
	_ = c.stub.PutState("_companies", []byte(companiesJson))
	docsJson, _ := json.Marshal(docs)
	_ = c.stub.PutState(company, []byte(docsJson))
}<commit_msg>batch check-in<commit_after>package main

import (
	"errors"
	"fmt"
	"encoding/json"
	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type Chaincode struct { }

type ChaincodeFunctions struct {
	stub shim.ChaincodeStubInterface
}

func main() {
	err := shim.Start(new(Chaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t Chaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	_ = stub.PutState("_companies", []byte("[]"))
	return nil, nil
}

func (t Chaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "declareDoc" {
		docType := args[0]
		return fns.DeclareDoc(docType)
	}
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (t Chaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "ping" {
		return fns.Ping()
	} else if function == "getDocs" {
		company := args[0]
		return fns.GetDocsFor(company)
	} else if function == "getDocsForAllCompanies" {
		return fns.GetDocsForAllCompanies()
	}
	return nil, errors.New("Received unknown function query: " + function)
}

// Public Functions

func (c ChaincodeFunctions) Ping() ([]byte, error) {
    return []byte("pong"), nil
}

func (c ChaincodeFunctions) DeclareDoc(docType string) ([]byte, error)  {
	companyBytes, _ := c.stub.ReadCertAttribute("company")
	company := string(companyBytes)
	docs := c.getDocsFromBlockChain(company)
	docs = append(docs, docType)
	c.saveDocsToBlockChain(company, docs)
	c.stub.SetEvent("New Doc Registered", []byte("{\"docType\":\"" + docType + "\"}"))
	return nil, nil
}

func (c ChaincodeFunctions) GetDocsFor(company string) ([]byte, error) {
	myCompanyBytes, _ := c.stub.ReadCertAttribute("company")
	roleBytes, _ := c.stub.ReadCertAttribute("role")
	myCompany := string(myCompanyBytes)
	role := string(roleBytes)
	if myCompany != company && role != "regulator" && role != "bank" {
		return nil, errors.New("Not Permitted")
	}
	docs := c.getDocsFromBlockChain(company)
	docsJson, _ := json.Marshal(docs)
	return docsJson, nil
}

func (c ChaincodeFunctions) GetDocsForAllCompanies() ([]byte, error) {
	roleBytes, _ := c.stub.ReadCertAttribute("role")
	role := string(roleBytes)
	if role != "regulator" && role != "bank" {
		return nil, errors.New("Not Permitted")
	}
	allDocs := make(map[string][]string)
	companiesJson, _ := c.stub.GetState("_companies")
	var companies []string
	_ = json.Unmarshal(companiesJson, &companies)
	for _, company := range companies {
		docs := c.getDocsFromBlockChain(company)
		allDocs[company] = docs
	}
	allDocsJson, _ := json.Marshal(allDocs)
	return allDocsJson, nil
}

// Private Functions

func (c ChaincodeFunctions) getDocsFromBlockChain(company string) []string {
	docsJson, _ := c.stub.GetState(company)
	if docsJson == nil {
		return make([]string, 0)
	}
	var docs []string
	_ = json.Unmarshal(docsJson, &docs)
	return docs
}

func (c ChaincodeFunctions) saveDocsToBlockChain(company string, docs []string) {
	companiesJson, _ := c.stub.GetState("_companies")
	var companies []string
	_ = json.Unmarshal(companiesJson, &companies)
	companies = append(companies, company)
	companiesJson, _ = json.Marshal(companies)
	_ = c.stub.PutState("_companies", []byte(companiesJson))
	docsJson, _ := json.Marshal(docs)
	_ = c.stub.PutState(company, []byte(docsJson))
}<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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 (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func getJSON(url string, v interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("failed to fetch %q: %v", url, err)
	}
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(v)
	if err != nil {
		return fmt.Errorf("failed to decode json from %q: %v", url, err)
	}
	return nil
}

func getCurrentOncaller() (string, error) {
	oncaller := struct {
		OnCall struct {
			TestInfra string `json:"testinfra"`
		} `json:"Oncall"`
	}{}

	err := getJSON("https://storage.googleapis.com/kubernetes-jenkins/oncall.json", &oncaller)
	if err != nil {
		return "", err
	}

	return oncaller.OnCall.TestInfra, nil
}

func mapGitHubToSlack(github string) (string, error) {
	// Maps (case-sensitive!) GitHub usernames to Slack user IDs.
	// Add yourself when you join the oncall rotation.
	// You can mostly easily find your user ID by visiting your Slack profile, clicking "...",
	// and clicking "Copy user ID".
	mapping := map[string]string{
		"amwat":       "U9B1P2UGP",
		"BenTheElder": "U1P7T516X",
		"cjwagner":    "U4QFZFMCM",
		"fejta":       "U0E2KHQ13",
		"Katharine":   "UBTBNJ6GL",
		"krzyzacy":    "U22Q65CTG",
	}
	id, ok := mapping[github]
	if !ok {
		return "", fmt.Errorf("couldn't find a Slack ID for GitHub user %q", github)
	}
	return id, nil
}

func updateGroupMembership(token, groupID, userID string) error {
	result := struct {
		Ok    bool   `json:"ok"`
		Error string `json:"error,omitempty"`
	}{}

	err := getJSON("https://slack.com/api/usergroups.users.update?token="+token+"&users="+userID+"&usergroup="+groupID, &result)
	if err != nil {
		return fmt.Errorf("couldn't make membership request: %v", err)
	}

	if !result.Ok {
		return fmt.Errorf("couldn't update membership: %s", result.Error)
	}

	return nil
}

func getTokenFromPath(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", fmt.Errorf("couldn't open file: %v", err)
	}
	return strings.TrimSpace(string(content)), nil
}

type options struct {
	tokenPath string
	groupID   string
}

func parseFlags() options {
	o := options{}
	flag.StringVar(&o.tokenPath, "token-path", "/etc/slack-token", "Path to a file containing the slack token")
	flag.StringVar(&o.groupID, "group-id", "", "Slack group ID")
	flag.Parse()
	return o
}

func main() {
	o := parseFlags()

	token, err := getTokenFromPath(o.tokenPath)
	if err != nil {
		log.Fatalf("Failed to get Slack token: %v", err)
	}

	oncall, err := getCurrentOncaller()
	if err != nil {
		log.Fatalf("Failed to find current oncaller: %v", err)
	}
	log.Printf("Current oncaller: %s\n", oncall)

	userID, err := mapGitHubToSlack(oncall)
	if err != nil {
		log.Fatalf("Failed to get Slack ID: %v", err)
	}
	log.Printf("%s's slack ID: %s\n", oncall, userID)

	log.Printf("Adding slack user %s to slack usergroup %s", userID, o.groupID)
	if err := updateGroupMembership(token, o.groupID, userID); err != nil {
		log.Fatalf("Failed to update usergroup membership: %v", err)
	}
	log.Printf("Done!")
}
<commit_msg>Make the slack oncall updater case-insensitive.<commit_after>/*
Copyright 2019 The Kubernetes 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 (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func getJSON(url string, v interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("failed to fetch %q: %v", url, err)
	}
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(v)
	if err != nil {
		return fmt.Errorf("failed to decode json from %q: %v", url, err)
	}
	return nil
}

func getCurrentOncaller() (string, error) {
	oncaller := struct {
		OnCall struct {
			TestInfra string `json:"testinfra"`
		} `json:"Oncall"`
	}{}

	err := getJSON("https://storage.googleapis.com/kubernetes-jenkins/oncall.json", &oncaller)
	if err != nil {
		return "", err
	}

	return oncaller.OnCall.TestInfra, nil
}

func mapGitHubToSlack(github string) (string, error) {
	// Maps (lowercase!) GitHub usernames to Slack user IDs.
	// Add yourself when you join the oncall rotation.
	// You can mostly easily find your user ID by visiting your Slack profile, clicking "...",
	// and clicking "Copy user ID".
	mapping := map[string]string{
		"amwat":       "U9B1P2UGP",
		"bentheelder": "U1P7T516X",
		"cjwagner":    "U4QFZFMCM",
		"fejta":       "U0E2KHQ13",
		"katharine":   "UBTBNJ6GL",
		"krzyzacy":    "U22Q65CTG",
	}
	id, ok := mapping[strings.ToLower(github)]
	if !ok {
		return "", fmt.Errorf("couldn't find a Slack ID for GitHub user %q", github)
	}
	return id, nil
}

func updateGroupMembership(token, groupID, userID string) error {
	result := struct {
		Ok    bool   `json:"ok"`
		Error string `json:"error,omitempty"`
	}{}

	err := getJSON("https://slack.com/api/usergroups.users.update?token="+token+"&users="+userID+"&usergroup="+groupID, &result)
	if err != nil {
		return fmt.Errorf("couldn't make membership request: %v", err)
	}

	if !result.Ok {
		return fmt.Errorf("couldn't update membership: %s", result.Error)
	}

	return nil
}

func getTokenFromPath(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", fmt.Errorf("couldn't open file: %v", err)
	}
	return strings.TrimSpace(string(content)), nil
}

type options struct {
	tokenPath string
	groupID   string
}

func parseFlags() options {
	o := options{}
	flag.StringVar(&o.tokenPath, "token-path", "/etc/slack-token", "Path to a file containing the slack token")
	flag.StringVar(&o.groupID, "group-id", "", "Slack group ID")
	flag.Parse()
	return o
}

func main() {
	o := parseFlags()

	token, err := getTokenFromPath(o.tokenPath)
	if err != nil {
		log.Fatalf("Failed to get Slack token: %v", err)
	}

	oncall, err := getCurrentOncaller()
	if err != nil {
		log.Fatalf("Failed to find current oncaller: %v", err)
	}
	log.Printf("Current oncaller: %s\n", oncall)

	userID, err := mapGitHubToSlack(oncall)
	if err != nil {
		log.Fatalf("Failed to get Slack ID: %v", err)
	}
	log.Printf("%s's slack ID: %s\n", oncall, userID)

	log.Printf("Adding slack user %s to slack usergroup %s", userID, o.groupID)
	if err := updateGroupMembership(token, o.groupID, userID); err != nil {
		log.Fatalf("Failed to update usergroup membership: %v", err)
	}
	log.Printf("Done!")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"runtime"
	"strings"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	scanner := bufio.NewScanner(os.Stdin)

	s := make(map[string]int, 0)

	for scanner.Scan() {
		somestring := scanner.Text()

		t := strings.Split(somestring, " ")

		for i := 0; i < (len(t) - 1); i++ {
			k1 := t[i] + "##" + t[i+1]
			k2 := t[i+1] + "##" + t[i]

			s[k1] += 1
			s[k2] += 1
		}
	}

	v := make(map[string]map[string]int)

	for key, value := range s {
		keys := strings.Split(key, "##")

		v[keys[0]] = make(map[string]int)

		v[keys[0]][keys[0]] = value
	}

	for key, value := range s {
		keys := strings.Split(key, "##")

		v[keys[0]][keys[1]] = value
	}

	for key, value := range v {
		if len(value) > 1000 {
			fmt.Println(key, value)
		}
	}

	fmt.Println("Goodnight. Ding-ding-ding-ding-ding- ding-ding-ding.")
}
<commit_msg>rm monty python reference<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"runtime"
	"strings"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	scanner := bufio.NewScanner(os.Stdin)

	s := make(map[string]int, 0)

	for scanner.Scan() {
		somestring := scanner.Text()

		t := strings.Split(somestring, " ")

		for i := 0; i < (len(t) - 1); i++ {
			k1 := t[i] + "##" + t[i+1]
			k2 := t[i+1] + "##" + t[i]

			s[k1] += 1
			s[k2] += 1
		}
	}

	v := make(map[string]map[string]int)

	for key, value := range s {
		keys := strings.Split(key, "##")

		v[keys[0]] = make(map[string]int)

		v[keys[0]][keys[0]] = value
	}

	for key, value := range s {
		keys := strings.Split(key, "##")

		v[keys[0]][keys[1]] = value
	}

	for key, value := range v {
		if len(value) > 2000 {
			fmt.Println(key, value)
		}
	}
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"math"
	"sort"

	schema "gopkg.in/raintank/schema.v1"

	"github.com/grafana/metrictank/api/models"
)

type FuncRemoveAboveBelowPercentile struct {
	in    GraphiteFunc
	n     float64
	above bool
}

func NewRemoveAboveBelowPercentileConstructor(above bool) func() GraphiteFunc {
	return func() GraphiteFunc {
		return &FuncRemoveAboveBelowPercentile{above: above}
	}
}

func (s *FuncRemoveAboveBelowPercentile) Signature() ([]Arg, []Arg) {

	return []Arg{
		ArgSeriesList{val: &s.in},
		ArgFloat{key: "n", val: &s.n},
	}, []Arg{ArgSeriesList{}}
}

func (s *FuncRemoveAboveBelowPercentile) Context(context Context) Context {
	return context
}

func (s *FuncRemoveAboveBelowPercentile) Exec(cache map[Req][]models.Series) ([]models.Series, error) {
	series, err := s.in.Exec(cache)
	if err != nil {
		return nil, err
	}

	var output []models.Series
	for _, serie := range series {
		if s.above {
			serie.Target = fmt.Sprintf("removeAbovePercentile(%s, %g)", serie.Target, s.n)
		} else {
			serie.Target = fmt.Sprintf("removeBelowPercentile(%s, %g)", serie.Target, s.n)
		}
		serie.QueryPatt = serie.Target

		out := pointSlicePool.Get().([]schema.Point)
		for _, p := range serie.Datapoints {
			if s.above {
				if p.Val > s.n {
					p.Val = math.NaN()
				}
			} else {
				if p.Val < s.n {
					p.Val = math.NaN()
				}
			}
			out = append(out, p)
		}
		serie.Datapoints = out
		output = append(output, serie)
	}

	cache[Req{}] = append(cache[Req{}], output...)

	return output, nil
}

func getPercentileValue(datapoints []schema.Point, n float64) float64 {
	sortedDatapoints := make([]schema.Point, len(datapoints))
	for _, p := range datapoints {
		if !math.IsNaN(p.Val) {
			sortedDatapoints = append(sortedDatapoints, p)
		}
	}

	sort.Slice(sortedDatapoints, func(i, j int) bool {
		return sortedDatapoints[i].Val < sortedDatapoints[j].Val
	})

	index := math.Ceil(n/100.0*float64(len(sortedDatapoints)+1)) - 1
	return sortedDatapoints[int(index)].Val
}
<commit_msg>add error<commit_after>package expr

import (
	"errors"
	"fmt"
	"math"
	"sort"

	schema "gopkg.in/raintank/schema.v1"

	"github.com/grafana/metrictank/api/models"
)

type FuncRemoveAboveBelowPercentile struct {
	in    GraphiteFunc
	n     float64
	above bool
}

func NewRemoveAboveBelowPercentileConstructor(above bool) func() GraphiteFunc {
	return func() GraphiteFunc {
		return &FuncRemoveAboveBelowPercentile{above: above}
	}
}

func (s *FuncRemoveAboveBelowPercentile) Signature() ([]Arg, []Arg) {

	return []Arg{
		ArgSeriesList{val: &s.in},
		ArgFloat{key: "n", val: &s.n},
	}, []Arg{ArgSeriesList{}}
}

func (s *FuncRemoveAboveBelowPercentile) Context(context Context) Context {
	return context
}

func (s *FuncRemoveAboveBelowPercentile) Exec(cache map[Req][]models.Series) ([]models.Series, error) {
	series, err := s.in.Exec(cache)
	if err != nil {
		return nil, err
	}

	if s.n <= 0 {
		return nil, errors.New("The requested percent is required to be greater than 0")
	}

	var output []models.Series
	for _, serie := range series {
		if s.above {
			serie.Target = fmt.Sprintf("removeAbovePercentile(%s, %g)", serie.Target, s.n)
		} else {
			serie.Target = fmt.Sprintf("removeBelowPercentile(%s, %g)", serie.Target, s.n)
		}
		serie.QueryPatt = serie.Target

		out := pointSlicePool.Get().([]schema.Point)
		for _, p := range serie.Datapoints {
			if s.above {
				if p.Val > s.n {
					p.Val = math.NaN()
				}
			} else {
				if p.Val < s.n {
					p.Val = math.NaN()
				}
			}
			out = append(out, p)
		}
		serie.Datapoints = out
		output = append(output, serie)
	}

	cache[Req{}] = append(cache[Req{}], output...)

	return output, nil
}

func getPercentileValue(datapoints []schema.Point, n float64) float64 {
	sortedDatapoints := make([]schema.Point, len(datapoints))
	for _, p := range datapoints {
		if !math.IsNaN(p.Val) {
			sortedDatapoints = append(sortedDatapoints, p)
		}
	}

	sort.Slice(sortedDatapoints, func(i, j int) bool {
		return sortedDatapoints[i].Val < sortedDatapoints[j].Val
	})

	index := math.Ceil(n/100.0*float64(len(sortedDatapoints)+1)) - 1
	return sortedDatapoints[int(index)].Val
}
<|endoftext|>


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

package local

import (
	"fmt"

	"launchpad.net/loggo"

	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/utils"
	"launchpad.net/juju-core/version"
)

var logger = loggo.GetLogger("juju.environs.local")

var _ environs.EnvironProvider = (*environProvider)(nil)

type environProvider struct{}

var provider environProvider

func init() {
	environs.RegisterProvider("local", &environProvider{})
}

// Open implements environs.EnvironProvider.Open.
func (environProvider) Open(cfg *config.Config) (env environs.Environ, err error) {
	logger.Infof("opening environment %q", cfg.Name())
	if _, ok := cfg.AgentVersion(); !ok {
		cfg, err = cfg.Apply(map[string]interface{}{
			"agent-version": version.CurrentNumber().String(),
		})
		if err != nil {
			return nil, err
		}
	}
	environ := &localEnviron{name: cfg.Name()}
	err = environ.SetConfig(cfg)
	if err != nil {
		logger.Errorf("failure setting config: %v", err)
		return nil, err
	}
	return environ, nil
}

// Validate implements environs.EnvironProvider.Validate.
func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	v, err := configChecker.Coerce(cfg.UnknownAttrs(), nil)
	if err != nil {
		return nil, err
	}
	localConfig := newEnvironConfig(cfg, v.(map[string]interface{}))
	// Before potentially creating directories, make sure that the
	// root directory has not changed.
	if old != nil {
		oldLocalConfig, err := provider.newConfig(old)
		if err != nil {
			return nil, fmt.Errorf("old config is not a valid local config: %v", old)
		}
		if localConfig.rootDir() != oldLocalConfig.rootDir() {
			return nil, fmt.Errorf("cannot change root-dir from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
	}
	dir := utils.NormalizePath(localConfig.rootDir())
	if dir == "." {
		dir = config.JujuHomePath(cfg.Name())
		localConfig.attrs["root-dir"] = dir
	}

	// Apply the coerced unknown values back into the config.
	return cfg.Apply(localConfig.attrs)
}

// BoilerplateConfig implements environs.EnvironProvider.BoilerplateConfig.
func (environProvider) BoilerplateConfig() string {
	return `
## https://juju.ubuntu.com/get-started/local/
local:
  type: local
  # Override the directory that is used for the storage files and database.
  # The default location is $JUJU_HOME/<ENV>.
  # $JUJU_HOME defaults to ~/.juju
  # root-dir: ~/.juju/local

`[1:]
}

// SecretAttrs implements environs.EnvironProvider.SecretAttrs.
func (environProvider) SecretAttrs(cfg *config.Config) (map[string]interface{}, error) {
	// don't have any secret attrs
	return nil, nil
}

// Location specific methods that are able to be called by any instance that
// has been created by this provider type.  So a machine agent may well call
// these methods to find out its own address or instance id.

// PublicAddress implements environs.EnvironProvider.PublicAddress.
func (environProvider) PublicAddress() (string, error) {
	return "", fmt.Errorf("not implemented")
}

// PrivateAddress implements environs.EnvironProvider.PrivateAddress.
func (environProvider) PrivateAddress() (string, error) {
	return "", fmt.Errorf("not implemented")
}

// InstanceId implements environs.EnvironProvider.InstanceId.
func (environProvider) InstanceId() (instance.Id, error) {
	// This hack only works until we get containers started.
	return instance.Id("localhost"), nil
	// return "", fmt.Errorf("not implemented")
}

func (environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
	valid, err := provider.Validate(cfg, nil)
	if err != nil {
		return nil, err
	}
	return newEnvironConfig(valid, valid.UnknownAttrs()), nil
}
<commit_msg>Remove old line.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local

import (
	"fmt"

	"launchpad.net/loggo"

	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/utils"
	"launchpad.net/juju-core/version"
)

var logger = loggo.GetLogger("juju.environs.local")

var _ environs.EnvironProvider = (*environProvider)(nil)

type environProvider struct{}

var provider environProvider

func init() {
	environs.RegisterProvider("local", &environProvider{})
}

// Open implements environs.EnvironProvider.Open.
func (environProvider) Open(cfg *config.Config) (env environs.Environ, err error) {
	logger.Infof("opening environment %q", cfg.Name())
	if _, ok := cfg.AgentVersion(); !ok {
		cfg, err = cfg.Apply(map[string]interface{}{
			"agent-version": version.CurrentNumber().String(),
		})
		if err != nil {
			return nil, err
		}
	}
	environ := &localEnviron{name: cfg.Name()}
	err = environ.SetConfig(cfg)
	if err != nil {
		logger.Errorf("failure setting config: %v", err)
		return nil, err
	}
	return environ, nil
}

// Validate implements environs.EnvironProvider.Validate.
func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	v, err := configChecker.Coerce(cfg.UnknownAttrs(), nil)
	if err != nil {
		return nil, err
	}
	localConfig := newEnvironConfig(cfg, v.(map[string]interface{}))
	// Before potentially creating directories, make sure that the
	// root directory has not changed.
	if old != nil {
		oldLocalConfig, err := provider.newConfig(old)
		if err != nil {
			return nil, fmt.Errorf("old config is not a valid local config: %v", old)
		}
		if localConfig.rootDir() != oldLocalConfig.rootDir() {
			return nil, fmt.Errorf("cannot change root-dir from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
	}
	dir := utils.NormalizePath(localConfig.rootDir())
	if dir == "." {
		dir = config.JujuHomePath(cfg.Name())
		localConfig.attrs["root-dir"] = dir
	}

	// Apply the coerced unknown values back into the config.
	return cfg.Apply(localConfig.attrs)
}

// BoilerplateConfig implements environs.EnvironProvider.BoilerplateConfig.
func (environProvider) BoilerplateConfig() string {
	return `
## https://juju.ubuntu.com/get-started/local/
local:
  type: local
  # Override the directory that is used for the storage files and database.
  # The default location is $JUJU_HOME/<ENV>.
  # $JUJU_HOME defaults to ~/.juju
  # root-dir: ~/.juju/local

`[1:]
}

// SecretAttrs implements environs.EnvironProvider.SecretAttrs.
func (environProvider) SecretAttrs(cfg *config.Config) (map[string]interface{}, error) {
	// don't have any secret attrs
	return nil, nil
}

// Location specific methods that are able to be called by any instance that
// has been created by this provider type.  So a machine agent may well call
// these methods to find out its own address or instance id.

// PublicAddress implements environs.EnvironProvider.PublicAddress.
func (environProvider) PublicAddress() (string, error) {
	return "", fmt.Errorf("not implemented")
}

// PrivateAddress implements environs.EnvironProvider.PrivateAddress.
func (environProvider) PrivateAddress() (string, error) {
	return "", fmt.Errorf("not implemented")
}

// InstanceId implements environs.EnvironProvider.InstanceId.
func (environProvider) InstanceId() (instance.Id, error) {
	// This hack only works until we get containers started.
	return instance.Id("localhost"), nil
}

func (environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
	valid, err := provider.Validate(cfg, nil)
	if err != nil {
		return nil, err
	}
	return newEnvironConfig(valid, valid.UnknownAttrs()), nil
}
<|endoftext|>


<commit_before>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event

// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check function that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// Retry returns a new check function that change the state to "critical" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) CriticalIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "critical"
			return result
		}
		return result
	}
}

// Retry returns a new check function that change the state to "warning" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) WarningIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "warning"
			return result
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

// BodyGreaterThan return a function that given a http response return true if the body is greater than a given number of bytes
func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

// NewGenercicHTTPChecker returns a check function that can check the returned http response of a http get with a given validation function
func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

// NewSnmpChecker returns a check function that check if a host respond to a snmp get query
func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewC4CMTSTempChecker returns a check function that check if any of the slot of a Arris C4 CMTS have a temperature above a given max
func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

// NewJuniperTempChecker returns a check function that check if a Juniper device (router, switch, etc) have a temperature above a given max
func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewJuniperCPUChecker returns a check function that check if a Juniper device (router, switch, etc) have a any cpu usage above a given percent
func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedCpuPercent uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedCpuPercent {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewRabbitMQQueueLenCheck returns a check function that check if queue have more pending messages than a given limit
func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

// NewMysqlConnectionCheck returns a check function to detect connection/credentials problems to connect to mysql
func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

// ObtainMetricFunction function that return a metric value
type ObtainMetricFunction func() float32

// CalculateStateFunction function that given a metric generate the corresponding state value
type CalculateStateFunction func(float32) string

// NewGeneriocCheck returns a check function that invoke a given function to obtain a metric (metricFunc) and
// invoke another function (stateFunc) to calculate the resulting state and description from this metric value
func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<commit_msg>Added some doc<commit_after>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event

// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check function that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// Retry returns a new check function that change the state to "critical" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) CriticalIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "critical"
			return result
		}
		return result
	}
}

// Retry returns a new check function that change the state to "warning" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) WarningIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "warning"
			return result
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

// BodyGreaterThan return a function that given a http response return true if the body is greater than a given number of bytes
func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

// NewGenercicHTTPChecker returns a check function that can check the returned http response of a http get with a given validation function
func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

// SnmpCheckConf snmp connection parameters to use for the check
type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

// DefaultSnmpCheckConf default values for snmp conection parameters
var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

// NewSnmpChecker returns a check function that check if a host respond to a snmp get query
func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewC4CMTSTempChecker returns a check function that check if any of the slot of a Arris C4 CMTS have a temperature above a given max
func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

// NewJuniperTempChecker returns a check function that check if a Juniper device (router, switch, etc) have a temperature above a given max
func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewJuniperCPUChecker returns a check function that check if a Juniper device (router, switch, etc) have a any cpu usage above a given percent
func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedCpuPercent uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedCpuPercent {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewRabbitMQQueueLenCheck returns a check function that check if queue have more pending messages than a given limit
func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

// NewMysqlConnectionCheck returns a check function to detect connection/credentials problems to connect to mysql
func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

// ObtainMetricFunction function that return a metric value
type ObtainMetricFunction func() float32

// CalculateStateFunction function that given a metric generate the corresponding state value
type CalculateStateFunction func(float32) string

// NewGeneriocCheck returns a check function that invoke a given function to obtain a metric (metricFunc) and
// invoke another function (stateFunc) to calculate the resulting state and description from this metric value
func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<|endoftext|>


<commit_before>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event

// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

// BodyGreaterThan return a function that given a http response return true if the body is greater than a given number of bytes
func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

// NewGenercicHTTPChecker returns a check function that can check the returned http response of a http get with a given validation function
func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

// NewSnmpChecker returns a check function that check if a host respond to a snmp get query
func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewC4CMTSTempChecker returns a check function that check if any of the slot of a Arris C4 CMTS have a temperature above a given max
func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

// NewJuniperTempChecker returns a check function that check if a Juniper device (router, switch, etc) have a temperature above a given max
func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewJuniperCPUChecker returns a check function that check if a Juniper device (router, switch, etc) have a any cpu usage above a given percent
func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedCpuPercent uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedCpuPercent {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewRabbitMQQueueLenCheck returns a check function that check if queue have more pending messages than a given limit
func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

// NewMysqlConnectionCheck returns a check function to detect connection/credentials problems to connect to mysql
func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

// NewGeneriocCheck returns a check function that invoke a given function to obtain a metric (metricFunc) and
// invoke another function (stateFunc) to calculate the resulting state and description from this metric value
func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<commit_msg>Helpers to check threadhold for metrics<commit_after>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event

// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check function that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// Retry returns a new check function that change the state to "critical" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) CriticalIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "critical"
			return result
		}
		return result
	}
}

// Retry returns a new check function that change the state to "warning" when the resulting metric is greater than a
// threadshold and is not already "critical"
func (f CheckFunction) WarningIfGreaterThan(threshold float32) CheckFunction {
	return func() Event {
		var result Event
		result = f()
		if result.State == "critical" {
			return result
		}
		if result.Metric.(float32) > threshold {
			result.State = "warning"
			return result
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

// BodyGreaterThan return a function that given a http response return true if the body is greater than a given number of bytes
func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

// NewGenercicHTTPChecker returns a check function that can check the returned http response of a http get with a given validation function
func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

// NewSnmpChecker returns a check function that check if a host respond to a snmp get query
func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewC4CMTSTempChecker returns a check function that check if any of the slot of a Arris C4 CMTS have a temperature above a given max
func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

// NewJuniperTempChecker returns a check function that check if a Juniper device (router, switch, etc) have a temperature above a given max
func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewJuniperCPUChecker returns a check function that check if a Juniper device (router, switch, etc) have a any cpu usage above a given percent
func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedCpuPercent uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedCpuPercent {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewRabbitMQQueueLenCheck returns a check function that check if queue have more pending messages than a given limit
func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

// NewMysqlConnectionCheck returns a check function to detect connection/credentials problems to connect to mysql
func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

// NewGeneriocCheck returns a check function that invoke a given function to obtain a metric (metricFunc) and
// invoke another function (stateFunc) to calculate the resulting state and description from this metric value
func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 healthchecker

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/mesos/mesos-go/upid"
	"github.com/stretchr/testify/assert"
)

type thresholdMonitor struct {
	cnt       int32
	threshold int32
}

func newThresholdMonitor(threshold int) *thresholdMonitor {
	return &thresholdMonitor{threshold: int32(threshold)}
}

// incAndTest returns true if the threshold is reached.
func (t *thresholdMonitor) incAndTest() bool {
	if atomic.AddInt32(&t.cnt, 1) >= t.threshold {
		return false
	}
	return true
}

// blockedServer replies only threshold times, after that
// it will block.
type blockedServer struct {
	th *thresholdMonitor
	ch chan struct{}
}

func newBlockedServer(threshold int) *blockedServer {
	return &blockedServer{
		th: newThresholdMonitor(threshold),
		ch: make(chan struct{}),
	}
}

func (s *blockedServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	<-s.ch
}

func (s *blockedServer) stop() {
	close(s.ch)
}

// eofServer will close the connection after it replies for threshold times.
// Thus the health checker will get an EOF error.
type eofServer struct {
	th *thresholdMonitor
}

func newEOFServer(threshold int) *eofServer {
	return &eofServer{newThresholdMonitor(threshold)}
}

func (s *eofServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	hj := w.(http.Hijacker)
	conn, _, err := hj.Hijack()
	if err != nil {
		panic("Cannot hijack")
	}
	conn.Close()
}

// errorStatusCodeServer will reply error status code (e.g. 503) after the
// it replies for threhold time.
type errorStatusCodeServer struct {
	th *thresholdMonitor
}

func newErrorStatusServer(threshold int) *errorStatusCodeServer {
	return &errorStatusCodeServer{newThresholdMonitor(threshold)}
}

func (s *errorStatusCodeServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	w.WriteHeader(http.StatusServiceUnavailable)
}

// goodServer always returns status ok.
type goodServer bool

func (s *goodServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {}

// partitionedServer returns status ok at some first requests.
// Then it will block for a while, and then reply again.
type partitionedServer struct {
	healthyCnt   int32
	partitionCnt int32
	cnt          int32
	mutex        *sync.Mutex
	cond         *sync.Cond
}

func newPartitionedServer(healthyCnt, partitionCnt int) *partitionedServer {
	mutex := new(sync.Mutex)
	cond := sync.NewCond(mutex)
	return &partitionedServer{
		healthyCnt:   int32(healthyCnt),
		partitionCnt: int32(partitionCnt),
		mutex:        mutex,
		cond:         cond,
	}
}

func (s *partitionedServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	cnt := atomic.AddInt32(&s.cnt, 1)
	if cnt < s.healthyCnt {
		return
	}
	if cnt < s.healthyCnt+s.partitionCnt {
		s.mutex.Lock()
		defer s.mutex.Unlock()
		s.cond.Wait()
		return
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.cond.Broadcast()
}

func TestSlaveHealthCheckerFailedOnBlockedSlave(t *testing.T) {
	s := newBlockedServer(5)
	ts := httptest.NewUnstartedServer(s)
	go ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		s.stop()
		t.Fatal("timeout")
	case <-ch:
		s.stop()
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerFailedOnEOFSlave(t *testing.T) {
	s := newEOFServer(5)
	ts := httptest.NewUnstartedServer(s)
	go ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		t.Fatal("timeout")
	case <-ch:
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerFailedOnErrorStatusSlave(t *testing.T) {
	s := newErrorStatusServer(5)
	ts := httptest.NewUnstartedServer(s)
	go ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		t.Fatal("timeout")
	case <-ch:
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerSucceed(t *testing.T) {
	s := new(goodServer)
	ts := httptest.NewUnstartedServer(s)
	go ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		assert.Equal(t, 0, checker.continuousUnhealthyCount)
	case <-ch:
		t.Fatal("Shouldn't get unhealthy notification")
	}
}

func TestSlaveHealthCheckerPartitonedSlave(t *testing.T) {
	s := newPartitionedServer(5, 9)
	ts := httptest.NewUnstartedServer(s)
	go ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		assert.Equal(t, 0, checker.continuousUnhealthyCount)
	case <-ch:
		t.Fatal("Shouldn't get unhealthy notification")
	}
}
<commit_msg>healthchecker: run ts.Start() in main goroutine<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 healthchecker

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/mesos/mesos-go/upid"
	"github.com/stretchr/testify/assert"
)

type thresholdMonitor struct {
	cnt       int32
	threshold int32
}

func newThresholdMonitor(threshold int) *thresholdMonitor {
	return &thresholdMonitor{threshold: int32(threshold)}
}

// incAndTest returns true if the threshold is reached.
func (t *thresholdMonitor) incAndTest() bool {
	if atomic.AddInt32(&t.cnt, 1) >= t.threshold {
		return false
	}
	return true
}

// blockedServer replies only threshold times, after that
// it will block.
type blockedServer struct {
	th *thresholdMonitor
	ch chan struct{}
}

func newBlockedServer(threshold int) *blockedServer {
	return &blockedServer{
		th: newThresholdMonitor(threshold),
		ch: make(chan struct{}),
	}
}

func (s *blockedServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	<-s.ch
}

func (s *blockedServer) stop() {
	close(s.ch)
}

// eofServer will close the connection after it replies for threshold times.
// Thus the health checker will get an EOF error.
type eofServer struct {
	th *thresholdMonitor
}

func newEOFServer(threshold int) *eofServer {
	return &eofServer{newThresholdMonitor(threshold)}
}

func (s *eofServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	hj := w.(http.Hijacker)
	conn, _, err := hj.Hijack()
	if err != nil {
		panic("Cannot hijack")
	}
	conn.Close()
}

// errorStatusCodeServer will reply error status code (e.g. 503) after the
// it replies for threhold time.
type errorStatusCodeServer struct {
	th *thresholdMonitor
}

func newErrorStatusServer(threshold int) *errorStatusCodeServer {
	return &errorStatusCodeServer{newThresholdMonitor(threshold)}
}

func (s *errorStatusCodeServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.th.incAndTest() {
		return
	}
	w.WriteHeader(http.StatusServiceUnavailable)
}

// goodServer always returns status ok.
type goodServer bool

func (s *goodServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {}

// partitionedServer returns status ok at some first requests.
// Then it will block for a while, and then reply again.
type partitionedServer struct {
	healthyCnt   int32
	partitionCnt int32
	cnt          int32
	mutex        *sync.Mutex
	cond         *sync.Cond
}

func newPartitionedServer(healthyCnt, partitionCnt int) *partitionedServer {
	mutex := new(sync.Mutex)
	cond := sync.NewCond(mutex)
	return &partitionedServer{
		healthyCnt:   int32(healthyCnt),
		partitionCnt: int32(partitionCnt),
		mutex:        mutex,
		cond:         cond,
	}
}

func (s *partitionedServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	cnt := atomic.AddInt32(&s.cnt, 1)
	if cnt < s.healthyCnt {
		return
	}
	if cnt < s.healthyCnt+s.partitionCnt {
		s.mutex.Lock()
		defer s.mutex.Unlock()
		s.cond.Wait()
		return
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.cond.Broadcast()
}

func TestSlaveHealthCheckerFailedOnBlockedSlave(t *testing.T) {
	s := newBlockedServer(5)
	ts := httptest.NewUnstartedServer(s)
	ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		s.stop()
		t.Fatal("timeout")
	case <-ch:
		s.stop()
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerFailedOnEOFSlave(t *testing.T) {
	s := newEOFServer(5)
	ts := httptest.NewUnstartedServer(s)
	ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		t.Fatal("timeout")
	case <-ch:
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerFailedOnErrorStatusSlave(t *testing.T) {
	s := newErrorStatusServer(5)
	ts := httptest.NewUnstartedServer(s)
	ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		t.Fatal("timeout")
	case <-ch:
		assert.True(t, atomic.LoadInt32(&s.th.cnt) > 10)
	}
}

func TestSlaveHealthCheckerSucceed(t *testing.T) {
	s := new(goodServer)
	ts := httptest.NewUnstartedServer(s)
	ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		assert.Equal(t, 0, checker.continuousUnhealthyCount)
	case <-ch:
		t.Fatal("Shouldn't get unhealthy notification")
	}
}

func TestSlaveHealthCheckerPartitonedSlave(t *testing.T) {
	s := newPartitionedServer(5, 9)
	ts := httptest.NewUnstartedServer(s)
	ts.Start()
	defer ts.Close()

	upid, err := upid.Parse(fmt.Sprintf("slave@%s", ts.Listener.Addr().String()))
	assert.NoError(t, err)

	checker := NewSlaveHealthChecker(upid, 10, time.Millisecond*10, time.Millisecond*10)
	ch := checker.Start()
	defer checker.Stop()

	select {
	case <-time.After(time.Second):
		assert.Equal(t, 0, checker.continuousUnhealthyCount)
	case <-ch:
		t.Fatal("Shouldn't get unhealthy notification")
	}
}
<|endoftext|>