<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package xurls

import "regexp"

//go:generate go run generate/tldsgen/main.go
//go:generate go run generate/regexgen/main.go

const (
	letters   = "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"
	iriChar   = letters + `0-9`
	pathChar  = iriChar + `/\-+_@&=#$~*%.,:;'"()?!`
	endChar   = iriChar + `/\-+_@&=#$~*%`
	octet     = `(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])`
	ipv4Addr  = `\b` + octet + `\.` + octet + `\.` + octet + `\.` + octet + `\b`
	ipv6Addr  = `([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:[0-9a-fA-F]{0,4}|:[0-9a-fA-F]{1,4})?|(:[0-9a-fA-F]{1,4}){0,2})|(:[0-9a-fA-F]{1,4}){0,3})|(:[0-9a-fA-F]{1,4}){0,4})|:(:[0-9a-fA-F]{1,4}){0,5})((:[0-9a-fA-F]{1,4}){2}|:(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])){3})|(([0-9a-fA-F]{1,4}:){1,6}|:):[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){7}:`
	ipAddr    = `(` + ipv4Addr + `|` + ipv6Addr + `)`
	iri       = `[` + iriChar + `]([` + iriChar + `\-]*[` + iriChar + `])?`
	domain    = `(` + iri + `\.)+`
	hostName  = `(` + domain + gtld + `|` + ipAddr + `)`
	wellParen = `([` + pathChar + `]*\([` + pathChar + `]*\))`
	pathCont  = `(` + wellParen + `|[` + pathChar + `]*[` + endChar + `])`
	path      = `(/` + pathCont + `?|\b|$)`
	webURL    = hostName + `(:[0-9]*)?` + path
	email     = `[a-zA-Z0-9._%\-+]+@` + hostName

	comScheme = `[a-zA-Z][a-zA-Z.\-+]*://`
	scheme    = `(` + comScheme + `|` + otherScheme + `)`
	strict    = `\b` + scheme + pathCont
	relaxed   = strict + `|` + webURL + `|` + email
)

var (
	// Relaxed matches all the urls it can find
	Relaxed = regexp.MustCompile(relaxed)
	// Strict only matches urls with a scheme to avoid false positives
	Strict = regexp.MustCompile(strict)
)

func init() {
	Relaxed.Longest()
	Strict.Longest()
}

// StrictMatching produces a regexp that matches urls like Strict but matching
// a specified scheme regular expression
func StrictMatching(schemeExp string) (*regexp.Regexp, error) {
	strictMatching := `\b(` + schemeExp + `)` + pathCont
	re, err := regexp.Compile(strictMatching)
	if err != nil {
		return nil, err
	}
	re.Longest()
	return re, nil
}
<commit_msg>Remove # and = as valid end chars<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package xurls

import "regexp"

//go:generate go run generate/tldsgen/main.go
//go:generate go run generate/regexgen/main.go

const (
	letters   = "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"
	iriChar   = letters + `0-9`
	pathChar  = iriChar + `/\-+_@&=#$~*%.,:;'"()?!`
	endChar   = iriChar + `/\-+_@&$~*%`
	octet     = `(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])`
	ipv4Addr  = `\b` + octet + `\.` + octet + `\.` + octet + `\.` + octet + `\b`
	ipv6Addr  = `([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:[0-9a-fA-F]{0,4}|:[0-9a-fA-F]{1,4})?|(:[0-9a-fA-F]{1,4}){0,2})|(:[0-9a-fA-F]{1,4}){0,3})|(:[0-9a-fA-F]{1,4}){0,4})|:(:[0-9a-fA-F]{1,4}){0,5})((:[0-9a-fA-F]{1,4}){2}|:(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])){3})|(([0-9a-fA-F]{1,4}:){1,6}|:):[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){7}:`
	ipAddr    = `(` + ipv4Addr + `|` + ipv6Addr + `)`
	iri       = `[` + iriChar + `]([` + iriChar + `\-]*[` + iriChar + `])?`
	domain    = `(` + iri + `\.)+`
	hostName  = `(` + domain + gtld + `|` + ipAddr + `)`
	wellParen = `([` + pathChar + `]*\([` + pathChar + `]*\))`
	pathCont  = `(` + wellParen + `|[` + pathChar + `]*[` + endChar + `])`
	path      = `(/` + pathCont + `?|\b|$)`
	webURL    = hostName + `(:[0-9]*)?` + path
	email     = `[a-zA-Z0-9._%\-+]+@` + hostName

	comScheme = `[a-zA-Z][a-zA-Z.\-+]*://`
	scheme    = `(` + comScheme + `|` + otherScheme + `)`
	strict    = `\b` + scheme + pathCont
	relaxed   = strict + `|` + webURL + `|` + email
)

var (
	// Relaxed matches all the urls it can find
	Relaxed = regexp.MustCompile(relaxed)
	// Strict only matches urls with a scheme to avoid false positives
	Strict = regexp.MustCompile(strict)
)

func init() {
	Relaxed.Longest()
	Strict.Longest()
}

// StrictMatching produces a regexp that matches urls like Strict but matching
// a specified scheme regular expression
func StrictMatching(schemeExp string) (*regexp.Regexp, error) {
	strictMatching := `\b(` + schemeExp + `)` + pathCont
	re, err := regexp.Compile(strictMatching)
	if err != nil {
		return nil, err
	}
	re.Longest()
	return re, nil
}
<|endoftext|>


<commit_before>package plugins

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"

	"github.com/InnovaCo/serve/manifest"
	"github.com/InnovaCo/serve/utils/gabs"
)

func init() {
	manifest.PluginRegestry.Add("gocd.pipeline.create", goCdPipelineCreate{})
}

/**
 * plugin for manifest section "goCd.pipeline.create"
 * section structure:
 *
 * goCd.pipeline.create:
 *   api-url: goCd_URL
 *   environment: ENV
 *   branch: BRANCH
 *   allowed-branches: [BRANCH, ...]
 *   pipeline:
 *     group: GROUP
 *     pipeline:
 *       according to the description: https://api.go.cd/current/#the-pipeline-config-object
 */

type goCdCredents struct {
	Login    string `json:"login"`
	Password string `json:"password"`
}

type goCdPipelineCreate struct{}

func (p goCdPipelineCreate) Run(data manifest.Manifest) error {
	name := data.GetString("pipeline.pipeline.name")
	url := data.GetString("api-url")
	body := data.GetTree("pipeline").String()
	branch := data.GetString("branch")

	m := false
	for _, b := range data.GetArray("allowed-branches") {
		re := b.Unwrap().(string)
		if re == branch {
			m = true
			break
		} else if m, _ = regexp.MatchString(re, branch); m {
			break
		}
	}

	if !m {
		log.Println("branch ", branch, " not in ", data.GetString("allowed-branches"))
		return nil
	}

	resp, err := goCdRequest("GET", url+"/pipelines/"+name, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode == http.StatusOK {
		err = goCdUpdate(name, data.GetString("environment"), url, body, map[string]string{"If-Match": resp.Header.Get("ETag")})
	} else if resp.StatusCode == http.StatusNotFound {
		err = goCdCreate(name, data.GetString("environment"), url, body, nil)
	} else {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	if err != nil {
		log.Println(err)
		return err
	}

	return nil
}

func goCdCreate(name string, env string, resource string, body string, headers map[string]string) error {
	if resp, err := goCdRequest("POST", resource+"/pipelines", body, nil); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}
	data, tag, err := goCdChangeEnv(resource, env, name, "")
	if err != nil {
		log.Println(err)
		return err
	}

	log.Println(data)

	if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	if resp, err := goCdRequest("POST", resource+"/pipelines/"+name+"/unpause", body, map[string]string{"Confirm": "true"}); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdUpdate(name string, env string, resource string, body string, headers map[string]string) error {
	if resp, err := goCdRequest("PUT", resource+"/pipelines/"+name, body, headers); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	cEnv, err := goCdFindEnv(resource, name)
	if err != nil {
		return err
	}

	if env != cEnv {
		data, tag, err := goCdChangeEnv(resource, cEnv, "", name)
		if err != nil {
			log.Println(err)
			return err
		}

		log.Println(data)

		if resp, err := goCdRequest("PUT", resource+"/environments/"+cEnv, data, map[string]string{"If-Match": tag}); err != nil {
			log.Println(err)
			return err
		} else if resp.StatusCode != http.StatusOK {
			log.Println("Operation error: " + resp.Status)
			return errors.New("Operation error: " + resp.Status)
		}
		//
		data, tag, err = goCdChangeEnv(resource, env, name, "")
		if err != nil {
			log.Println(err)
			return err
		}

		log.Println(data)

		if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
			log.Println(err)
			return err
		} else if resp.StatusCode != http.StatusOK {
			log.Println("Operation error: " + resp.Status)
			return errors.New("Operation error: " + resp.Status)
		}
	}

	if resp, err := goCdRequest("POST", resource+"/pipelines/"+name+"/unpause", body, map[string]string{"Confirm": "true"}); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdDelete(name string, env string, resource string) error {
	data, tag, err := goCdChangeEnv(resource, env, "", name)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Println(data)

	if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	if resp, err := goCdRequest("DELETE", resource+"/pipelines/"+name, "", nil); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdChangeEnv(resource string, env string, addPipeline string, delPipeline string) (string, string, error) {
	resp, err := goCdRequest("GET", resource+"/environments/"+env, "", nil)
	if err != nil {
		log.Println(err)
		return "", "", err
	}

	data, err := ChangeJSON(resp, addPipeline, delPipeline)
	if err != nil {
		log.Println(err)
		return "", "", err
	}

	return data, resp.Header.Get("ETag"), nil
}

func goCdFindEnv(resource string, pipeline string) (string, error) {
	resp, err := goCdRequest("GET", resource+"/environments", "", nil)
	if err != nil {
		log.Println(err)
		return "", err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", errors.New("parse error")
	}

	tree, err := gabs.ParseJSON(body)
	if err != nil {
		return "", errors.New("parse error")
	}

	envs, _ := tree.Path("_embedded.environments").Children()
	for _, env := range envs {
		envName := env.Path("name").Data().(string)
		pipelines, _ := env.Path("pipelines").Children()
		for _, pline := range pipelines {
			if pline.Path("name").Data().(string) == pipeline {
				return envName, nil
			}
		}
	}

	return "", nil
}

func goCdRequest(method string, resource string, body string, headers map[string]string) (*http.Response, error) {
	req, _ := http.NewRequest(method, resource, bytes.NewReader([]byte(body)))

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	req.Header.Set("Accept", "application/vnd.go.cd.v2+json")
	req.Header.Set("Content-Type", "application/json")

	data, err := ioutil.ReadFile("/etc/serve/gocd_credentials")
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Credentias file error: %v", err))
	}

	creds := &goCdCredents{}
	json.Unmarshal(data, creds)

	req.SetBasicAuth(creds.Login, creds.Password)

	log.Printf(" --> %s %s:\n%s\n%s\n", method, resource, req.Header, body)

	return http.DefaultClient.Do(req)
}

func ChangeJSON(resp *http.Response, addPipeline string, delPipeline string) (string, error) {
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return "", errors.New("read body error")
	}

	tree, err := gabs.ParseJSON(body)
	if err != nil {
		return "", errors.New("parse error")
	}
	result := gabs.New()

	result.Set(tree.Path("name").Data(), "name")

	children, _ := tree.S("pipelines").Children()
	vals := []map[string]string{}
	for _, m := range children {
		name := m.Path("name").Data().(string)
		if (delPipeline != "") && (name == delPipeline) {
			continue
		}
		if (addPipeline != "") && (name == addPipeline) {
			addPipeline = ""
		}
		vals = append(vals, map[string]string{"name": name})
	}
	if addPipeline != "" {
		vals = append(vals, map[string]string{"name": addPipeline})
	}
	result.Set(vals, "pipelines")

	children, _ = tree.S("agents").Children()
	vals = []map[string]string{}
	for _, m := range children {
		vals = append(vals, map[string]string{"uuid": m.Path("uuid").Data().(string)})
	}
	result.Set(vals, "agents")
	result.Set(tree.Path("environment_variables").Data(), "environment_variables")

	return result.String(), nil
}
<commit_msg>Update gocd.pipeline.create.go<commit_after>package plugins

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"

	"github.com/InnovaCo/serve/manifest"
	"github.com/InnovaCo/serve/utils/gabs"
)

func init() {
	manifest.PluginRegestry.Add("gocd.pipeline.create", goCdPipelineCreate{})
}

/**
 * plugin for manifest section "goCd.pipeline.create"
 * section structure:
 *
 * goCd.pipeline.create:
 *   api-url: goCd_URL
 *   environment: ENV
 *   branch: BRANCH
 *   allowed-branches: [BRANCH, ...]
 *   pipeline:
 *     group: GROUP
 *     pipeline:
 *       according to the description: https://api.go.cd/current/#the-pipeline-config-object
 */

type goCdCredents struct {
	Login    string `json:"login"`
	Password string `json:"password"`
}

type goCdPipelineCreate struct{}

func (p goCdPipelineCreate) Run(data manifest.Manifest) error {
	name := data.GetString("pipeline.pipeline.name")
	url := data.GetString("api-url")
	body := data.GetTree("pipeline").String()
	branch := data.GetString("branch")

	m := false
	for _, b := range data.GetArray("allowed-branches") {
		re := b.Unwrap().(string)
		if re == branch {
			m = true
			break
		} else if m, _ = regexp.MatchString(re, branch); m {
			break
		}
	}

	if !m {
		log.Println("branch ", branch, " not in ", data.GetString("allowed-branches"))
		return nil
	}

	resp, err := goCdRequest("GET", url+"/pipelines/"+name, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode == http.StatusOK {
		err = goCdUpdate(name, data.GetString("environment"), url, body, map[string]string{"If-Match": resp.Header.Get("ETag")})
	} else if resp.StatusCode == http.StatusNotFound {
		err = goCdCreate(name, data.GetString("environment"), url, body, nil)
	} else {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	if err != nil {
		log.Println(err)
		return err
	}

	return nil
}

func goCdCreate(name string, env string, resource string, body string, headers map[string]string) error {
	if resp, err := goCdRequest("POST", resource+"/pipelines", body, nil); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}
	data, tag, err := goCdChangeEnv(resource, env, name, "")
	if err != nil {
		log.Println(err)
		return err
	}

	log.Println(data)

	if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}

	if resp, err := goCdRequest("POST", resource+"/pipelines/"+name+"/unpause", body, map[string]string{"Confirm": "true"}); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdUpdate(name string, env string, resource string, body string, headers map[string]string) error {
	if resp, err := goCdRequest("PUT", resource+"/pipelines/"+name, body, headers); err != nil {
		log.Println(err)
		return err
	} else if resp.StatusCode != http.StatusOK {
		log.Println("Operation error: " + resp.Status)
		return errors.New("Operation error: " + resp.Status)
	}

	cEnv, err := goCdFindEnv(resource, name)
	if err != nil {
		return err
	}

	if env != cEnv {
		data, tag, err := goCdChangeEnv(resource, cEnv, "", name)
		if err != nil {
			return err
		}

		log.Println(data)

		if resp, err := goCdRequest("PUT", resource+"/environments/"+cEnv, data, map[string]string{"If-Match": tag}); err != nil {
			return err
		} else if resp.StatusCode != http.StatusOK {
			return errors.New("Operation error: " + resp.Status)
		}

		data, tag, err = goCdChangeEnv(resource, env, name, "")
		if err != nil {
			return err
		}

		log.Println(data)

		if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
			return err
		} else if resp.StatusCode != http.StatusOK {
			return errors.New("Operation error: " + resp.Status)
		}
	}

	if resp, err := goCdRequest("POST", resource+"/pipelines/"+name+"/unpause", body, map[string]string{"Confirm": "true"}); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdDelete(name string, env string, resource string) error {
	data, tag, err := goCdChangeEnv(resource, env, "", name)
	if err != nil {
		return err
	}

	log.Println(data)

	if resp, err := goCdRequest("PUT", resource+"/environments/"+env, data, map[string]string{"If-Match": tag}); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}

	if resp, err := goCdRequest("DELETE", resource+"/pipelines/"+name, "", nil); err != nil {
		return err
	} else if resp.StatusCode != http.StatusOK {
		return errors.New("Operation error: " + resp.Status)
	}

	return nil
}

func goCdChangeEnv(resource string, env string, addPipeline string, delPipeline string) (string, string, error) {
	resp, err := goCdRequest("GET", resource+"/environments/"+env, "", nil)
	if err != nil {
		return "", "", err
	}

	data, err := ChangeJSON(resp, addPipeline, delPipeline)
	if err != nil {
		return "", "", err
	}

	return data, resp.Header.Get("ETag"), nil
}

func goCdFindEnv(resource string, pipeline string) (string, error) {
	resp, err := goCdRequest("GET", resource+"/environments", "", nil)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", errors.New("parse error")
	}

	tree, err := gabs.ParseJSON(body)
	if err != nil {
		return "", errors.New("parse error")
	}

	envs, _ := tree.Path("_embedded.environments").Children()
	for _, env := range envs {
		envName := env.Path("name").Data().(string)
		pipelines, _ := env.Path("pipelines").Children()
		for _, pline := range pipelines {
			if pline.Path("name").Data().(string) == pipeline {
				return envName, nil
			}
		}
	}

	return "", nil
}

func goCdRequest(method string, resource string, body string, headers map[string]string) (*http.Response, error) {
	req, _ := http.NewRequest(method, resource, bytes.NewReader([]byte(body)))

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	req.Header.Set("Accept", "application/vnd.go.cd.v2+json")
	req.Header.Set("Content-Type", "application/json")

	data, err := ioutil.ReadFile("/etc/serve/gocd_credentials")
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Credentias file error: %v", err))
	}

	creds := &goCdCredents{}
	json.Unmarshal(data, creds)

	req.SetBasicAuth(creds.Login, creds.Password)

	log.Printf(" --> %s %s:\n%s\n%s\n", method, resource, req.Header, body)

	return http.DefaultClient.Do(req)
}

func ChangeJSON(resp *http.Response, addPipeline string, delPipeline string) (string, error) {
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return "", errors.New("read body error")
	}

	tree, err := gabs.ParseJSON(body)
	if err != nil {
		return "", errors.New("parse error")
	}
	result := gabs.New()

	result.Set(tree.Path("name").Data(), "name")

	children, _ := tree.S("pipelines").Children()
	vals := []map[string]string{}
	for _, m := range children {
		name := m.Path("name").Data().(string)
		if (delPipeline != "") && (name == delPipeline) {
			continue
		}
		if (addPipeline != "") && (name == addPipeline) {
			addPipeline = ""
		}
		vals = append(vals, map[string]string{"name": name})
	}
	if addPipeline != "" {
		vals = append(vals, map[string]string{"name": addPipeline})
	}
	result.Set(vals, "pipelines")

	children, _ = tree.S("agents").Children()
	vals = []map[string]string{}
	for _, m := range children {
		vals = append(vals, map[string]string{"uuid": m.Path("uuid").Data().(string)})
	}
	result.Set(vals, "agents")
	result.Set(tree.Path("environment_variables").Data(), "environment_variables")

	return result.String(), nil
}
<|endoftext|>


<commit_before>package edit

import (
	"fmt"
	"os"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
)

type location struct {
	listing
	candidates []store.Dir
}

func (*location) Mode() ModeType {
	return modeLocation
}

func (l *location) ModeLine(width int) *buffer {
	return l.modeLine(" LOCATION ", width)
}

func (l *location) update(ed *Editor) bool {
	dirs, err := ed.store.FindDirsSubseq(l.filter)
	if err != nil {
		l.candidates = nil
		l.selected = -1
		ed.notify("find directories: %v", err)
		return false
	}
	l.candidates = dirs

	if len(l.candidates) > 0 {
		l.selected = 0
	} else {
		l.selected = -1
	}
	return true
}

func startLocation(ed *Editor) {
	ed.location = location{}
	if ed.location.update(ed) {
		ed.mode = &ed.location
	}
}

func locationPrev(ed *Editor) {
	ed.location.prev(false)
}

func locationCyclePrev(ed *Editor) {
	ed.location.prev(true)
}

func locationNext(ed *Editor) {
	ed.location.next(false)
}

func locationCycleNext(ed *Editor) {
	ed.location.next(true)
}

func locationBackspace(ed *Editor) {
	ed.location.backspace(ed)
}

func acceptLocation(ed *Editor) {
	// XXX Maybe we want to use eval.cdInner and increase the score?
	loc := &ed.location
	if len(loc.candidates) > 0 {
		dir := loc.candidates[loc.selected].Path
		err := os.Chdir(dir)
		if err == nil {
			store := ed.store
			go func() {
				store.Waits.Add(1)
				// XXX Error ignored.
				store.AddDir(dir, 0.5)
				store.Waits.Done()
				Logger.Println("added dir to store:", dir)
			}()
		} else {
			ed.notify("%v", err)
		}
	}
	ed.mode = &ed.insert
}

func locationDefault(ed *Editor) {
	k := ed.lastKey
	if ed.location.handleFilterKey(k) {
		ed.location.update(ed)
	} else {
		startInsert(ed)
		ed.nextAction = action{typ: reprocessKey}
	}
}

func (loc *location) prev(cycle bool) {
	loc.listing.prev(cycle, len(loc.candidates))
}

func (loc *location) next(cycle bool) {
	loc.listing.next(cycle, len(loc.candidates))
}

func (loc *location) backspace(ed *Editor) {
	if loc.listing.backspace() {
		loc.update(ed)
	}
}

func (loc *location) List(width, maxHeight int) *buffer {
	get := func(i int) string {
		cand := loc.candidates[i]
		return fmt.Sprintf("%4.0f %s", cand.Score, parse.Quote(cand.Path))
	}
	return loc.listing.list(get, len(loc.candidates), width, maxHeight)
}
<commit_msg>Remove an outdated comment.<commit_after>package edit

import (
	"fmt"
	"os"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
)

type location struct {
	listing
	candidates []store.Dir
}

func (*location) Mode() ModeType {
	return modeLocation
}

func (l *location) ModeLine(width int) *buffer {
	return l.modeLine(" LOCATION ", width)
}

func (l *location) update(ed *Editor) bool {
	dirs, err := ed.store.FindDirsSubseq(l.filter)
	if err != nil {
		l.candidates = nil
		l.selected = -1
		ed.notify("find directories: %v", err)
		return false
	}
	l.candidates = dirs

	if len(l.candidates) > 0 {
		l.selected = 0
	} else {
		l.selected = -1
	}
	return true
}

func startLocation(ed *Editor) {
	ed.location = location{}
	if ed.location.update(ed) {
		ed.mode = &ed.location
	}
}

func locationPrev(ed *Editor) {
	ed.location.prev(false)
}

func locationCyclePrev(ed *Editor) {
	ed.location.prev(true)
}

func locationNext(ed *Editor) {
	ed.location.next(false)
}

func locationCycleNext(ed *Editor) {
	ed.location.next(true)
}

func locationBackspace(ed *Editor) {
	ed.location.backspace(ed)
}

func acceptLocation(ed *Editor) {
	loc := &ed.location
	if len(loc.candidates) > 0 {
		dir := loc.candidates[loc.selected].Path
		err := os.Chdir(dir)
		if err == nil {
			store := ed.store
			go func() {
				store.Waits.Add(1)
				// XXX Error ignored.
				store.AddDir(dir, 0.5)
				store.Waits.Done()
				Logger.Println("added dir to store:", dir)
			}()
		} else {
			ed.notify("%v", err)
		}
	}
	ed.mode = &ed.insert
}

func locationDefault(ed *Editor) {
	k := ed.lastKey
	if ed.location.handleFilterKey(k) {
		ed.location.update(ed)
	} else {
		startInsert(ed)
		ed.nextAction = action{typ: reprocessKey}
	}
}

func (loc *location) prev(cycle bool) {
	loc.listing.prev(cycle, len(loc.candidates))
}

func (loc *location) next(cycle bool) {
	loc.listing.next(cycle, len(loc.candidates))
}

func (loc *location) backspace(ed *Editor) {
	if loc.listing.backspace() {
		loc.update(ed)
	}
}

func (loc *location) List(width, maxHeight int) *buffer {
	get := func(i int) string {
		cand := loc.candidates[i]
		return fmt.Sprintf("%4.0f %s", cand.Score, parse.Quote(cand.Path))
	}
	return loc.listing.list(get, len(loc.candidates), width, maxHeight)
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/dcos/dcos-metrics/producers"
)

// retained to take advantage of connection re-use
var httpClient http.Client

func init() {
	httpClient.Timeout = 5 * time.Second
}

// postRequest is the main payload to librato
type postRequest struct {
	Measurements []*measurement `json:"measurements"`
	opts         *postRequestOpts
}

// postRequestOpts are the configurable options for postRequest
type postRequestOpts struct {
	libratoUrl      string
	libratoEmail    string
	libratoToken    string
	pollingInterval int64
}

func newPostRequest(opts *postRequestOpts) (*postRequest, error) {
	isBlank := func(str string) bool {
		return len(strings.TrimSpace(str)) == 0
	}
	if isBlank(opts.libratoUrl) {
		return nil, errors.New("Librato url must be specified")
	}
	if isBlank(opts.libratoEmail) {
		return nil, errors.New("Librato email address must be specified")
	}
	if isBlank(opts.libratoToken) {
		return nil, errors.New("Librato account token must be specified")
	}
	if opts.pollingInterval <= 0 {
		return nil, errors.New("Polling interval must be >= 0")
	}
	pr := &postRequest{
		opts: opts,
	}
	return pr, nil
}

func (p *postRequest) add(messages []producers.MetricsMessage) {
	oldDatapoints := 0
	for _, message := range messages {
		dimensions := message.Dimensions
		for _, datapoint := range message.Datapoints {
			measurement := newMeasurement()
			measurement.Name = datapoint.Name
			timestamp, err := p.parseTimestamp(datapoint.Timestamp)
			if err != nil {
				log.Errorf("Could not parse timestamp '%s': %v", datapoint.Timestamp, err)
				continue
			}
			if timestamp.Before(time.Now().Add(-10 * time.Minute)) {
				log.Debugf("Timestamp '%s' for '%s' is too old", datapoint.Timestamp, datapoint.Name)
				oldDatapoints++
				continue
			}
			measurement.Time = timestamp.Unix()
			measurement.floorTime(p.opts.pollingInterval)
			for k, v := range datapoint.Tags {
				p.setTag(measurement, k, v)
			}
			p.setTag(measurement, "mesosId", dimensions.MesosID)
			p.setTag(measurement, "clusterId", dimensions.ClusterID)
			p.setTag(measurement, "containerId", dimensions.ContainerID)
			p.setTag(measurement, "executorId", dimensions.ExecutorID)
			p.setTag(measurement, "frameworkName", dimensions.FrameworkName)
			p.setTag(measurement, "frameworkId", dimensions.FrameworkID)
			p.setTag(measurement, "frameworkRole", dimensions.FrameworkRole)
			p.setTag(measurement, "frameworkPrincipal", dimensions.FrameworkPrincipal)
			p.setTag(measurement, "hostname", dimensions.Hostname)
			for k, v := range dimensions.Labels {
				p.setTag(measurement, fmt.Sprintf("label:%s", k), v)
			}
			if err := measurement.setValue(datapoint.Value); err != nil {
				log.Errorf("Skipping datapoint '%s' due to an invalid value: %v", measurement, err)
				continue
			}
			if err := measurement.validate(); err != nil {
				log.Errorf("Skipping datapoint '%s' due to a validation error: %v", measurement, err)
				continue
			}
			p.Measurements = append(p.Measurements, measurement)
		}
	}
	if oldDatapoints > 0 {
		log.Warnf("Rejected %d datapoints because they were too old", oldDatapoints)
	}
}

func (p *postRequest) parseTimestamp(timestamp string) (*time.Time, error) {
	if timestamp == "" {
		return nil, errors.New("Received an empty timestamp")
	}
	parsed, err := time.Parse(time.RFC3339, timestamp)
	if err != nil {
		return nil, err
	}
	return &parsed, nil
}

func (p *postRequest) setTag(m *measurement, name string, value string) {
	if len(value) == 0 {
		// don't bother if a tag value was not specified
		return
	}
	if err := m.addTag(name, value); err != nil {
		// some tags cannot be used, this will get noisy if on the warn level
		log.Debugf("Invalid tag '%s'='%s' for measurement '%s': %v", name, value, m, err)
	}
}

func (p *postRequest) floorTime(value int64) int64 {
	if value <= 0 {
		value = time.Now().Unix()
	}
	// floor the timestamp to the polling interval to align datapoints
	return value / p.opts.pollingInterval * p.opts.pollingInterval
}

func (p *postRequest) send() error {
	encoded, err := json.Marshal(p)
	if err != nil {
		return fmt.Errorf("Could not marshal request: %v", err)
	}
	url := p.opts.libratoUrl + "/v1/measurements"
	req, err := http.NewRequest("POST", url, bytes.NewReader(encoded))
	if err != nil {
		return fmt.Errorf("Could not build request: %v", err)
	}
	authHeader, err := p.authHeader()
	if err != nil {
		return fmt.Errorf("Could not create authentication header: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", authHeader)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return p.checkResponse(resp)
}

func (p *postRequest) checkResponse(resp *http.Response) error {
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Got response code %d but could not read body: %v", resp.StatusCode, err)
	}
	if resp.StatusCode == http.StatusAccepted {
		var libratoResponse libratoResponse
		if err := json.Unmarshal(respBody, &libratoResponse); err != nil {
			return fmt.Errorf("Got response code %d but could not decode response body: %v", resp.StatusCode, err)
		}
		if libratoResponse.Measurements.Summary.Failed == 0 {
			return nil
		}
	}
	return fmt.Errorf("Librato responded with code: %d and body: %s", resp.StatusCode, string(respBody))
}

func (p *postRequest) authHeader() (string, error) {
	libratoEmail := p.opts.libratoEmail
	libratoToken := p.opts.libratoToken
	if len(strings.TrimSpace(libratoEmail)) == 0 {
		return "", errors.New("The " + libratoEmailFlagName + " flag must be specified.")
	}
	if len(strings.TrimSpace(libratoToken)) == 0 {
		return "", errors.New("The " + libratoTokenFlagName + " flag must be specified.")
	}
	userPass := fmt.Sprintf("%s:%s", libratoEmail, libratoToken)
	base64Encoded := base64.StdEncoding.EncodeToString([]byte(userPass))
	return fmt.Sprintf("Basic %s", base64Encoded), nil
}
<commit_msg>Refactored to use plugin.ParseDatapointTimestamp.<commit_after>// Copyright 2017 Mesosphere, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/dcos/dcos-metrics/plugins"
	"github.com/dcos/dcos-metrics/producers"
)

// retained to take advantage of connection re-use
var httpClient http.Client

func init() {
	httpClient.Timeout = 5 * time.Second
}

// postRequest is the main payload to librato
type postRequest struct {
	Measurements []*measurement `json:"measurements"`
	opts         *postRequestOpts
}

// postRequestOpts are the configurable options for postRequest
type postRequestOpts struct {
	libratoUrl      string
	libratoEmail    string
	libratoToken    string
	pollingInterval int64
}

func newPostRequest(opts *postRequestOpts) (*postRequest, error) {
	isBlank := func(str string) bool {
		return len(strings.TrimSpace(str)) == 0
	}
	if isBlank(opts.libratoUrl) {
		return nil, errors.New("Librato url must be specified")
	}
	if isBlank(opts.libratoEmail) {
		return nil, errors.New("Librato email address must be specified")
	}
	if isBlank(opts.libratoToken) {
		return nil, errors.New("Librato account token must be specified")
	}
	if opts.pollingInterval <= 0 {
		return nil, errors.New("Polling interval must be >= 0")
	}
	pr := &postRequest{
		opts: opts,
	}
	return pr, nil
}

func (p *postRequest) add(messages []producers.MetricsMessage) {
	oldDatapoints := 0
	for _, message := range messages {
		dimensions := message.Dimensions
		for _, datapoint := range message.Datapoints {
			measurement := newMeasurement()
			measurement.Name = datapoint.Name
			timestamp, err := plugin.ParseDatapointTimestamp(datapoint.Timestamp)
			if err != nil {
				log.Errorf("Could not parse timestamp '%s': %v", datapoint.Timestamp, err)
				continue
			}
			if timestamp.Before(time.Now().Add(-10 * time.Minute)) {
				log.Debugf("Timestamp '%s' for '%s' is too old", datapoint.Timestamp, datapoint.Name)
				oldDatapoints++
				continue
			}
			measurement.Time = timestamp.Unix()
			measurement.floorTime(p.opts.pollingInterval)
			for k, v := range datapoint.Tags {
				p.setTag(measurement, k, v)
			}
			p.setTag(measurement, "mesosId", dimensions.MesosID)
			p.setTag(measurement, "clusterId", dimensions.ClusterID)
			p.setTag(measurement, "containerId", dimensions.ContainerID)
			p.setTag(measurement, "executorId", dimensions.ExecutorID)
			p.setTag(measurement, "frameworkName", dimensions.FrameworkName)
			p.setTag(measurement, "frameworkId", dimensions.FrameworkID)
			p.setTag(measurement, "frameworkRole", dimensions.FrameworkRole)
			p.setTag(measurement, "frameworkPrincipal", dimensions.FrameworkPrincipal)
			p.setTag(measurement, "hostname", dimensions.Hostname)
			for k, v := range dimensions.Labels {
				p.setTag(measurement, fmt.Sprintf("label:%s", k), v)
			}
			if err := measurement.setValue(datapoint.Value); err != nil {
				log.Errorf("Skipping datapoint '%s' due to an invalid value: %v", measurement, err)
				continue
			}
			if err := measurement.validate(); err != nil {
				log.Errorf("Skipping datapoint '%s' due to a validation error: %v", measurement, err)
				continue
			}
			p.Measurements = append(p.Measurements, measurement)
		}
	}
	if oldDatapoints > 0 {
		log.Warnf("Rejected %d datapoints because they were too old", oldDatapoints)
	}
}

func (p *postRequest) setTag(m *measurement, name string, value string) {
	if len(value) == 0 {
		// don't bother if a tag value was not specified
		return
	}
	if err := m.addTag(name, value); err != nil {
		// some tags cannot be used, this will get noisy if on the warn level
		log.Debugf("Invalid tag '%s'='%s' for measurement '%s': %v", name, value, m, err)
	}
}

func (p *postRequest) floorTime(value int64) int64 {
	if value <= 0 {
		value = time.Now().Unix()
	}
	// floor the timestamp to the polling interval to align datapoints
	return value / p.opts.pollingInterval * p.opts.pollingInterval
}

func (p *postRequest) send() error {
	encoded, err := json.Marshal(p)
	if err != nil {
		return fmt.Errorf("Could not marshal request: %v", err)
	}
	url := p.opts.libratoUrl + "/v1/measurements"
	req, err := http.NewRequest("POST", url, bytes.NewReader(encoded))
	if err != nil {
		return fmt.Errorf("Could not build request: %v", err)
	}
	authHeader, err := p.authHeader()
	if err != nil {
		return fmt.Errorf("Could not create authentication header: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", authHeader)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return p.checkResponse(resp)
}

func (p *postRequest) checkResponse(resp *http.Response) error {
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Got response code %d but could not read body: %v", resp.StatusCode, err)
	}
	if resp.StatusCode == http.StatusAccepted {
		var libratoResponse libratoResponse
		if err := json.Unmarshal(respBody, &libratoResponse); err != nil {
			return fmt.Errorf("Got response code %d but could not decode response body: %v", resp.StatusCode, err)
		}
		if libratoResponse.Measurements.Summary.Failed == 0 {
			return nil
		}
	}
	return fmt.Errorf("Librato responded with code: %d and body: %s", resp.StatusCode, string(respBody))
}

func (p *postRequest) authHeader() (string, error) {
	libratoEmail := p.opts.libratoEmail
	libratoToken := p.opts.libratoToken
	if len(strings.TrimSpace(libratoEmail)) == 0 {
		return "", errors.New("The " + libratoEmailFlagName + " flag must be specified.")
	}
	if len(strings.TrimSpace(libratoToken)) == 0 {
		return "", errors.New("The " + libratoTokenFlagName + " flag must be specified.")
	}
	userPass := fmt.Sprintf("%s:%s", libratoEmail, libratoToken)
	base64Encoded := base64.StdEncoding.EncodeToString([]byte(userPass))
	return fmt.Sprintf("Basic %s", base64Encoded), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

// allow every nickname for now
func filter(nickname, channel string) bool {
	return true
}

// from RFC 2812 :
// nickname   =  ( letter / special ) *8( letter / digit / special / "-" )
// letter     =  %x41-5A / %x61-7A       ; A-Z / a-z
//  digit      =  %x30-39                 ; 0-9
//  special    =  %x5B-60 / %x7B-7D
//                   ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
func letterOrSpecial(c byte) bool {
	switch {
	case c >= 0x41 && c <= 0x5A:
		return true
	case c >= 0x61 && c <= 0x7A:
		return true
	case c >= 0x5B && c <= 0x60:
		return true
	case c >= 0x7B && c <= 0x7D:
		return true
	default:
		return false
	}
}
func nicknameChar(c byte) bool {
	switch {
	case c >= 0x30 && c <= 0x39:
		return true
	case c == '-':
		return true
	default:
		return letterOrSpecial(c)
	}
}

func trimNickname(word string) string {
	if len(word) == 0 {
		return word
	}

	if !letterOrSpecial(word[0]) {
		if len(word) > 1 {
			return trimNickname(word[1:])
		} else {
			return ""
		}
	}

	if !nicknameChar(word[len(word)-1]) {
		if len(word) > 1 {
			return trimNickname(word[:len(word)-1])
		} else {
			return ""
		}
	}

	return word
}

func validNickname(nickname string) bool {
	if len(nickname) < 2 {
		return false
	}
	if !letterOrSpecial(nickname[0]) {
		return false
	}
	for i := 0; i < len(nickname); i++ {
		if !nicknameChar(nickname[i]) {
			return false
		}
	}
	return true
}

var r1 = regexp.MustCompile(":[^ ]+ PRIVMSG (#[^ ]+) :(.*)")

// regex to find nickname++
// from RFC 2812 :
// nickname   =  ( letter / special ) *8( letter / digit / special / "-" )
// letter     =  %x41-5A / %x61-7A       ; A-Z / a-z
//  digit      =  %x30-39                 ; 0-9
//  special    =  %x5B-60 / %x7B-7D
//                   ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
var r2 = regexp.MustCompile("([\\w`\\[\\]\\^_\\{|\\}][\\w\\d-`\\[\\]\\^_\\{|\\}]+)\\+\\+")

func parseKarmaRegex(line string) ([]string, string, bool) {
	var (
		channel   string
		text      string
		nicknames []string
		found     bool
	)

	match := r1.FindStringSubmatch(line)

	if len(match) != 3 {
		return []string{}, "", false
	}

	channel = match[1]
	text = match[2]

	matches := r2.FindAllStringSubmatch(text, -1)

	for _, match = range matches {
		if len(match) > 1 {
			nicknames = append(nicknames, match[1])
		}
	}

	if len(nicknames) > 0 {
		found = true
	}

	return nicknames, channel, found
}

// Same as parseKarmaRegex above except not using regexp
func parseKarma(line string) ([]string, string, bool) {
	var (
		channel   string
		command   string
		who       string
		nicknames []string
		found     bool
	)

	fmt.Sscanf(line, "%s %s %s", &who, &command, &channel)
	if command != "PRIVMSG" {
		return []string{}, "", false
	}

	// position of text in PRIVMSG lines, line[1:] for ignoring first char usually ':'
	index := strings.Index(line[1:], ":")
	if index == -1 {
		return []string{}, "", false
	}

	// prevent out of slice
	if len(line) < index+3 {
		return []string{}, "", false
	}

	text := line[index+2:]

	if !strings.Contains(text, "++") {
		return []string{}, "", false
	}

	words := strings.Split(text, " ")
	for _, word := range words {
		word := strings.Trim(word, "\n \r")

		if !strings.Contains(word, "++") {
			continue
		}

		if len(word) > 2 {
			l := len(word)
			// word must end with "++" + optional char (like ')')
			if word[l-2] == '+' && (word[l-1] == '+' || word[l-3] == '+') {
				trimmed := trimNickname(word)
				if validNickname(trimmed) {
					nicknames = append(nicknames, trimmed)
				}
			}
		}
	}

	if len(nicknames) > 0 {
		found = true
	}

	return nicknames, channel, found
}

// check if user asked for karma of someone (:karma nickname)
// if yes, then reply to the channel with current karma for that nickname
func replyKarma(line string) {
	var (
		channel string
		command string
		who     string
	)

	fmt.Sscanf(line, "%s %s %s :", &who, &command, &channel)
	if command != "PRIVMSG" {
		return
	}

	// position of text in PRIVMSG lines, line[1:] for ignoring first char usually ':'
	index := strings.Index(line[1:], ":")
	if index == -1 {
		return
	}

	// prevent out of slice
	if len(line) < index+3 {
		return
	}
	text := line[index+2:]

	if len(text) > 6 && text[:6] == ":karma" {
		nickname := strings.Trim(text[7:len(text)-1], "\n \r")
		fmt.Printf("PRIVMSG %s :%s has a karma of %d\n", channel, nickname, db[nickname])
	}

	return
}

var db map[string]uint

func inc(nickname string) {
	db[nickname] += 1
}

func dump() {
	f, _ := os.Create("db.json")
	defer f.Close()
	data, _ := json.Marshal(db)
	f.Write(data)
	f.Sync()
}

func load() {
	_, err := os.Stat("db.json")
	if err != nil {
		db = make(map[string]uint)
		return
	}

	data, _ := ioutil.ReadFile("db.json")
	err = json.Unmarshal(data, &db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error", err)
	}
}

func main() {
	bio := bufio.NewReader(os.Stdin)

	load()

	for {
		line, err := bio.ReadString('\n')
		if err == nil {
			replyKarma(line)

			nicknames, channel, found := parseKarmaRegex(line)

			if found {
				for _, nickname := range nicknames {
					if filter(nickname, channel) {
						inc(nickname)
						dump()
					}
				}
			}
		}
	}
}
<commit_msg>Fix karma parsing<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

// allow every nickname for now
func filter(nickname, channel string) bool {
	return true
}

// from RFC 2812 :
// nickname   =  ( letter / special ) *8( letter / digit / special / "-" )
// letter     =  %x41-5A / %x61-7A       ; A-Z / a-z
//  digit      =  %x30-39                 ; 0-9
//  special    =  %x5B-60 / %x7B-7D
//                   ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
func letterOrSpecial(c byte) bool {
	switch {
	case c >= 0x41 && c <= 0x5A:
		return true
	case c >= 0x61 && c <= 0x7A:
		return true
	case c >= 0x5B && c <= 0x60:
		return true
	case c >= 0x7B && c <= 0x7D:
		return true
	default:
		return false
	}
}
func nicknameChar(c byte) bool {
	switch {
	case c >= 0x30 && c <= 0x39:
		return true
	case c == '-':
		return true
	default:
		return letterOrSpecial(c)
	}
}

func trimNickname(word string) string {
	if len(word) == 0 {
		return word
	}

	if !letterOrSpecial(word[0]) {
		if len(word) > 1 {
			return trimNickname(word[1:])
		} else {
			return ""
		}
	}

	if !nicknameChar(word[len(word)-1]) {
		if len(word) > 1 {
			return trimNickname(word[:len(word)-1])
		} else {
			return ""
		}
	}

	return word
}

func validNickname(nickname string) bool {
	if len(nickname) < 2 {
		return false
	}
	if !letterOrSpecial(nickname[0]) {
		return false
	}
	for i := 0; i < len(nickname); i++ {
		if !nicknameChar(nickname[i]) {
			return false
		}
	}
	return true
}

var r1 = regexp.MustCompile(":[^ ]+ PRIVMSG (#[^ ]+) :(.*)")

// regex to find nickname++
// from RFC 2812 :
// nickname   =  ( letter / special ) *8( letter / digit / special / "-" )
// letter     =  %x41-5A / %x61-7A       ; A-Z / a-z
//  digit      =  %x30-39                 ; 0-9
//  special    =  %x5B-60 / %x7B-7D
//                   ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
var r2 = regexp.MustCompile("([\\w`\\[\\]\\^_\\{|\\}][\\w\\d-`\\[\\]\\^_\\{|\\}]+)\\+\\+")

func parseKarmaRegex(line string) ([]string, string, bool) {
	var (
		channel   string
		text      string
		nicknames []string
		found     bool
	)

	match := r1.FindStringSubmatch(line)

	if len(match) != 3 {
		return []string{}, "", false
	}

	channel = match[1]
	text = match[2]

	matches := r2.FindAllStringSubmatch(text, -1)

	for _, match = range matches {
		if len(match) > 1 {
			nicknames = append(nicknames, match[1])
		}
	}

	if len(nicknames) > 0 {
		found = true
	}

	return nicknames, channel, found
}

// Same as parseKarmaRegex above except not using regexp
func parseKarma(line string) ([]string, string, bool) {
	var (
		channel   string
		command   string
		who       string
		nicknames []string
		found     bool
	)

	fmt.Sscanf(line, "%s %s %s", &who, &command, &channel)
	if command != "PRIVMSG" {
		return []string{}, "", false
	}

	// position of text in PRIVMSG lines, line[1:] for ignoring first char usually ':'
	index := strings.Index(line[1:], ":")
	if index == -1 {
		return []string{}, "", false
	}

	// prevent out of slice
	if len(line) < index+3 {
		return []string{}, "", false
	}

	text := line[index+2:]

	if !strings.Contains(text, "++") {
		return []string{}, "", false
	}

	words := strings.Split(text, " ")
	for _, word := range words {
		word := strings.Trim(word, "\n \r")

		if !strings.Contains(word, "++") {
			continue
		}

		if len(word) > 2 {
			l := len(word)
			// word must end with "++" + optional char (like ')')
			if word[l-2] == '+' && (word[l-1] == '+' || word[l-3] == '+') {
				trimmed := trimNickname(word)
				if validNickname(trimmed) {
					nicknames = append(nicknames, trimmed)
				}
			}
		}
	}

	if len(nicknames) > 0 {
		found = true
	}

	return nicknames, channel, found
}

// check if user asked for karma of someone (:karma nickname)
// if yes, then reply to the channel with current karma for that nickname
func replyKarma(line string) {
	var (
		channel string
		command string
		who     string
	)

	fmt.Sscanf(line, "%s %s %s :", &who, &command, &channel)
	if command != "PRIVMSG" {
		return
	}

	// position of text in PRIVMSG lines, line[1:] for ignoring first char usually ':'
	index := strings.Index(line, " ::karma")
	if index == -1 {
		return
	}

	text := line[index+2:]

	if len(text) > 6 && text[:6] == ":karma" {
		nickname := strings.Trim(text[7:len(text)-1], "\n \r")
		fmt.Printf("PRIVMSG %s :%s has a karma of %d\n", channel, nickname, db[nickname])
	}

	return
}

var db map[string]uint

func inc(nickname string) {
	db[nickname] += 1
}

func dump() {
	f, _ := os.Create("db.json")
	defer f.Close()
	data, _ := json.Marshal(db)
	f.Write(data)
	f.Sync()
}

func load() {
	_, err := os.Stat("db.json")
	if err != nil {
		db = make(map[string]uint)
		return
	}

	data, _ := ioutil.ReadFile("db.json")
	err = json.Unmarshal(data, &db)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error", err)
	}
}

func main() {
	bio := bufio.NewReader(os.Stdin)

	load()

	for {
		line, err := bio.ReadString('\n')
		if err == nil {
			replyKarma(line)

			nicknames, channel, found := parseKarmaRegex(line)

			if found {
				for _, nickname := range nicknames {
					if filter(nickname, channel) {
						inc(nickname)
						dump()
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

// Imports and register the Zookeeper TopologyServer

import (
	"html/template"
	"path"
	"sort"
	"strings"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/zktopo"
	"github.com/youtube/vitess/go/zk"
)

func init() {
	// handles /zk paths
	ts := topo.GetServerByName("zookeeper")
	if ts == nil {
		log.Error("zookeeper explorer disabled: no zktopo.Server")
		return
	}

	HandleExplorer("zk", "/zk/", "zk.html", NewZkExplorer(ts.(*zktopo.Server).GetZConn()))
}

type ZkExplorer struct {
	zconn zk.Conn
}

func NewZkExplorer(zconn zk.Conn) *ZkExplorer {
	return &ZkExplorer{zconn}
}

func (ex ZkExplorer) GetKeyspacePath(keyspace string) string {
	return path.Join("/zk/global/vt/keyspaces", keyspace)
}

func (ex ZkExplorer) GetShardPath(keyspace, shard string) string {
	return path.Join("/zk/global/vt/keyspaces", keyspace, shard)
}

func (ex ZkExplorer) GetTabletPath(alias topo.TabletAlias) string {
	return path.Join("/zk", alias.Cell, "vt/tablets", alias.TabletUidStr())
}

func (ex ZkExplorer) HandlePath(actionRepo *ActionRepository, zkPath string) interface{} {
	result := NewZkResult(zkPath)

	if zkPath == "/zk" {
		cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"})
		if err != nil {
			result.Error = err.Error()
			return result
		}
		for i, cell := range cells {
			cells[i] = cell[4:] // cut off "/zk/"
		}
		result.Children = cells
		sort.Strings(result.Children)
		return result
	}

	data, _, err := ex.zconn.Get(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m {
		keyspace := path.Base(zkPath)
		actionRepo.PopulateKeyspaceActions(result.Actions, keyspace)
	} else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m {
		zkPathParts := strings.Split(zkPath, "/")
		keyspace := zkPathParts[5]
		shard := zkPathParts[7]
		actionRepo.PopulateShardActions(result.Actions, keyspace, shard)
	} else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m {
		zkPathParts := strings.Split(result.Path, "/")
		alias := zkPathParts[2] + "-" + zkPathParts[5]
		actionRepo.PopulateTabletActions(result.Actions, alias)
	}
	result.Data = data
	children, _, err := ex.zconn.Children(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	result.Children = children
	sort.Strings(result.Children)
	return result
}

type ZkResult struct {
	Path     string
	Data     string
	Children []string
	Actions  map[string]template.URL
	Error    string
}

func NewZkResult(zkPath string) *ZkResult {
	return &ZkResult{Actions: make(map[string]template.URL), Path: zkPath}
}
<commit_msg>Fix typo in ZooKeeper explorer.<commit_after>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

// Imports and register the Zookeeper TopologyServer

import (
	"html/template"
	"path"
	"sort"
	"strings"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/zktopo"
	"github.com/youtube/vitess/go/zk"
)

func init() {
	// handles /zk paths
	ts := topo.GetServerByName("zookeeper")
	if ts == nil {
		log.Error("zookeeper explorer disabled: no zktopo.Server")
		return
	}

	HandleExplorer("zk", "/zk/", "zk.html", NewZkExplorer(ts.(*zktopo.Server).GetZConn()))
}

type ZkExplorer struct {
	zconn zk.Conn
}

func NewZkExplorer(zconn zk.Conn) *ZkExplorer {
	return &ZkExplorer{zconn}
}

func (ex ZkExplorer) GetKeyspacePath(keyspace string) string {
	return path.Join("/zk/global/vt/keyspaces", keyspace)
}

func (ex ZkExplorer) GetShardPath(keyspace, shard string) string {
	return path.Join("/zk/global/vt/keyspaces", keyspace, "shards", shard)
}

func (ex ZkExplorer) GetTabletPath(alias topo.TabletAlias) string {
	return path.Join("/zk", alias.Cell, "vt/tablets", alias.TabletUidStr())
}

func (ex ZkExplorer) HandlePath(actionRepo *ActionRepository, zkPath string) interface{} {
	result := NewZkResult(zkPath)

	if zkPath == "/zk" {
		cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"})
		if err != nil {
			result.Error = err.Error()
			return result
		}
		for i, cell := range cells {
			cells[i] = cell[4:] // cut off "/zk/"
		}
		result.Children = cells
		sort.Strings(result.Children)
		return result
	}

	data, _, err := ex.zconn.Get(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m {
		keyspace := path.Base(zkPath)
		actionRepo.PopulateKeyspaceActions(result.Actions, keyspace)
	} else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m {
		zkPathParts := strings.Split(zkPath, "/")
		keyspace := zkPathParts[5]
		shard := zkPathParts[7]
		actionRepo.PopulateShardActions(result.Actions, keyspace, shard)
	} else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m {
		zkPathParts := strings.Split(result.Path, "/")
		alias := zkPathParts[2] + "-" + zkPathParts[5]
		actionRepo.PopulateTabletActions(result.Actions, alias)
	}
	result.Data = data
	children, _, err := ex.zconn.Children(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	result.Children = children
	sort.Strings(result.Children)
	return result
}

type ZkResult struct {
	Path     string
	Data     string
	Children []string
	Actions  map[string]template.URL
	Error    string
}

func NewZkResult(zkPath string) *ZkResult {
	return &ZkResult{Actions: make(map[string]template.URL), Path: zkPath}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package persistentvolume

import (
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/controller/framework"
	"k8s.io/kubernetes/pkg/runtime"
	vol "k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/watch"

	"github.com/golang/glog"
)

// PersistentVolumeController is a controller that synchronizes
// PersistentVolumeClaims and PersistentVolumes. It starts two
// framework.Controllers that watch PerstentVolume and PersistentVolumeClaim
// changes.
type PersistentVolumeController struct {
	volumes                persistentVolumeOrderedIndex
	volumeController       *framework.Controller
	volumeControllerStopCh chan struct{}
	claims                 cache.Store
	claimController        *framework.Controller
	claimControllerStopCh  chan struct{}
	kubeClient             clientset.Interface
}

// NewPersistentVolumeController creates a new PersistentVolumeController
func NewPersistentVolumeController(
	kubeClient clientset.Interface,
	syncPeriod time.Duration,
	provisioner vol.ProvisionableVolumePlugin,
	recyclers []vol.VolumePlugin,
	cloud cloudprovider.Interface) *PersistentVolumeController {

	controller := &PersistentVolumeController{
		kubeClient: kubeClient,
	}

	volumeSource := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return kubeClient.Core().PersistentVolumes().List(options)
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return kubeClient.Core().PersistentVolumes().Watch(options)
		},
	}

	claimSource := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).List(options)
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).Watch(options)
		},
	}

	controller.initializeController(syncPeriod, volumeSource, claimSource)

	return controller
}

// initializeController prepares watching for PersistentVolume and
// PersistentVolumeClaim events from given sources. This should be used to
// initialize the controller for real operation (with real event sources) and
// also during testing (with fake ones).
func (ctrl *PersistentVolumeController) initializeController(syncPeriod time.Duration, volumeSource, claimSource cache.ListerWatcher) {
	glog.V(4).Infof("initializing PersistentVolumeController, sync every %s", syncPeriod.String())
	ctrl.volumes.store, ctrl.volumeController = framework.NewIndexerInformer(
		volumeSource,
		&api.PersistentVolume{},
		syncPeriod,
		framework.ResourceEventHandlerFuncs{
			AddFunc:    ctrl.addVolume,
			UpdateFunc: ctrl.updateVolume,
			DeleteFunc: ctrl.deleteVolume,
		},
		cache.Indexers{"accessmodes": accessModesIndexFunc},
	)
	ctrl.claims, ctrl.claimController = framework.NewInformer(
		claimSource,
		&api.PersistentVolumeClaim{},
		syncPeriod,
		framework.ResourceEventHandlerFuncs{
			AddFunc:    ctrl.addClaim,
			UpdateFunc: ctrl.updateClaim,
			DeleteFunc: ctrl.deleteClaim,
		},
	)
}

// addVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) addVolume(obj interface{}) {
	pv, ok := obj.(*api.PersistentVolume)
	if !ok {
		glog.Errorf("expected PersistentVolume but handler received %+v", obj)
		return
	}
	if err := ctrl.syncVolume(pv); err != nil {
		glog.Errorf("PersistentVolumeController could not add volume %q: %+v", pv.Name, err)
	}
}

// updateVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) updateVolume(oldObj, newObj interface{}) {
	newVolume, ok := newObj.(*api.PersistentVolume)
	if !ok {
		glog.Errorf("Expected PersistentVolume but handler received %+v", newObj)
		return
	}
	if err := ctrl.syncVolume(newVolume); err != nil {
		glog.Errorf("PersistentVolumeController could not update volume %q: %+v", newVolume.Name, err)
	}
}

// deleteVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) deleteVolume(obj interface{}) {
	// Intentionally left blank - we do not react on deleted volumes
}

// addClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) addClaim(obj interface{}) {
	claim, ok := obj.(*api.PersistentVolumeClaim)
	if !ok {
		glog.Errorf("Expected PersistentVolumeClaim but addClaim received %+v", obj)
		return
	}
	if err := ctrl.syncClaim(claim); err != nil {
		glog.Errorf("PersistentVolumeController could not add claim %q: %+v", claimToClaimKey(claim), err)
	}
}

// updateClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) updateClaim(oldObj, newObj interface{}) {
	newClaim, ok := newObj.(*api.PersistentVolumeClaim)
	if !ok {
		glog.Errorf("Expected PersistentVolumeClaim but updateClaim received %+v", newObj)
		return
	}
	if err := ctrl.syncClaim(newClaim); err != nil {
		glog.Errorf("PersistentVolumeController could not update claim %q: %+v", claimToClaimKey(newClaim), err)
	}
}

// deleteClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) deleteClaim(obj interface{}) {
	var volume *api.PersistentVolume
	var claim *api.PersistentVolumeClaim
	var ok bool

	claim, ok = obj.(*api.PersistentVolumeClaim)
	if !ok {
		if unknown, ok := obj.(cache.DeletedFinalStateUnknown); ok && unknown.Obj != nil {
			claim, ok = unknown.Obj.(*api.PersistentVolumeClaim)
			if !ok {
				glog.Errorf("Expected PersistentVolumeClaim but deleteClaim received %+v", unknown.Obj)
				return
			}
		} else {
			glog.Errorf("Expected PersistentVolumeClaim but deleteClaim received %+v", obj)
			return
		}
	}

	if !ok || claim == nil {
		return
	}

	if pvObj, exists, _ := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName); exists {
		if volume, ok = pvObj.(*api.PersistentVolume); ok {
			// sync the volume when its claim is deleted.  Explicitly sync'ing the
			// volume here in response to claim deletion prevents the volume from
			// waiting until the next sync period for its Release.
			if volume != nil {
				err := ctrl.syncVolume(volume)
				if err != nil {
					glog.Errorf("PersistentVolumeController could not update volume %q from deleteClaim handler: %+v", volume.Name, err)
				}
			}
		} else {
			glog.Errorf("Cannot convert object from volume cache to volume %q!?: %+v", claim.Spec.VolumeName, pvObj)
		}
	}
}

func (ctrl *PersistentVolumeController) syncVolume(volume *api.PersistentVolume) error {
	glog.V(4).Infof("synchronizing PersistentVolume[%s], current phase: %s", volume.Name, volume.Status.Phase)

	return nil
}

func (ctrl *PersistentVolumeController) syncClaim(claim *api.PersistentVolumeClaim) error {
	glog.V(4).Infof("synchronizing PersistentVolumeClaim[%s], current phase: %s", claim.Name, claim.Status.Phase)

	return nil
}

// Run starts all of this controller's control loops
func (ctrl *PersistentVolumeController) Run() {
	glog.V(4).Infof("starting PersistentVolumeController")

	if ctrl.volumeControllerStopCh == nil {
		ctrl.volumeControllerStopCh = make(chan struct{})
		go ctrl.volumeController.Run(ctrl.volumeControllerStopCh)
	}

	if ctrl.claimControllerStopCh == nil {
		ctrl.claimControllerStopCh = make(chan struct{})
		go ctrl.claimController.Run(ctrl.claimControllerStopCh)
	}
}

// Stop gracefully shuts down this controller
func (ctrl *PersistentVolumeController) Stop() {
	glog.V(4).Infof("stopping PersistentVolumeController")
	close(ctrl.volumeControllerStopCh)
	close(ctrl.claimControllerStopCh)
}
<commit_msg>Don't process events until fully initialized.<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package persistentvolume

import (
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/controller/framework"
	"k8s.io/kubernetes/pkg/runtime"
	vol "k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/watch"

	"github.com/golang/glog"
)

// PersistentVolumeController is a controller that synchronizes
// PersistentVolumeClaims and PersistentVolumes. It starts two
// framework.Controllers that watch PerstentVolume and PersistentVolumeClaim
// changes.
type PersistentVolumeController struct {
	volumes                persistentVolumeOrderedIndex
	volumeController       *framework.Controller
	volumeControllerStopCh chan struct{}
	claims                 cache.Store
	claimController        *framework.Controller
	claimControllerStopCh  chan struct{}
	kubeClient             clientset.Interface
}

// NewPersistentVolumeController creates a new PersistentVolumeController
func NewPersistentVolumeController(
	kubeClient clientset.Interface,
	syncPeriod time.Duration,
	provisioner vol.ProvisionableVolumePlugin,
	recyclers []vol.VolumePlugin,
	cloud cloudprovider.Interface) *PersistentVolumeController {

	controller := &PersistentVolumeController{
		kubeClient: kubeClient,
	}

	volumeSource := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return kubeClient.Core().PersistentVolumes().List(options)
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return kubeClient.Core().PersistentVolumes().Watch(options)
		},
	}

	claimSource := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).List(options)
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).Watch(options)
		},
	}

	controller.initializeController(syncPeriod, volumeSource, claimSource)

	return controller
}

// initializeController prepares watching for PersistentVolume and
// PersistentVolumeClaim events from given sources. This should be used to
// initialize the controller for real operation (with real event sources) and
// also during testing (with fake ones).
func (ctrl *PersistentVolumeController) initializeController(syncPeriod time.Duration, volumeSource, claimSource cache.ListerWatcher) {
	glog.V(4).Infof("initializing PersistentVolumeController, sync every %s", syncPeriod.String())
	ctrl.volumes.store, ctrl.volumeController = framework.NewIndexerInformer(
		volumeSource,
		&api.PersistentVolume{},
		syncPeriod,
		framework.ResourceEventHandlerFuncs{
			AddFunc:    ctrl.addVolume,
			UpdateFunc: ctrl.updateVolume,
			DeleteFunc: ctrl.deleteVolume,
		},
		cache.Indexers{"accessmodes": accessModesIndexFunc},
	)
	ctrl.claims, ctrl.claimController = framework.NewInformer(
		claimSource,
		&api.PersistentVolumeClaim{},
		syncPeriod,
		framework.ResourceEventHandlerFuncs{
			AddFunc:    ctrl.addClaim,
			UpdateFunc: ctrl.updateClaim,
			DeleteFunc: ctrl.deleteClaim,
		},
	)
}

// addVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) addVolume(obj interface{}) {
	if !ctrl.isFullySynced() {
		return
	}

	pv, ok := obj.(*api.PersistentVolume)
	if !ok {
		glog.Errorf("expected PersistentVolume but handler received %+v", obj)
		return
	}
	if err := ctrl.syncVolume(pv); err != nil {
		glog.Errorf("PersistentVolumeController could not add volume %q: %+v", pv.Name, err)
	}
}

// updateVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) updateVolume(oldObj, newObj interface{}) {
	if !ctrl.isFullySynced() {
		return
	}

	newVolume, ok := newObj.(*api.PersistentVolume)
	if !ok {
		glog.Errorf("Expected PersistentVolume but handler received %+v", newObj)
		return
	}
	if err := ctrl.syncVolume(newVolume); err != nil {
		glog.Errorf("PersistentVolumeController could not update volume %q: %+v", newVolume.Name, err)
	}
}

// deleteVolume is callback from framework.Controller watching PersistentVolume
// events.
func (ctrl *PersistentVolumeController) deleteVolume(obj interface{}) {
	// Intentionally left blank - we do not react on deleted volumes
}

// addClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) addClaim(obj interface{}) {
	if !ctrl.isFullySynced() {
		return
	}

	claim, ok := obj.(*api.PersistentVolumeClaim)
	if !ok {
		glog.Errorf("Expected PersistentVolumeClaim but addClaim received %+v", obj)
		return
	}
	if err := ctrl.syncClaim(claim); err != nil {
		glog.Errorf("PersistentVolumeController could not add claim %q: %+v", claimToClaimKey(claim), err)
	}
}

// updateClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) updateClaim(oldObj, newObj interface{}) {
	if !ctrl.isFullySynced() {
		return
	}

	newClaim, ok := newObj.(*api.PersistentVolumeClaim)
	if !ok {
		glog.Errorf("Expected PersistentVolumeClaim but updateClaim received %+v", newObj)
		return
	}
	if err := ctrl.syncClaim(newClaim); err != nil {
		glog.Errorf("PersistentVolumeController could not update claim %q: %+v", claimToClaimKey(newClaim), err)
	}
}

// deleteClaim is callback from framework.Controller watching PersistentVolumeClaim
// events.
func (ctrl *PersistentVolumeController) deleteClaim(obj interface{}) {
	if !ctrl.isFullySynced() {
		return
	}

	var volume *api.PersistentVolume
	var claim *api.PersistentVolumeClaim
	var ok bool

	claim, ok = obj.(*api.PersistentVolumeClaim)
	if !ok {
		if unknown, ok := obj.(cache.DeletedFinalStateUnknown); ok && unknown.Obj != nil {
			claim, ok = unknown.Obj.(*api.PersistentVolumeClaim)
			if !ok {
				glog.Errorf("Expected PersistentVolumeClaim but deleteClaim received %+v", unknown.Obj)
				return
			}
		} else {
			glog.Errorf("Expected PersistentVolumeClaim but deleteClaim received %+v", obj)
			return
		}
	}

	if !ok || claim == nil {
		return
	}

	if pvObj, exists, _ := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName); exists {
		if volume, ok = pvObj.(*api.PersistentVolume); ok {
			// sync the volume when its claim is deleted.  Explicitly sync'ing the
			// volume here in response to claim deletion prevents the volume from
			// waiting until the next sync period for its Release.
			if volume != nil {
				err := ctrl.syncVolume(volume)
				if err != nil {
					glog.Errorf("PersistentVolumeController could not update volume %q from deleteClaim handler: %+v", volume.Name, err)
				}
			}
		} else {
			glog.Errorf("Cannot convert object from volume cache to volume %q!?: %+v", claim.Spec.VolumeName, pvObj)
		}
	}
}

func (ctrl *PersistentVolumeController) syncVolume(volume *api.PersistentVolume) error {
	glog.V(4).Infof("synchronizing PersistentVolume[%s], current phase: %s", volume.Name, volume.Status.Phase)

	return nil
}

func (ctrl *PersistentVolumeController) syncClaim(claim *api.PersistentVolumeClaim) error {
	glog.V(4).Infof("synchronizing PersistentVolumeClaim[%s], current phase: %s", claim.Name, claim.Status.Phase)

	return nil
}

// Run starts all of this controller's control loops
func (ctrl *PersistentVolumeController) Run() {
	glog.V(4).Infof("starting PersistentVolumeController")

	if ctrl.volumeControllerStopCh == nil {
		ctrl.volumeControllerStopCh = make(chan struct{})
		go ctrl.volumeController.Run(ctrl.volumeControllerStopCh)
	}

	if ctrl.claimControllerStopCh == nil {
		ctrl.claimControllerStopCh = make(chan struct{})
		go ctrl.claimController.Run(ctrl.claimControllerStopCh)
	}
}

// Stop gracefully shuts down this controller
func (ctrl *PersistentVolumeController) Stop() {
	glog.V(4).Infof("stopping PersistentVolumeController")
	close(ctrl.volumeControllerStopCh)
	close(ctrl.claimControllerStopCh)
}

// isFullySynced returns true, if both volume and claim caches are fully loaded
// after startup.
// We do not want to process events with not fully loaded caches - e.g. we might
// recycle/delete PVs that don't have corresponding claim in the cache yet.
func (ctrl *PersistentVolumeController) isFullySynced() bool {
	return ctrl.volumeController.HasSynced() && ctrl.claimController.HasSynced()
}
<|endoftext|>


<commit_before>package dynamodb

import (
	"sync"
	"time"

	"github.com/Clever/leakybucket"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
)

var _ leakybucket.Bucket = &bucket{}

type bucket struct {
	name                string
	capacity, remaining uint
	reset               time.Time
	rate                time.Duration
	db                  *bucketDB
	mutex               sync.Mutex
}

// Capacity ...
func (b *bucket) Capacity() uint {
	return b.capacity
}

// Remaining space in the bucket.
func (b *bucket) Remaining() uint {
	return b.remaining
}

// Reset returns when the bucket will be drained.
func (b *bucket) Reset() time.Time {
	return b.reset
}

// Add to the bucket.
func (b *bucket) Add(amount uint) (leakybucket.BucketState, error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	dbBucket, err := b.db.bucket(b.name)
	if err != nil {
		return b.state(), err
	}
	if dbBucket.expired() {
		dbBucket, err = b.db.flushBucket(*dbBucket, time.Now().Add(b.rate))
		if err != nil {
			return b.state(), err
		}
	}
	// update local state
	b.remaining = b.capacity - min(dbBucket.Value, b.capacity)
	b.reset = dbBucket.Expiration
	if amount > b.remaining {
		return b.state(), leakybucket.ErrorFull
	}
	updatedDBBucket, err := b.db.incrementBucketValue(b.name, amount, b.capacity)
	if err != nil {
		if err == errBucketCapacityExceeded {
			return b.state(), leakybucket.ErrorFull
		}
		return b.state(), err
	}
	// ensure we can't overflow
	b.remaining = b.capacity - min(uint(updatedDBBucket.Value), b.capacity)
	return b.state(), nil
}

func (b *bucket) state() leakybucket.BucketState {
	return leakybucket.BucketState{
		Capacity:  b.Capacity(),
		Remaining: b.Remaining(),
		Reset:     b.Reset(),
	}
}

var _ leakybucket.Storage = &Storage{}

// Storage is a dyanamodb-based, thread-safe leaky bucket factory.
type Storage struct {
	db *bucketDB
}

// Create a bucket. It will determine the current state of the bucket based on:
// - The corresponding bucket in the database
// - From scratch using the values provided
func (s *Storage) Create(name string, capacity uint, rate time.Duration) (leakybucket.Bucket, error) {
	bucket := &bucket{
		name:      name,
		capacity:  capacity,
		remaining: capacity,
		reset:     time.Now().Add(rate),
		rate:      rate,
		db:        s.db,
	}
	dbBucket, err := s.db.createOrFindBucket(newDDBBucket(name, bucket.reset))
	if err != nil {
		return nil, err
	}
	// guarantee the bucket is in a good state
	if dbBucket.expired() {
		// adding 0 will reset the persisted bucket
		if _, err := bucket.Add(0); err != nil {
			return nil, err
		}
	}
	bucket.remaining = max(capacity-dbBucket.Value, 0)
	bucket.reset = dbBucket.Expiration

	return bucket, nil
}

// New initializes the connection to dynamodb
func New(tableName string, s *session.Session) (*Storage, error) {
	ddb := dynamodb.New(s)

	db := &bucketDB{
		ddb:       ddb,
		tableName: tableName,
	}

	// fail early if the table doesn't exist or we have any other issues with the DynamoDB API
	if _, err := ddb.DescribeTable(&dynamodb.DescribeTableInput{
		TableName: aws.String(tableName),
	}); err != nil {
		return nil, err
	}

	return &Storage{
		db: db,
	}, nil
}

func max(a, b uint) uint {
	if a > b {
		return a
	}
	return b
}

func min(a, b uint) uint {
	if a < b {
		return a
	}
	return b
}
<commit_msg>Add - return empty leakybucket.BucketState rather than populated values<commit_after>package dynamodb

import (
	"sync"
	"time"

	"github.com/Clever/leakybucket"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
)

var _ leakybucket.Bucket = &bucket{}

type bucket struct {
	name                string
	capacity, remaining uint
	reset               time.Time
	rate                time.Duration
	db                  *bucketDB
	mutex               sync.Mutex
}

// Capacity ...
func (b *bucket) Capacity() uint {
	return b.capacity
}

// Remaining space in the bucket.
func (b *bucket) Remaining() uint {
	return b.remaining
}

// Reset returns when the bucket will be drained.
func (b *bucket) Reset() time.Time {
	return b.reset
}

// Add to the bucket.
func (b *bucket) Add(amount uint) (leakybucket.BucketState, error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	dbBucket, err := b.db.bucket(b.name)
	if err != nil {
		return leakybucket.BucketState{}, err
	}
	if dbBucket.expired() {
		dbBucket, err = b.db.flushBucket(*dbBucket, time.Now().Add(b.rate))
		if err != nil {
			return leakybucket.BucketState{}, err
		}
	}
	// update local state
	b.remaining = b.capacity - min(dbBucket.Value, b.capacity)
	b.reset = dbBucket.Expiration
	if amount > b.remaining {
		return leakybucket.BucketState{}, leakybucket.ErrorFull
	}
	updatedDBBucket, err := b.db.incrementBucketValue(b.name, amount, b.capacity)
	if err != nil {
		if err == errBucketCapacityExceeded {
			return b.state(), leakybucket.ErrorFull
		}
		return leakybucket.BucketState{}, err
	}
	// ensure we can't overflow
	b.remaining = b.capacity - min(uint(updatedDBBucket.Value), b.capacity)
	return b.state(), nil
}

func (b *bucket) state() leakybucket.BucketState {
	return leakybucket.BucketState{
		Capacity:  b.Capacity(),
		Remaining: b.Remaining(),
		Reset:     b.Reset(),
	}
}

var _ leakybucket.Storage = &Storage{}

// Storage is a dyanamodb-based, thread-safe leaky bucket factory.
type Storage struct {
	db *bucketDB
}

// Create a bucket. It will determine the current state of the bucket based on:
// - The corresponding bucket in the database
// - From scratch using the values provided
func (s *Storage) Create(name string, capacity uint, rate time.Duration) (leakybucket.Bucket, error) {
	bucket := &bucket{
		name:      name,
		capacity:  capacity,
		remaining: capacity,
		reset:     time.Now().Add(rate),
		rate:      rate,
		db:        s.db,
	}
	dbBucket, err := s.db.createOrFindBucket(newDDBBucket(name, bucket.reset))
	if err != nil {
		return nil, err
	}
	// guarantee the bucket is in a good state
	if dbBucket.expired() {
		// adding 0 will reset the persisted bucket
		if _, err := bucket.Add(0); err != nil {
			return nil, err
		}
	}
	bucket.remaining = max(capacity-dbBucket.Value, 0)
	bucket.reset = dbBucket.Expiration

	return bucket, nil
}

// New initializes the connection to dynamodb
func New(tableName string, s *session.Session) (*Storage, error) {
	ddb := dynamodb.New(s)

	db := &bucketDB{
		ddb:       ddb,
		tableName: tableName,
	}

	// fail early if the table doesn't exist or we have any other issues with the DynamoDB API
	if _, err := ddb.DescribeTable(&dynamodb.DescribeTableInput{
		TableName: aws.String(tableName),
	}); err != nil {
		return nil, err
	}

	return &Storage{
		db: db,
	}, nil
}

func max(a, b uint) uint {
	if a > b {
		return a
	}
	return b
}

func min(a, b uint) uint {
	if a < b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package TF2RconWrapper

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/james4k/rcon"
)

// TF2RconConnection represents a rcon connection to a TF2 server
type TF2RconConnection struct {
	rc   *rcon.RemoteConsole
	host string
}

var (
	ErrUnknownCommand = errors.New("Unknown Command")
	userIDRegex       = regexp.MustCompile(`^#\s+([0-9]+)`)
	nameRegex         = regexp.MustCompile(`"(.*)"`)
	uniqueIDRegex     = regexp.MustCompile(`\[U:\d*:\d*[:1]*\]`)
	IPRegex           = regexp.MustCompile(`\d+\.\d+.\d+.\d+`)
	CVarValueRegex    = regexp.MustCompile(`^"(?:.*?)" = "(.*?)"`)
)

// Query executes a query and returns the server responses
func (c *TF2RconConnection) Query(req string) (string, error) {
	reqID, reqErr := c.rc.Write(req)
	if reqErr != nil {
		// log.Println(reqErr)
		return "", reqErr
	}

	resp, respID, respErr := c.rc.Read()
	if respErr != nil {
		// log.Println(respErr)
		return "", respErr
	}

	counter := 10
	// retry 10 times
	for {
		if reqID == respID {
			break
		} else if counter < 0 {
			return "", errors.New("Couldn't get a response.")
		} else {
			counter--
			resp, respID, respErr = c.rc.Read()
			if respErr != nil {
				// log.Println(respErr)
				return "", reqErr
			}
		}
	}

	if strings.HasPrefix(resp, "Unknown command") {
		return resp, ErrUnknownCommand
	}

	return resp, nil
}

func (c *TF2RconConnection) GetConVar(cvar string) (string, error) {
	raw, err := c.Query(cvar)

	if err != nil {
		return "", err
	}

	// Querying just a variable's name sends back a message like the
	// following:
	//
	// "cvar_name" = "current value" ( def. "default value" )
	//  var flags like notify replicated
	//  - short description of cvar

	firstLine := strings.Split(raw, "\n")[0]
	matches := CVarValueRegex.FindStringSubmatch(firstLine)
	if len(matches) != 2 {
		return "", errors.New("Unknown cvar.")
	}

	return matches[1], nil
}

func (c *TF2RconConnection) SetConVar(cvar string, val string) (string, error) {
	return c.Query(fmt.Sprintf("%s \"%s\"", cvar, val))
}

// GetPlayers returns a list of players in the server. Includes bots.
func (c *TF2RconConnection) GetPlayers() ([]Player, error) {
	playerString, err := c.Query("status")
	if err != nil {
		return nil, err
	}

	res := strings.Split(playerString, "\n")
	for !strings.HasPrefix(res[0], "#") {
		res = res[1:]
	}
	res = res[1:]
	var list []Player
	for _, line := range res {
		if line == "" {
			continue
		}
		if !strings.HasPrefix(line, "#") {
			break
		}

		var matches []string

		matches = userIDRegex.FindStringSubmatch(line)
		if matches == nil {
			continue
		}

		userID := matches[len(matches)-1]
		loc := nameRegex.FindStringSubmatchIndex(line)
		if loc == nil {
			continue
		}

		name := line[loc[0]:loc[1]]
		matches = uniqueIDRegex.FindStringSubmatch(line[loc[1]:])
		if matches == nil {
			list = append(list, Player{userID, name, "BOT", ""})
			continue
		}

		uniqueID := matches[len(matches)-1]
		ip := IPRegex.FindString(line[loc[1]:])
		list = append(list, Player{userID, name, uniqueID, ip})

	}
	return list, nil
}

// KickPlayer kicks a player
func (c *TF2RconConnection) KickPlayer(p Player, message string) error {
	return c.KickPlayerID(p.UserID, message)
}

// Kicks a player with the given player ID
func (c *TF2RconConnection) KickPlayerID(userID string, message string) error {
	query := fmt.Sprintf("kickid %s %s", userID, message)
	_, err := c.Query(query)
	return err
}

// BanPlayer bans a player
func (c *TF2RconConnection) BanPlayer(minutes int, p Player, message string) error {
	query := "banid " + fmt.Sprintf("%v", minutes) + " " + p.UserID
	if message != "" {
		query += " \"" + message + "\""
	}
	_, err := c.Query(query)
	return err
}

// UnbanPlayer unbans a player
func (c *TF2RconConnection) UnbanPlayer(p Player) error {
	query := "unbanid " + p.UserID
	_, err := c.Query(query)
	return err
}

// Say sends a message to the TF2 server chat
func (c *TF2RconConnection) Say(message string) error {
	query := "say " + message
	_, err := c.Query(query)
	return err
}

// ChangeRconPassword changes the rcon password and updates the current connection
// to use the new password
func (c *TF2RconConnection) ChangeRconPassword(password string) error {
	_, err := c.SetConVar("rcon_password", password)

	if err == nil {
		c.rc.Close()
		newConnection, _ := rcon.Dial(c.host, password)
		c.rc = newConnection
	}

	return err
}

// ChangeMap changes the map
func (c *TF2RconConnection) ChangeMap(mapname string) error {
	query := "changelevel \"" + mapname + "\""
	res, err := c.Query(query)
	if res != "" {
		return errors.New("Map not found.")
	}
	return err
}

// ChangeServerPassword changes the server password
func (c *TF2RconConnection) ChangeServerPassword(password string) error {
	_, err := c.SetConVar("sv_password", password)
	return err
}

// GetServerPassword returns the server password
func (c *TF2RconConnection) GetServerPassword() (string, error) {
	return c.GetConVar("sv_password")
}

// RedirectLogs send the logaddress_add command
func (c *TF2RconConnection) RedirectLogs(ip string, port string) error {
	query := "logaddress_add " + ip + ":" + port
	_, err := c.Query(query)
	return err
}

func (c *TF2RconConnection) StopLogRedirection(localip string, port string) {
	query := fmt.Sprintf("logaddress_del %s:%s", localip, port)
	c.Query(query)
}

// Close closes the connection
func (c *TF2RconConnection) Close() {
	c.rc.Close()
}

// ExecConfig accepts a string and executes its lines one by one. Assumes
// UNiX line endings
func (c *TF2RconConnection) ExecConfig(config string) error {
	lines := strings.Split(config, "\n")
	for _, line := range lines {
		_, err := c.Query(line)
		if err != nil {
			return err
		}
	}
	return nil
}

// NewTF2RconConnection builds a new TF2RconConnection to a server at address ("ip:port") using
// a rcon_password password
func NewTF2RconConnection(address, password string) (*TF2RconConnection, error) {
	rc, err := rcon.Dial(address, password)
	if err != nil {
		return nil, err
	}
	return &TF2RconConnection{rc, address}, nil
}
<commit_msg>Add Sayf<commit_after>package TF2RconWrapper

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/james4k/rcon"
)

// TF2RconConnection represents a rcon connection to a TF2 server
type TF2RconConnection struct {
	rc   *rcon.RemoteConsole
	host string
}

var (
	ErrUnknownCommand = errors.New("Unknown Command")
	userIDRegex       = regexp.MustCompile(`^#\s+([0-9]+)`)
	nameRegex         = regexp.MustCompile(`"(.*)"`)
	uniqueIDRegex     = regexp.MustCompile(`\[U:\d*:\d*[:1]*\]`)
	IPRegex           = regexp.MustCompile(`\d+\.\d+.\d+.\d+`)
	CVarValueRegex    = regexp.MustCompile(`^"(?:.*?)" = "(.*?)"`)
)

// Query executes a query and returns the server responses
func (c *TF2RconConnection) Query(req string) (string, error) {
	reqID, reqErr := c.rc.Write(req)
	if reqErr != nil {
		// log.Println(reqErr)
		return "", reqErr
	}

	resp, respID, respErr := c.rc.Read()
	if respErr != nil {
		// log.Println(respErr)
		return "", respErr
	}

	counter := 10
	// retry 10 times
	for {
		if reqID == respID {
			break
		} else if counter < 0 {
			return "", errors.New("Couldn't get a response.")
		} else {
			counter--
			resp, respID, respErr = c.rc.Read()
			if respErr != nil {
				// log.Println(respErr)
				return "", reqErr
			}
		}
	}

	if strings.HasPrefix(resp, "Unknown command") {
		return resp, ErrUnknownCommand
	}

	return resp, nil
}

func (c *TF2RconConnection) GetConVar(cvar string) (string, error) {
	raw, err := c.Query(cvar)

	if err != nil {
		return "", err
	}

	// Querying just a variable's name sends back a message like the
	// following:
	//
	// "cvar_name" = "current value" ( def. "default value" )
	//  var flags like notify replicated
	//  - short description of cvar

	firstLine := strings.Split(raw, "\n")[0]
	matches := CVarValueRegex.FindStringSubmatch(firstLine)
	if len(matches) != 2 {
		return "", errors.New("Unknown cvar.")
	}

	return matches[1], nil
}

func (c *TF2RconConnection) SetConVar(cvar string, val string) (string, error) {
	return c.Query(fmt.Sprintf("%s \"%s\"", cvar, val))
}

// GetPlayers returns a list of players in the server. Includes bots.
func (c *TF2RconConnection) GetPlayers() ([]Player, error) {
	playerString, err := c.Query("status")
	if err != nil {
		return nil, err
	}

	res := strings.Split(playerString, "\n")
	for !strings.HasPrefix(res[0], "#") {
		res = res[1:]
	}
	res = res[1:]
	var list []Player
	for _, line := range res {
		if line == "" {
			continue
		}
		if !strings.HasPrefix(line, "#") {
			break
		}

		var matches []string

		matches = userIDRegex.FindStringSubmatch(line)
		if matches == nil {
			continue
		}

		userID := matches[len(matches)-1]
		loc := nameRegex.FindStringSubmatchIndex(line)
		if loc == nil {
			continue
		}

		name := line[loc[0]:loc[1]]
		matches = uniqueIDRegex.FindStringSubmatch(line[loc[1]:])
		if matches == nil {
			list = append(list, Player{userID, name, "BOT", ""})
			continue
		}

		uniqueID := matches[len(matches)-1]
		ip := IPRegex.FindString(line[loc[1]:])
		list = append(list, Player{userID, name, uniqueID, ip})

	}
	return list, nil
}

// KickPlayer kicks a player
func (c *TF2RconConnection) KickPlayer(p Player, message string) error {
	return c.KickPlayerID(p.UserID, message)
}

// Kicks a player with the given player ID
func (c *TF2RconConnection) KickPlayerID(userID string, message string) error {
	query := fmt.Sprintf("kickid %s %s", userID, message)
	_, err := c.Query(query)
	return err
}

// BanPlayer bans a player
func (c *TF2RconConnection) BanPlayer(minutes int, p Player, message string) error {
	query := "banid " + fmt.Sprintf("%v", minutes) + " " + p.UserID
	if message != "" {
		query += " \"" + message + "\""
	}
	_, err := c.Query(query)
	return err
}

// UnbanPlayer unbans a player
func (c *TF2RconConnection) UnbanPlayer(p Player) error {
	query := "unbanid " + p.UserID
	_, err := c.Query(query)
	return err
}

// Say sends a message to the TF2 server chat
func (c *TF2RconConnection) Say(message string) error {
	query := "say " + message
	_, err := c.Query(query)
	return err
}

func (c *TF2RconConnection) Sayf(format string, a ...interface{}) error {
	err := c.Say(fmt.Sprintf(format, a...))
	return err
}

// ChangeRconPassword changes the rcon password and updates the current connection
// to use the new password
func (c *TF2RconConnection) ChangeRconPassword(password string) error {
	_, err := c.SetConVar("rcon_password", password)

	if err == nil {
		c.rc.Close()
		newConnection, _ := rcon.Dial(c.host, password)
		c.rc = newConnection
	}

	return err
}

// ChangeMap changes the map
func (c *TF2RconConnection) ChangeMap(mapname string) error {
	query := "changelevel \"" + mapname + "\""
	res, err := c.Query(query)
	if res != "" {
		return errors.New("Map not found.")
	}
	return err
}

// ChangeServerPassword changes the server password
func (c *TF2RconConnection) ChangeServerPassword(password string) error {
	_, err := c.SetConVar("sv_password", password)
	return err
}

// GetServerPassword returns the server password
func (c *TF2RconConnection) GetServerPassword() (string, error) {
	return c.GetConVar("sv_password")
}

// RedirectLogs send the logaddress_add command
func (c *TF2RconConnection) RedirectLogs(ip string, port string) error {
	query := "logaddress_add " + ip + ":" + port
	_, err := c.Query(query)
	return err
}

func (c *TF2RconConnection) StopLogRedirection(localip string, port string) {
	query := fmt.Sprintf("logaddress_del %s:%s", localip, port)
	c.Query(query)
}

// Close closes the connection
func (c *TF2RconConnection) Close() {
	c.rc.Close()
}

// ExecConfig accepts a string and executes its lines one by one. Assumes
// UNiX line endings
func (c *TF2RconConnection) ExecConfig(config string) error {
	lines := strings.Split(config, "\n")
	for _, line := range lines {
		_, err := c.Query(line)
		if err != nil {
			return err
		}
	}
	return nil
}

// NewTF2RconConnection builds a new TF2RconConnection to a server at address ("ip:port") using
// a rcon_password password
func NewTF2RconConnection(address, password string) (*TF2RconConnection, error) {
	rc, err := rcon.Dial(address, password)
	if err != nil {
		return nil, err
	}
	return &TF2RconConnection{rc, address}, nil
}
<|endoftext|>


<commit_before>package wxalogin

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

	"gopkg.in/goyy/goyy.v0/comm/xtype"
	"gopkg.in/goyy/goyy.v0/util/strings"
	"gopkg.in/goyy/goyy.v0/util/times"
	"gopkg.in/goyy/goyy.v0/web/controller"
	"gopkg.in/goyy/goyy.v0/web/xhttp"
)

func init() {
	xhttp.GET("/wxalogin", ctl.login)
}

func (me *Controller) login(c xhttp.Context) {
	code := c.Param("code")
	r := getWxInfo(code)
	if r.Success {
		var p *xtype.Principal
		if Wx2Principal != nil {
			p = Wx2Principal(r.OpenId, r.UnionId, r.SessionKey)
		} else {
			p = getPrincipal(r)
		}
		err := c.Session().SetPrincipal(*p)
		if err != nil {
			logger.Errorln("login.SetPrincipal err:", err)
		}
	}
	sid := c.Session().Id()
	err := c.JSON(xhttp.StatusOK, res{Success: true, Message: "", Token: sid})
	if err != nil {
		logger.Errorln("response err:", err)
	}
}

func getPrincipal(r *result) *xtype.Principal {
	id, ok := getUserId(r.UnionId)
	if !ok {
		id = createUser(r.UnionId, r.OpenId)
	}
	p := &xtype.Principal{
		Id:        id,
		Code:      r.OpenId,
		Key:       r.SessionKey,
		LoginName: r.UnionId,
		LoginTime: times.NowUnixStr(),
	}
	return p
}

func getWxInfo(code string) *result {
	// http request
	url := "https://api.weixin.qq.com/sns/jscode2session"
	query := fmt.Sprintf("?appid=%v&secret=%v&js_code=%v&grant_type=authorization_code", AppId, AppSecret, code)
	fmt.Println(query)
	resp, err := http.Get(url + query)
	if err != nil {
		logger.Errorln("getWxInfo.Get err:", err)
		return &result{Success: false}
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Errorln("getWxInfo.ReadAll err:", err)
		return &result{Success: false}
	}
	// json unmarshal
	r := &result{Success: true}
	err = json.Unmarshal(body, r)
	if err != nil {
		logger.Errorln("getWxInfo.Unmarshal err:", err)
		return &result{Success: false}
	}
	if strings.IsBlank(r.OpenId) {
		r.Success = false
	} else {
		if strings.IsBlank(r.UnionId) {
			r.UnionId = r.OpenId
		}
	}
	return r
}

type res struct {
	Success bool   `json:"success"`
	Token   string `json:"token"`
	Message string `json:"message"`
}

type result struct {
	Success    bool   `json:"success"`
	OpenId     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}

type Controller struct {
	controller.JSONController
}

var ctl = &Controller{}
<commit_msg>Determine whether WeChat code is empty<commit_after>package wxalogin

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

	"gopkg.in/goyy/goyy.v0/comm/xtype"
	"gopkg.in/goyy/goyy.v0/util/strings"
	"gopkg.in/goyy/goyy.v0/util/times"
	"gopkg.in/goyy/goyy.v0/web/controller"
	"gopkg.in/goyy/goyy.v0/web/xhttp"
)

func init() {
	xhttp.GET("/wxalogin", ctl.login)
}

func (me *Controller) login(c xhttp.Context) {
	code := c.Param("code")
	r := res{Success: true}
	if strings.IsBlank(code) || code == "null" || code == "undefined" {
		logger.Errorln("login.code err:", code)
		r.Success = false
	} else {
		r := getWxInfo(code)
		if r.Success {
			var p *xtype.Principal
			if Wx2Principal != nil {
				p = Wx2Principal(r.OpenId, r.UnionId, r.SessionKey)
			} else {
				p = getPrincipal(r)
			}
			err := c.Session().SetPrincipal(*p)
			if err != nil {
				logger.Errorln("login.SetPrincipal err:", err)
			}
		}
	}
	r.Token = c.Session().Id()
	err := c.JSON(xhttp.StatusOK, r)
	if err != nil {
		logger.Errorln("response err:", err)
	}
}

func getPrincipal(r *result) *xtype.Principal {
	id, ok := getUserId(r.UnionId)
	if !ok {
		id = createUser(r.UnionId, r.OpenId)
	}
	p := &xtype.Principal{
		Id:        id,
		Code:      r.OpenId,
		Key:       r.SessionKey,
		LoginName: r.UnionId,
		LoginTime: times.NowUnixStr(),
	}
	return p
}

func getWxInfo(code string) *result {
	// http request
	url := "https://api.weixin.qq.com/sns/jscode2session"
	query := fmt.Sprintf("?appid=%v&secret=%v&js_code=%v&grant_type=authorization_code", AppId, AppSecret, code)
	fmt.Println(query)
	resp, err := http.Get(url + query)
	if err != nil {
		logger.Errorln("getWxInfo.Get err:", err)
		return &result{Success: false}
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Errorln("getWxInfo.ReadAll err:", err)
		return &result{Success: false}
	}
	// json unmarshal
	r := &result{Success: true}
	err = json.Unmarshal(body, r)
	if err != nil {
		logger.Errorln("getWxInfo.Unmarshal err:", err)
		return &result{Success: false}
	}
	if strings.IsBlank(r.OpenId) {
		r.Success = false
	} else {
		if strings.IsBlank(r.UnionId) {
			r.UnionId = r.OpenId
		}
	}
	return r
}

type res struct {
	Success bool   `json:"success"`
	Token   string `json:"token"`
	Message string `json:"message"`
}

type result struct {
	Success    bool   `json:"success"`
	OpenId     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}

type Controller struct {
	controller.JSONController
}

var ctl = &Controller{}
<|endoftext|>


<commit_before><commit_msg>clean up elo<commit_after><|endoftext|>