<commit_before>package main

// Directory Index

import (
	"bytes"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/xyproto/pinterface"
)

// Directory listing
func directoryListing(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {
	var buf bytes.Buffer
	for _, filename := range getFilenames(dirname) {

		// Find the full name
		fullFilename := dirname

		// Add a "/" after the directory name, if missing
		if !strings.HasSuffix(fullFilename, pathsep) {
			fullFilename += pathsep
		}

		// Add the filename at the end
		fullFilename += filename

		// Remove the root directory from the link path
		urlpath := fullFilename[len(rootdir)+1:]

		// Output different entries for files and directories
		buf.WriteString(easyLink(filename, urlpath, fs.isDir(fullFilename)))
	}
	title := dirname
	// Strip the leading "./"
	if strings.HasPrefix(title, "."+pathsep) {
		title = title[1+len(pathsep):]
	}
	// Strip double "/" at the end, just keep one
	// Replace "//" with just "/"
	if strings.Contains(title, pathsep+pathsep) {
		title = strings.Replace(title, pathsep+pathsep, pathsep, everyInstance)
	}

	// Use the application title for the main page
	//if title == "" {
	//	title = versionString
	//}

	var htmldata []byte
	if buf.Len() > 0 {
		htmldata = []byte(easyPage(title, buf.String(), theme))
	} else {
		htmldata = []byte(easyPage(title, "Empty directory", theme))
	}

	// If the auto-refresh feature has been enabled
	if autoRefreshMode {
		// Insert JavaScript for refreshing the page into the generated HTML
		htmldata = insertAutoRefresh(req, htmldata)
	}

	// Serve the page
	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	NewDataBlock(htmldata).ToClient(w, req)
}

// Serve a directory. The directory must exist.
// rootdir is the base directory (can be ".")
// dirname is the specific directory that is to be served (should never be ".")
func dirPage(w http.ResponseWriter, req *http.Request, rootdir, dirname string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, theme string) {

	if quitAfterFirstRequest {
		go quitSoon("Quit after first request", defaultSoonDuration)
	}

	// If the URL does not end with a slash, redirect to an URL that does
	if !strings.HasSuffix(req.URL.Path, "/") {
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}
	// Handle the serving of index files, if needed
	for _, indexfile := range indexFilenames {
		filename := filepath.Join(dirname, indexfile)
		if fs.exists(filename) {
			filePage(w, req, filename, perm, luapool, cache)
			return
		}
	}
	// Serve a directory listing of no index file is found
	directoryListing(w, req, rootdir, dirname, theme)
}
<commit_msg>Add a warning when POST-ing without a trailing slash<commit_after>package main

// Directory Index

import (
	"bytes"
	log "github.com/sirupsen/logrus"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/xyproto/pinterface"
)

// Directory listing
func directoryListing(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {
	var buf bytes.Buffer
	for _, filename := range getFilenames(dirname) {

		// Find the full name
		fullFilename := dirname

		// Add a "/" after the directory name, if missing
		if !strings.HasSuffix(fullFilename, pathsep) {
			fullFilename += pathsep
		}

		// Add the filename at the end
		fullFilename += filename

		// Remove the root directory from the link path
		urlpath := fullFilename[len(rootdir)+1:]

		// Output different entries for files and directories
		buf.WriteString(easyLink(filename, urlpath, fs.isDir(fullFilename)))
	}
	title := dirname
	// Strip the leading "./"
	if strings.HasPrefix(title, "."+pathsep) {
		title = title[1+len(pathsep):]
	}
	// Strip double "/" at the end, just keep one
	// Replace "//" with just "/"
	if strings.Contains(title, pathsep+pathsep) {
		title = strings.Replace(title, pathsep+pathsep, pathsep, everyInstance)
	}

	// Use the application title for the main page
	//if title == "" {
	//	title = versionString
	//}

	var htmldata []byte
	if buf.Len() > 0 {
		htmldata = []byte(easyPage(title, buf.String(), theme))
	} else {
		htmldata = []byte(easyPage(title, "Empty directory", theme))
	}

	// If the auto-refresh feature has been enabled
	if autoRefreshMode {
		// Insert JavaScript for refreshing the page into the generated HTML
		htmldata = insertAutoRefresh(req, htmldata)
	}

	// Serve the page
	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	NewDataBlock(htmldata).ToClient(w, req)
}

// Serve a directory. The directory must exist.
// rootdir is the base directory (can be ".")
// dirname is the specific directory that is to be served (should never be ".")
func dirPage(w http.ResponseWriter, req *http.Request, rootdir, dirname string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, theme string) {

	if quitAfterFirstRequest {
		go quitSoon("Quit after first request", defaultSoonDuration)
	}

	// If the URL does not end with a slash, redirect to an URL that does
	if !strings.HasSuffix(req.URL.Path, "/") {
		if req.Method == "POST" {
			log.Warn("Redirecting a POST request: " + req.URL.Path + " -> " + req.URL.Path + "/.")
			log.Warn("Header data may be lost! Please add the missing slash.")
		}
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}
	// Handle the serving of index files, if needed
	for _, indexfile := range indexFilenames {
		filename := filepath.Join(dirname, indexfile)
		if fs.exists(filename) {
			filePage(w, req, filename, perm, luapool, cache)
			return
		}
	}
	// Serve a directory listing of no index file is found
	directoryListing(w, req, rootdir, dirname, theme)
}
<|endoftext|>


<commit_before>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<commit_msg>Added mediaconnect to brokenAuthHeaderProviders list<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
	"https://api-test.mediaconnect.no/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<|endoftext|>


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

package main

import (
  "flag"
  "log"
  "net/http"

  "github.com/gorilla/mux"
)

var (
  addr      = flag.String("address", ":8080", "Address to bind to.")
  staticDir = flag.String("static_dir", "client", "Root directory for static files.")
  muxer     = mux.NewRouter()
)

func quitQuitQuitHandler(w http.ResponseWriter, r *http.Request) {
  log.Fatalf("%v requested we quit.", r.RemoteAddr)
}

func main() {
  flag.Parse()
  muxer.HandleFunc("/quitquitquit", quitQuitQuitHandler)
  muxer.Handle("/", http.FileServer(http.Dir(*staticDir)))
  http.Handle("/", muxer)
  log.Printf("Server now listening on %v", *addr)
  log.Fatal(http.ListenAndServe(*addr, nil))
}
<commit_msg>go fmt<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

var (
	addr      = flag.String("address", ":8080", "Address to bind to.")
	staticDir = flag.String("static_dir", "client", "Root directory for static files.")
	muxer     = mux.NewRouter()
)

func quitQuitQuitHandler(w http.ResponseWriter, r *http.Request) {
	log.Fatalf("%v requested we quit.", r.RemoteAddr)
}

func main() {
	flag.Parse()
	muxer.HandleFunc("/quitquitquit", quitQuitQuitHandler)
	muxer.Handle("/", http.FileServer(http.Dir(*staticDir)))
	http.Handle("/", muxer)
	log.Printf("Server now listening on %v", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/writer_test.go                                      *
 *                                                        *
 * hprose writer test for Go.                             *
 *                                                        *
 * LastModified: Aug 17, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package io

import (
	"bytes"
	"strconv"
	"testing"
)

func testSerializeNil(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(nil)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "n" {
		t.Error(b.String())
	}
}
func testSerializeTrue(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(true)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "t" {
		t.Error(b.String())
	}
}
func testSerializeFalse(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(false)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "f" {
		t.Error(b.String())
	}
}
func testSerializeDigit(t *testing.T, writer *Writer, b *bytes.Buffer) {
	for i := 0; i <= 9; i++ {
		b.Truncate(0)
		err := writer.Serialize(i)
		if err != nil {
			t.Error(err.Error())
		}
		if b.String() != strconv.Itoa(i) {
			t.Error(b.String())
		}
	}
}

func TestSerialize(t *testing.T) {
	b := new(bytes.Buffer)
	writer := NewWriter(b, false)
	testSerializeNil(t, writer, b)
	testSerializeTrue(t, writer, b)
	testSerializeFalse(t, writer, b)
	testSerializeDigit(t, writer, b)

}
<commit_msg>Update writer_test<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/writer_test.go                                      *
 *                                                        *
 * hprose writer test for Go.                             *
 *                                                        *
 * LastModified: Aug 17, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package io

import (
	"bytes"
	"math"
	"math/rand"
	"strconv"
	"testing"
)

func testSerializeNil(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(nil)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "n" {
		t.Error(b.String())
	}
}
func testSerializeTrue(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(true)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "t" {
		t.Error(b.String())
	}
}
func testSerializeFalse(t *testing.T, writer *Writer, b *bytes.Buffer) {
	b.Truncate(0)
	err := writer.Serialize(false)
	if err != nil {
		t.Error(err.Error())
	}
	if b.String() != "f" {
		t.Error(b.String())
	}
}
func testSerializeDigit(t *testing.T, writer *Writer, b *bytes.Buffer) {
	for i := 0; i <= 9; i++ {
		b.Truncate(0)
		err := writer.Serialize(i)
		if err != nil {
			t.Error(err.Error())
		}
		if b.String() != strconv.Itoa(i) {
			t.Error(b.String())
		}
	}
}
func testSerializeInt(t *testing.T, writer *Writer, b *bytes.Buffer) {
	for i := 0; i <= 100; i++ {
		b.Truncate(0)
		x := rand.Intn(math.MaxInt32-10) + 10
		err := writer.Serialize(x)
		if err != nil {
			t.Error(err.Error())
		}
		if b.String() != "i"+strconv.Itoa(x)+";" {
			t.Error(b.String())
		}
	}
}

func TestSerialize(t *testing.T) {
	b := new(bytes.Buffer)
	writer := NewWriter(b, false)
	testSerializeNil(t, writer, b)
	testSerializeTrue(t, writer, b)
	testSerializeFalse(t, writer, b)
	testSerializeDigit(t, writer, b)
	testSerializeInt(t, writer, b)
}
<|endoftext|>


<commit_before>package kut

import (
	"bytes"
	"io"
	"testing"
)

func TestNewCutter(t *testing.T) {
	if c := NewCutter(new(bytes.Buffer), new(bytes.Buffer)); c == nil {
		t.Errorf("NewCutter returned nil")
	}
}

var scanTests = []struct {
	input  string
	ranges []ColRange
	err    error
	output string
}{
	{"", []ColRange{}, io.EOF, ""},
	{"\n", []ColRange{}, nil, "\n"},
	{"abc", []ColRange{{1, 1}}, nil, "abc\n"},
	{"abc,def,ghi", []ColRange{{1, 1}, {3, 3}}, nil, "abc,ghi\n"},
	{"abc,def,ghi", []ColRange{{1, EOL}}, nil, "abc,def,ghi\n"},
	{"abc,def,ghi", []ColRange{{2, 2}}, nil, "def\n"},
	{"abc,def,ghi", []ColRange{{2, 3}}, nil, "def,ghi\n"},
	{"abc,def,ghi", []ColRange{{3, 2}}, nil, "\n"},
	{"abc,def,ghi", []ColRange{{3, EOL}}, nil, "ghi\n"},
	{"abc,def,ghi", []ColRange{{4, EOL}}, nil, "\n"},
	{"abc,def,ghi", []ColRange{}, nil, "\n"},
	{"abc,def,ghi,j\"k", []ColRange{{2, 4}}, nil, "def,ghi,\"j\"\"k\"\n"},
}

func TestScan(t *testing.T) {
	for i, test := range scanTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		if err := c.Scan(); err != test.err {
			t.Errorf("TestScan %v: expected return value %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestScan %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}

var scanAllTests = []struct {
	input  string
	ranges []ColRange
	err    error
	output string
}{
	{"1,2,3\n\n\n1,2,3", []ColRange{{2, 2}}, nil, "2\n\n\n2\n"},
	{"\n\n\n", []ColRange{{2, 2}}, nil, "\n\n\n"},
	{"abc,def,ghi\njkl,mn\ro,pqr\n", []ColRange{{2, 2}}, nil, "def\n\"mn\ro\"\n"},
	{"abc,def,ghi\njkl,mno,pqr", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\njkl,mno,pqr\n", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\r\njkl,mno,pqr", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\r\njkl,mno,pqr\r\n", []ColRange{{2, 2}}, nil, "def\nmno\n"},
}

func TestScanAll(t *testing.T) {
	for i, test := range scanAllTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		if err := c.ScanAll(); err != test.err {
			t.Errorf("TestScanAll %v: expected return value %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestScanAll %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}

var setDelimiterTests = []struct {
	input     string
	ranges    []ColRange
	delimiter rune
	err       error
	output    string
}{
	{"abc;def,ghi;123\njkl;mno,pqr;45678\n", []ColRange{{2, 2}}, ';', nil, "def,ghi\nmno,pqr\n"},
}

func TestSetDelimiter(t *testing.T) {
	for i, test := range setDelimiterTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		c.SetDelimiter(test.delimiter)
		if err := c.ScanAll(); err != test.err {
			t.Errorf("TestSetDelimiter %v: expected return value %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestSetDelimiter %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}
<commit_msg>Make test output more generic<commit_after>package kut

import (
	"bytes"
	"io"
	"testing"
)

func TestNewCutter(t *testing.T) {
	if c := NewCutter(new(bytes.Buffer), new(bytes.Buffer)); c == nil {
		t.Errorf("NewCutter returned nil")
	}
}

var scanTests = []struct {
	input  string
	ranges []ColRange
	err    error
	output string
}{
	{"", []ColRange{}, io.EOF, ""},
	{"\n", []ColRange{}, nil, "\n"},
	{"abc", []ColRange{{1, 1}}, nil, "abc\n"},
	{"abc,def,ghi", []ColRange{{1, 1}, {3, 3}}, nil, "abc,ghi\n"},
	{"abc,def,ghi", []ColRange{{1, EOL}}, nil, "abc,def,ghi\n"},
	{"abc,def,ghi", []ColRange{{2, 2}}, nil, "def\n"},
	{"abc,def,ghi", []ColRange{{2, 3}}, nil, "def,ghi\n"},
	{"abc,def,ghi", []ColRange{{3, 2}}, nil, "\n"},
	{"abc,def,ghi", []ColRange{{3, EOL}}, nil, "ghi\n"},
	{"abc,def,ghi", []ColRange{{4, EOL}}, nil, "\n"},
	{"abc,def,ghi", []ColRange{}, nil, "\n"},
	{"abc,def,ghi,j\"k", []ColRange{{2, 4}}, nil, "def,ghi,\"j\"\"k\"\n"},
}

func TestScan(t *testing.T) {
	for i, test := range scanTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		if err := c.Scan(); err != test.err {
			t.Errorf("TestScan %v: expected error %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestScan %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}

var scanAllTests = []struct {
	input  string
	ranges []ColRange
	err    error
	output string
}{
	{"1,2,3\n\n\n1,2,3", []ColRange{{2, 2}}, nil, "2\n\n\n2\n"},
	{"\n\n\n", []ColRange{{2, 2}}, nil, "\n\n\n"},
	{"abc,def,ghi\njkl,mn\ro,pqr\n", []ColRange{{2, 2}}, nil, "def\n\"mn\ro\"\n"},
	{"abc,def,ghi\njkl,mno,pqr", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\njkl,mno,pqr\n", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\r\njkl,mno,pqr", []ColRange{{2, 2}}, nil, "def\nmno\n"},
	{"abc,def,ghi\r\njkl,mno,pqr\r\n", []ColRange{{2, 2}}, nil, "def\nmno\n"},
}

func TestScanAll(t *testing.T) {
	for i, test := range scanAllTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		if err := c.ScanAll(); err != test.err {
			t.Errorf("TestScanAll %v: expected error %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestScanAll %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}

var setDelimiterTests = []struct {
	input     string
	ranges    []ColRange
	delimiter rune
	err       error
	output    string
}{
	{"abc;def,ghi;123\njkl;mno,pqr;45678\n", []ColRange{{2, 2}}, ';', nil, "def,ghi\nmno,pqr\n"},
}

func TestSetDelimiter(t *testing.T) {
	for i, test := range setDelimiterTests {
		outBuf := new(bytes.Buffer)
		c := NewCutter(bytes.NewBufferString(test.input), outBuf)
		c.Ranges = test.ranges
		c.SetDelimiter(test.delimiter)
		if err := c.ScanAll(); err != test.err {
			t.Errorf("TestSetDelimiter %v: expected error %#v but got %#v", i, test.err, err)
		}
		if outString := outBuf.String(); outString != test.output {
			t.Errorf("TestSetDelimiter %v: expected output %#v but got %#v", i, test.output, outString)

		}
	}
}
<|endoftext|>


<commit_before>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package ircmsg

import (
	"errors"
	"strings"
)

var (
	// ErrorLineIsEmpty indicates that the given IRC line was empty.
	ErrorLineIsEmpty = errors.New("Line is empty")
)

// IrcMessage represents an IRC message, as defined by the RFCs and as
// extended by the IRCv3 Message Tags specification with the introduction
// of message tags.
type IrcMessage struct {
	Tags    map[string]TagValue
	Prefix  string
	Command string
	Params  []string
}

// ParseLine creates and returns an IrcMessage from the given IRC line.
//
// Quirks:
//
//   The RFCs say that last parameters with no characters MUST be a trailing.
//   IE, they need to be prefixed with ":". We disagree with that and handle
//   incoming last empty parameters whether they are trailing or ordinary
//   parameters. However, we do follow that rule when emitting lines.
func ParseLine(line string) (IrcMessage, error) {
	line = strings.Trim(line, "\r\n")
	var ircmsg IrcMessage

	if len(line) < 1 {
		return ircmsg, ErrorLineIsEmpty
	}

	// tags
	ircmsg.Tags = make(map[string]TagValue)
	if line[0] == '@' {
		splitLine := strings.SplitN(line, " ", 2)
		tags := splitLine[0][1:]
		line = strings.TrimLeft(splitLine[1], " ")

		for _, fulltag := range strings.Split(tags, ";") {
			var name string
			var val TagValue
			if strings.Contains(fulltag, "=") {
				val.HasValue = true
				splittag := strings.SplitN(fulltag, "=", 2)
				name = splittag[0]
				val.Value = UnescapeTagValue(splittag[1])
			} else {
				name = fulltag
				val.HasValue = false
			}

			ircmsg.Tags[name] = val
		}
	}

	// prefix
	if line[0] == ':' {
		splitLine := strings.SplitN(line, " ", 2)
		ircmsg.Prefix = splitLine[0][1:]
		line = strings.TrimLeft(splitLine[1], " ")
	}

	// command
	splitLine := strings.SplitN(line, " ", 2)
	ircmsg.Command = strings.ToUpper(splitLine[0])
	if len(splitLine) > 1 {
		line = strings.TrimLeft(splitLine[1], " ")

		// parameters
		for {
			// handle trailing
			if len(line) > 0 && line[0] == ':' {
				ircmsg.Params = append(ircmsg.Params, line[1:])
				break
			}

			// regular params
			splitLine := strings.SplitN(line, " ", 2)
			ircmsg.Params = append(ircmsg.Params, splitLine[0])

			if len(splitLine) > 1 {
				line = strings.TrimLeft(splitLine[1], " ")
			} else {
				break
			}
		}
	}

	return ircmsg, nil
}

// MakeMessage provides a simple way to create a new IrcMessage.
func MakeMessage(tags *map[string]TagValue, prefix string, command string, params ...string) IrcMessage {
	var ircmsg IrcMessage

	ircmsg.Tags = make(map[string]TagValue)
	if tags != nil {
		for tag, value := range *tags {
			ircmsg.Tags[tag] = value
		}
	}

	ircmsg.Prefix = prefix
	ircmsg.Command = command
	ircmsg.Params = params

	return ircmsg
}

// Line returns a sendable line created from an IrcMessage.
func (ircmsg *IrcMessage) Line() (string, error) {
	var line string

	if len(ircmsg.Command) < 1 {
		return "", errors.New("irc: IRC messages MUST have a command")
	}

	if len(ircmsg.Tags) > 0 {
		line += "@"

		for tag, val := range ircmsg.Tags {
			line += tag

			if val.HasValue {
				line += "="
				line += EscapeTagValue(val.Value)
			}

			line += ";"
		}
		// TODO: this is ugly, but it works for now
		line = strings.TrimSuffix(line, ";")

		line += " "
	}

	if len(ircmsg.Prefix) > 0 {
		line += ":"
		line += ircmsg.Prefix
		line += " "
	}

	line += ircmsg.Command

	if len(ircmsg.Params) > 0 {
		for i, param := range ircmsg.Params {
			line += " "
			if strings.Contains(param, " ") || strings.Contains(param, ":") || len(param) < 1 {
				if i != len(ircmsg.Params)-1 {
					return "", errors.New("irc: Cannot have a param with spaces / colon or an empty param before the last parameter")
				}
				line += ":"
			}
			line += param
		}
	}

	line += "\r\n"

	return line, nil
}
<commit_msg>ircmsg: Improve robustness<commit_after>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package ircmsg

import (
	"errors"
	"strings"
)

var (
	// ErrorLineIsEmpty indicates that the given IRC line was empty.
	ErrorLineIsEmpty = errors.New("Line is empty")
)

// IrcMessage represents an IRC message, as defined by the RFCs and as
// extended by the IRCv3 Message Tags specification with the introduction
// of message tags.
type IrcMessage struct {
	Tags    map[string]TagValue
	Prefix  string
	Command string
	Params  []string
}

// ParseLine creates and returns an IrcMessage from the given IRC line.
//
// Quirks:
//
//   The RFCs say that last parameters with no characters MUST be a trailing.
//   IE, they need to be prefixed with ":". We disagree with that and handle
//   incoming last empty parameters whether they are trailing or ordinary
//   parameters. However, we do follow that rule when emitting lines.
func ParseLine(line string) (IrcMessage, error) {
	line = strings.Trim(line, "\r\n")
	var ircmsg IrcMessage

	if len(line) < 1 {
		return ircmsg, ErrorLineIsEmpty
	}

	// tags
	ircmsg.Tags = make(map[string]TagValue)
	if line[0] == '@' {
		splitLine := strings.SplitN(line, " ", 2)
		tags := splitLine[0][1:]
		line = strings.TrimLeft(splitLine[1], " ")

		for _, fulltag := range strings.Split(tags, ";") {
			var name string
			var val TagValue
			if strings.Contains(fulltag, "=") {
				val.HasValue = true
				splittag := strings.SplitN(fulltag, "=", 2)
				name = splittag[0]
				val.Value = UnescapeTagValue(splittag[1])
			} else {
				name = fulltag
				val.HasValue = false
			}

			ircmsg.Tags[name] = val
		}
	}

	if len(line) < 1 {
		return ircmsg, ErrorLineIsEmpty
	}

	// prefix
	if line[0] == ':' {
		splitLine := strings.SplitN(line, " ", 2)
		ircmsg.Prefix = splitLine[0][1:]
		line = strings.TrimLeft(splitLine[1], " ")
	}

	if len(line) < 1 {
		return ircmsg, ErrorLineIsEmpty
	}

	// command
	splitLine := strings.SplitN(line, " ", 2)
	ircmsg.Command = strings.ToUpper(splitLine[0])
	if len(splitLine) > 1 {
		line = strings.TrimLeft(splitLine[1], " ")

		// parameters
		for {
			// handle trailing
			if len(line) > 0 && line[0] == ':' {
				ircmsg.Params = append(ircmsg.Params, line[1:])
				break
			}

			// regular params
			splitLine := strings.SplitN(line, " ", 2)
			ircmsg.Params = append(ircmsg.Params, splitLine[0])

			if len(splitLine) > 1 {
				line = strings.TrimLeft(splitLine[1], " ")
			} else {
				break
			}
		}
	}

	return ircmsg, nil
}

// MakeMessage provides a simple way to create a new IrcMessage.
func MakeMessage(tags *map[string]TagValue, prefix string, command string, params ...string) IrcMessage {
	var ircmsg IrcMessage

	ircmsg.Tags = make(map[string]TagValue)
	if tags != nil {
		for tag, value := range *tags {
			ircmsg.Tags[tag] = value
		}
	}

	ircmsg.Prefix = prefix
	ircmsg.Command = command
	ircmsg.Params = params

	return ircmsg
}

// Line returns a sendable line created from an IrcMessage.
func (ircmsg *IrcMessage) Line() (string, error) {
	var line string

	if len(ircmsg.Command) < 1 {
		return "", errors.New("irc: IRC messages MUST have a command")
	}

	if len(ircmsg.Tags) > 0 {
		line += "@"

		for tag, val := range ircmsg.Tags {
			line += tag

			if val.HasValue {
				line += "="
				line += EscapeTagValue(val.Value)
			}

			line += ";"
		}
		// TODO: this is ugly, but it works for now
		line = strings.TrimSuffix(line, ";")

		line += " "
	}

	if len(ircmsg.Prefix) > 0 {
		line += ":"
		line += ircmsg.Prefix
		line += " "
	}

	line += ircmsg.Command

	if len(ircmsg.Params) > 0 {
		for i, param := range ircmsg.Params {
			line += " "
			if strings.Contains(param, " ") || strings.Contains(param, ":") || len(param) < 1 {
				if i != len(ircmsg.Params)-1 {
					return "", errors.New("irc: Cannot have a param with spaces / colon or an empty param before the last parameter")
				}
				line += ":"
			}
			line += param
		}
	}

	line += "\r\n"

	return line, nil
}
<|endoftext|>


<commit_before>package rin

import (
	"database/sql"
	"log"
	"sync"

	_ "github.com/lib/pq"
)

var (
	DBPool      = make(map[string]*sql.DB, 0)
	DBPoolMutex sync.Mutex
)

func Import(event Event) (int, error) {
	imported := 0
	for _, record := range event.Records {
		for _, target := range config.Targets {
			if target.MatchEventRecord(record) {
				err := ImportRedshift(target, record)
				if err != nil {
					return imported, err
				} else {
					imported++
				}
			}
		}
	}
	return imported, nil
}

func ConnectToRedshift(target Target) (*sql.DB, error) {
	r := target.Redshift
	dsn := r.DSN()

	DBPoolMutex.Lock()
	defer DBPoolMutex.Unlock()

	if db := DBPool[dsn]; db != nil {
		return db, nil
	}
	log.Println("Connect to Redshift", dsn)
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, err
	}
	DBPool[dsn] = db
	return db, nil
}

func ImportRedshift(target Target, record EventRecord) error {
	log.Printf("Import to target %s from record %s", target, record)
	db, err := ConnectToRedshift(target)
	if err != nil {
		return err
	}
	query, binds, err := target.BuildCopySQL(record.S3.Object.Key, config.Credentials)
	if err != nil {
		return err
	}
	if Debug {
		log.Printf("SQL: %s %#v", query, binds)
	}
	stmt, err := db.Prepare(query)
	if err != nil {
		return err
	}
	defer stmt.Close()
	rows, err := stmt.Query(binds...)
	if err != nil {
		return err
	}
	defer rows.Close()
	return nil
}
<commit_msg>copy in a transaction<commit_after>package rin

import (
	"database/sql"
	"log"
	"sync"

	_ "github.com/lib/pq"
)

var (
	DBPool      = make(map[string]*sql.DB, 0)
	DBPoolMutex sync.Mutex
)

func Import(event Event) (int, error) {
	imported := 0
	for _, record := range event.Records {
		for _, target := range config.Targets {
			if target.MatchEventRecord(record) {
				err := ImportRedshift(target, record)
				if err != nil {
					return imported, err
				} else {
					imported++
				}
			}
		}
	}
	return imported, nil
}

func ConnectToRedshift(target Target) (*sql.DB, error) {
	r := target.Redshift
	dsn := r.DSN()

	DBPoolMutex.Lock()
	defer DBPoolMutex.Unlock()

	if db := DBPool[dsn]; db != nil {
		return db, nil
	}
	log.Println("Connect to Redshift", dsn)
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, err
	}
	DBPool[dsn] = db
	return db, nil
}

func ImportRedshift(target Target, record EventRecord) error {
	log.Printf("Import to target %s from record %s", target, record)
	db, err := ConnectToRedshift(target)
	if err != nil {
		return err
	}
	txn, err := db.Begin()
	if err != nil {
		return err
	}
	defer txn.Rollback()

	query, binds, err := target.BuildCopySQL(record.S3.Object.Key, config.Credentials)
	if err != nil {
		return err
	}
	if Debug {
		log.Printf("SQL: %s %#v", query, binds)
	}
	stmt, err := db.Prepare(query)
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(binds...)
	if err != nil {
		return err
	}

	err = txn.Commit()
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package irma

import (
	"crypto/sha256"
	"encoding/asn1"
	"log"
	"math/big"

	"github.com/bwesterb/go-atum"
	"github.com/mhe/gabi"
)

// IrmaSignedMessage is a message signed with an attribute-based signature
// The 'realnonce' will be calculated as: SigRequest.GetNonce() = ASN1(sha256(message), sha256(nonce))
type IrmaSignedMessage struct {
	Signature gabi.ProofList  `json:"signature"`
	Nonce     *big.Int        `json:"nonce"`
	Context   *big.Int        `json:"context"`
	Message   string          `json:"message"`
	Timestamp *atum.Timestamp `json:"timestamp"`
}

func (im *IrmaSignedMessage) GetNonce() *big.Int {
	return ASN1ConvertSignatureNonce(im.Message, im.Nonce, im.Timestamp)
}

func (im *IrmaSignedMessage) MatchesNonceAndContext(request *SignatureRequest) bool {
	return im.Nonce.Cmp(request.Nonce) == 0 &&
		im.Context.Cmp(request.Context) == 0 &&
		im.GetNonce().Cmp(request.GetNonce()) == 0
}

// Convert a Nonce to a nonce of a signature session
// (with the message already hashed into it).
func ASN1ConvertSignatureNonce(message string, nonce *big.Int, timestamp *atum.Timestamp) *big.Int {
	msgHash := sha256.Sum256([]byte(message))
	tohash := []interface{}{nonce, new(big.Int).SetBytes(msgHash[:])}
	if timestamp != nil {
		tohash = append(tohash, timestamp.Sig.Data)
	}
	// TODO remove the 2, or keep backwards compatible?
	tohash = append([]interface{}{big.NewInt(int64(len(tohash)))}, tohash...)
	asn1bytes, err := asn1.Marshal(tohash)
	if err != nil {
		log.Print(err) // TODO
	}
	asn1hash := sha256.Sum256(asn1bytes)
	return new(big.Int).SetBytes(asn1hash[:])
}
<commit_msg>Remove superfluous parameter in signature hashing<commit_after>package irma

import (
	"crypto/sha256"
	"encoding/asn1"
	"log"
	"math/big"

	"github.com/bwesterb/go-atum"
	"github.com/mhe/gabi"
)

// IrmaSignedMessage is a message signed with an attribute-based signature
// The 'realnonce' will be calculated as: SigRequest.GetNonce() = ASN1(sha256(message), sha256(nonce))
type IrmaSignedMessage struct {
	Signature gabi.ProofList  `json:"signature"`
	Nonce     *big.Int        `json:"nonce"`
	Context   *big.Int        `json:"context"`
	Message   string          `json:"message"`
	Timestamp *atum.Timestamp `json:"timestamp"`
}

func (im *IrmaSignedMessage) GetNonce() *big.Int {
	return ASN1ConvertSignatureNonce(im.Message, im.Nonce, im.Timestamp)
}

func (im *IrmaSignedMessage) MatchesNonceAndContext(request *SignatureRequest) bool {
	return im.Nonce.Cmp(request.Nonce) == 0 &&
		im.Context.Cmp(request.Context) == 0 &&
		im.GetNonce().Cmp(request.GetNonce()) == 0
}

// ASN1ConvertSignatureNonce computes the nonce that is used in the creation of the attribute-based signature:
//    nonce = SHA256(serverNonce, SHA256(message), timestampSignature)
// where serverNonce is the nonce sent by the signature requestor.
func ASN1ConvertSignatureNonce(message string, nonce *big.Int, timestamp *atum.Timestamp) *big.Int {
	msgHash := sha256.Sum256([]byte(message))
	tohash := []interface{}{nonce, new(big.Int).SetBytes(msgHash[:])}
	if timestamp != nil {
		tohash = append(tohash, timestamp.Sig.Data)
	}
	asn1bytes, err := asn1.Marshal(tohash)
	if err != nil {
		log.Print(err) // TODO
	}
	asn1hash := sha256.Sum256(asn1bytes)
	return new(big.Int).SetBytes(asn1hash[:])
}
<|endoftext|>


<commit_before>// Copyright 2014 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

/*
Package tags 0.1.0

Definition:

"Tags" - a strings which used for tag any object
"non-strict Tags" - a strings which match to strings in data ("new" -> "new")
"strict Tags" - all strict tags have prefix "+" for strict match ("+new")
    and "-" for strict mismatch ("-old")

All strict Tags applied with logical operator "AND" between each other
All non-strict Tags applied with logical operator "OR" between all tags

Example:

	package main

	import (
		"fmt"

		"github.com/takama/tags"
	)

	// Product is struct with tags
	type Product struct {
		Name        string
		Description string
		Tags        tags.Tags
	}

	func main() {
		product := Product{
			Name:        "the tee",
			Description: "the boutle of ice black tee with sugar",
			Tags:        tags.Tags{"ice", "black", "sugar"},
		}

		fmt.Println("Product:", product.Description)

		fmt.Println("Is this tee black or green?")
		query := tags.Tags{"black", "green"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is black.")
		} else {
			fmt.Println("No, the tee has not black or green options.")
		}

		fmt.Println("Is this tee green with sugar?")
		query = tags.Tags{"+green", "+sugar"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is green with sugar.")
		} else {
			fmt.Println("No, the tee with sugar, but is not green.")
		}

		fmt.Println("Is this tee hot?")
		query = tags.Tags{"-ice"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is hot.")
		} else {
			fmt.Println("No, it is ice tee.")
		}
	}

Go Tags
*/
package tags

import (
	"strings"
)

// Tags a slice of strings which used for tag any object
type Tags []string

// IsTagged method checks if elements in a data compliant for tags in query
func (t Tags) IsTagged(query Tags) bool {
	if len(query) == 0 {
		return true
	}
	if len(t) == 0 {
		return false
	}
	for _, qTag := range query {
		for _, tag := range t {
			if tag == qTag {
				return true
			}
		}
	}

	var strict uint8
	for _, qTag := range query {
		if strings.HasPrefix(qTag, "+") || strings.HasPrefix(qTag, "-") {
			strict++
		}
	}
	if strict == 0 {
		return false
	}
	for _, qTag := range query {
		if strings.HasPrefix(qTag, "+") {
			for _, tag := range t {
				if tag == strings.TrimPrefix(qTag, "+") {
					strict--
					break
				}
			}
		}
		if strings.HasPrefix(qTag, "-") {
			fit := true
			for _, tag := range t {
				if tag == strings.TrimPrefix(qTag, "-") {
					fit = false
					break
				}
			}
			if fit {
				strict--
			}
		}
	}

	return strict == 0
}
<commit_msg>Bumped version number to 0.1.1<commit_after>// Copyright 2014 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

/*
Package tags 0.1.1

Definition:

"Tags" - a strings which used for tag any object
"non-strict Tags" - a strings which match to strings in data ("new" -> "new")
"strict Tags" - all strict tags have prefix "+" for strict match ("+new")
    and "-" for strict mismatch ("-old")

All strict Tags applied with logical operator "AND" between each other
All non-strict Tags applied with logical operator "OR" between all tags

Example:

	package main

	import (
		"fmt"

		"github.com/takama/tags"
	)

	// Product is struct with tags
	type Product struct {
		Name        string
		Description string
		Tags        tags.Tags
	}

	func main() {
		product := Product{
			Name:        "the tee",
			Description: "the boutle of ice black tee with sugar",
			Tags:        tags.Tags{"ice", "black", "sugar"},
		}

		fmt.Println("Product:", product.Description)

		fmt.Println("Is this tee black or green?")
		query := tags.Tags{"black", "green"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is black.")
		} else {
			fmt.Println("No, the tee has not black or green options.")
		}

		fmt.Println("Is this tee green with sugar?")
		query = tags.Tags{"+green", "+sugar"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is green with sugar.")
		} else {
			fmt.Println("No, the tee with sugar, but is not green.")
		}

		fmt.Println("Is this tee hot?")
		query = tags.Tags{"-ice"}
		if product.Tags.IsTagged(query) {
			fmt.Println("Yes, the tee is hot.")
		} else {
			fmt.Println("No, it is ice tee.")
		}
	}

Go Tags
*/
package tags

import (
	"strings"
)

// Tags a slice of strings which used for tag any object
type Tags []string

// IsTagged method checks if elements in a data compliant for tags in query
func (t Tags) IsTagged(query Tags) bool {
	if len(query) == 0 {
		return true
	}
	if len(t) == 0 {
		return false
	}
	for _, qTag := range query {
		for _, tag := range t {
			if tag == qTag {
				return true
			}
		}
	}

	var strict uint8
	for _, qTag := range query {
		if strings.HasPrefix(qTag, "+") || strings.HasPrefix(qTag, "-") {
			strict++
		}
	}
	if strict == 0 {
		return false
	}
	for _, qTag := range query {
		if strings.HasPrefix(qTag, "+") {
			for _, tag := range t {
				if tag == strings.TrimPrefix(qTag, "+") {
					strict--
					break
				}
			}
		}
		if strings.HasPrefix(qTag, "-") {
			fit := true
			for _, tag := range t {
				if tag == strings.TrimPrefix(qTag, "-") {
					fit = false
					break
				}
			}
			if fit {
				strict--
			}
		}
	}

	return strict == 0
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package aws

import (
	"fmt"

	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/util/wait"
)

// TagNameKubernetesClusterPrefix is the tag name we use to differentiate multiple
// logically independent clusters running in the same AZ.
// The tag key = TagNameKubernetesClusterPrefix + clusterID
// The tag value is an ownership value
const TagNameKubernetesClusterPrefix = "kubernetes.io/cluster/"

// TagNameKubernetesClusterLegacy is the legacy tag name we use to differentiate multiple
// logically independent clusters running in the same AZ.  The problem with it was that it
// did not allow shared resources.
const TagNameKubernetesClusterLegacy = "KubernetesCluster"

type ResourceLifecycle string

const (
	// ResourceLifecycleOwned is the value we use when tagging resources to indicate
	// that the resource is considered owned and managed by the cluster,
	// and in particular that the lifecycle is tied to the lifecycle of the cluster.
	ResourceLifecycleOwned = "owned"
	// ResourceLifecycleShared is the value we use when tagging resources to indicate
	// that the resource is shared between multiple clusters, and should not be destroyed
	// if the cluster is destroyed.
	ResourceLifecycleShared = "shared"
)

type awsTagging struct {
	// ClusterID is our cluster identifier: we tag AWS resources with this value,
	// and thus we can run two independent clusters in the same VPC or subnets.
	// This gives us similar functionality to GCE projects.
	ClusterID string

	// usesLegacyTags is true if we are using the legacy TagNameKubernetesClusterLegacy tags
	usesLegacyTags bool
}

func (t *awsTagging) init(legacyClusterID string, clusterID string) error {
	if legacyClusterID != "" {
		if clusterID != "" && legacyClusterID != clusterID {
			return fmt.Errorf("ClusterID tags did not match: %q vs %q", clusterID, legacyClusterID)
		}
		t.usesLegacyTags = true
		clusterID = legacyClusterID
	}

	t.ClusterID = clusterID

	if clusterID != "" {
		glog.Infof("AWS cloud filtering on ClusterID: %v", clusterID)
	} else {
		glog.Infof("AWS cloud - no clusterID filtering")
	}

	return nil
}

// Extracts a clusterID from the given tags, if one is present
// If no clusterID is found, returns "", nil
// If multiple (different) clusterIDs are found, returns an error
func (t *awsTagging) initFromTags(tags []*ec2.Tag) error {
	legacyClusterID, newClusterID, err := findClusterIDs(tags)
	if err != nil {
		return err
	}

	if legacyClusterID == "" && newClusterID == "" {
		glog.Errorf("Tag %q nor %q not found; Kubernetes may behave unexpectedly.", TagNameKubernetesClusterLegacy, TagNameKubernetesClusterPrefix+"...")
	}

	return t.init(legacyClusterID, newClusterID)
}

// Extracts the legacy & new cluster ids from the given tags, if they are present
// If duplicate tags are found, returns an error
func findClusterIDs(tags []*ec2.Tag) (string, string, error) {
	legacyClusterID := ""
	newClusterID := ""

	for _, tag := range tags {
		tagKey := aws.StringValue(tag.Key)
		if strings.HasPrefix(tagKey, TagNameKubernetesClusterPrefix) {
			id := strings.TrimPrefix(tagKey, TagNameKubernetesClusterPrefix)
			if newClusterID != "" {
				return "", "", fmt.Errorf("Found multiple cluster tags with prefix %s (%q and %q)", TagNameKubernetesClusterPrefix, newClusterID, id)
			}
			newClusterID = id
		}

		if tagKey == TagNameKubernetesClusterLegacy {
			id := aws.StringValue(tag.Value)
			if legacyClusterID != "" {
				return "", "", fmt.Errorf("Found multiple %s tags (%q and %q)", TagNameKubernetesClusterLegacy, legacyClusterID, id)
			}
			legacyClusterID = id
		}
	}

	return legacyClusterID, newClusterID, nil
}

func (t *awsTagging) clusterTagKey() string {
	return TagNameKubernetesClusterPrefix + t.ClusterID
}

func (t *awsTagging) hasClusterTag(tags []*ec2.Tag) bool {
	// if the clusterID is not configured -- we consider all instances.
	if len(t.ClusterID) == 0 {
		return true
	}
	clusterTagKey := t.clusterTagKey()
	for _, tag := range tags {
		tagKey := aws.StringValue(tag.Key)
		// For 1.6, we continue to recognize the legacy tags, for the 1.5 -> 1.6 upgrade
		if tagKey == TagNameKubernetesClusterLegacy {
			return aws.StringValue(tag.Value) == t.ClusterID
		}

		if tagKey == clusterTagKey {
			return true
		}
	}
	return false
}

// Ensure that a resource has the correct tags
// If it has no tags, we assume that this was a problem caused by an error in between creation and tagging,
// and we add the tags.  If it has a different cluster's tags, that is an error.
func (c *awsTagging) readRepairClusterTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string, observedTags []*ec2.Tag) error {
	actualTagMap := make(map[string]string)
	for _, tag := range observedTags {
		actualTagMap[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value)
	}

	expectedTags := c.buildTags(lifecycle, additionalTags)

	addTags := make(map[string]string)
	for k, expected := range expectedTags {
		actual := actualTagMap[k]
		if actual == expected {
			continue
		}
		if actual == "" {
			glog.Warningf("Resource %q was missing expected cluster tag %q.  Will add (with value %q)", resourceID, k, expected)
			addTags[k] = expected
		} else {
			return fmt.Errorf("resource %q has tag belonging to another cluster: %q=%q (expected %q)", resourceID, k, actual, expected)
		}
	}

	if len(addTags) == 0 {
		return nil
	}

	if err := c.createTags(client, resourceID, lifecycle, addTags); err != nil {
		return fmt.Errorf("error adding missing tags to resource %q: %q", resourceID, err)
	}

	return nil
}

// createTags calls EC2 CreateTags, but adds retry-on-failure logic
// We retry mainly because if we create an object, we cannot tag it until it is "fully created" (eventual consistency)
// The error code varies though (depending on what we are tagging), so we simply retry on all errors
func (t *awsTagging) createTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string) error {
	tags := t.buildTags(lifecycle, additionalTags)

	if tags == nil || len(tags) == 0 {
		return nil
	}

	var awsTags []*ec2.Tag
	for k, v := range tags {
		tag := &ec2.Tag{
			Key:   aws.String(k),
			Value: aws.String(v),
		}
		awsTags = append(awsTags, tag)
	}

	backoff := wait.Backoff{
		Duration: createTagInitialDelay,
		Factor:   createTagFactor,
		Steps:    createTagSteps,
	}
	request := &ec2.CreateTagsInput{}
	request.Resources = []*string{&resourceID}
	request.Tags = awsTags

	var lastErr error
	err := wait.ExponentialBackoff(backoff, func() (bool, error) {
		_, err := client.CreateTags(request)
		if err == nil {
			return true, nil
		}

		// We could check that the error is retryable, but the error code changes based on what we are tagging
		// SecurityGroup: InvalidGroup.NotFound
		glog.V(2).Infof("Failed to create tags; will retry.  Error was %q", err)
		lastErr = err
		return false, nil
	})
	if err == wait.ErrWaitTimeout {
		// return real CreateTags error instead of timeout
		err = lastErr
	}
	return err
}

// Add additional filters, to match on our tags
// This lets us run multiple k8s clusters in a single EC2 AZ
func (t *awsTagging) addFilters(filters []*ec2.Filter) []*ec2.Filter {
	// if there are no clusterID configured - no filtering by special tag names
	// should be applied to revert to legacy behaviour.
	if len(t.ClusterID) == 0 {
		if len(filters) == 0 {
			// We can't pass a zero-length Filters to AWS (it's an error)
			// So if we end up with no filters; just return nil
			return nil
		}
		return filters
	}
	// For 1.6, we always recognize the legacy tag, for the 1.5 -> 1.6 upgrade
	// There are no "or" filters by key, so we look for both the legacy and new key, and then we have to post-filter
	f := newEc2Filter("tag-key", TagNameKubernetesClusterLegacy, t.clusterTagKey())

	// We can't pass a zero-length Filters to AWS (it's an error)
	// So if we end up with no filters; we need to return nil
	filters = append(filters, f)
	return filters
}

func (t *awsTagging) buildTags(lifecycle ResourceLifecycle, additionalTags map[string]string) map[string]string {
	tags := make(map[string]string)
	for k, v := range additionalTags {
		tags[k] = v
	}

	// no clusterID is a sign of misconfigured cluster, but we can't be tagging the resources with empty
	// strings
	if len(t.ClusterID) == 0 {
		return tags
	}

	// We only create legacy tags if we are using legacy tags, i.e. if we have seen a legacy tag on our instance
	if t.usesLegacyTags {
		tags[TagNameKubernetesClusterLegacy] = t.ClusterID
	}
	tags[t.clusterTagKey()] = string(lifecycle)

	return tags
}
<commit_msg>Warn if aws has no cluster id provided<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package aws

import (
	"fmt"

	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/util/wait"
)

// TagNameKubernetesClusterPrefix is the tag name we use to differentiate multiple
// logically independent clusters running in the same AZ.
// The tag key = TagNameKubernetesClusterPrefix + clusterID
// The tag value is an ownership value
const TagNameKubernetesClusterPrefix = "kubernetes.io/cluster/"

// TagNameKubernetesClusterLegacy is the legacy tag name we use to differentiate multiple
// logically independent clusters running in the same AZ.  The problem with it was that it
// did not allow shared resources.
const TagNameKubernetesClusterLegacy = "KubernetesCluster"

type ResourceLifecycle string

const (
	// ResourceLifecycleOwned is the value we use when tagging resources to indicate
	// that the resource is considered owned and managed by the cluster,
	// and in particular that the lifecycle is tied to the lifecycle of the cluster.
	ResourceLifecycleOwned = "owned"
	// ResourceLifecycleShared is the value we use when tagging resources to indicate
	// that the resource is shared between multiple clusters, and should not be destroyed
	// if the cluster is destroyed.
	ResourceLifecycleShared = "shared"
)

type awsTagging struct {
	// ClusterID is our cluster identifier: we tag AWS resources with this value,
	// and thus we can run two independent clusters in the same VPC or subnets.
	// This gives us similar functionality to GCE projects.
	ClusterID string

	// usesLegacyTags is true if we are using the legacy TagNameKubernetesClusterLegacy tags
	usesLegacyTags bool
}

func (t *awsTagging) init(legacyClusterID string, clusterID string) error {
	if legacyClusterID != "" {
		if clusterID != "" && legacyClusterID != clusterID {
			return fmt.Errorf("ClusterID tags did not match: %q vs %q", clusterID, legacyClusterID)
		}
		t.usesLegacyTags = true
		clusterID = legacyClusterID
	}

	t.ClusterID = clusterID

	if clusterID != "" {
		glog.Infof("AWS cloud filtering on ClusterID: %v", clusterID)
	} else {
		glog.Warning("AWS cloud - no clusterID filtering applied for shared resources; do not run multiple clusters in this AZ.")
	}

	return nil
}

// Extracts a clusterID from the given tags, if one is present
// If no clusterID is found, returns "", nil
// If multiple (different) clusterIDs are found, returns an error
func (t *awsTagging) initFromTags(tags []*ec2.Tag) error {
	legacyClusterID, newClusterID, err := findClusterIDs(tags)
	if err != nil {
		return err
	}

	if legacyClusterID == "" && newClusterID == "" {
		glog.Errorf("Tag %q nor %q not found; Kubernetes may behave unexpectedly.", TagNameKubernetesClusterLegacy, TagNameKubernetesClusterPrefix+"...")
	}

	return t.init(legacyClusterID, newClusterID)
}

// Extracts the legacy & new cluster ids from the given tags, if they are present
// If duplicate tags are found, returns an error
func findClusterIDs(tags []*ec2.Tag) (string, string, error) {
	legacyClusterID := ""
	newClusterID := ""

	for _, tag := range tags {
		tagKey := aws.StringValue(tag.Key)
		if strings.HasPrefix(tagKey, TagNameKubernetesClusterPrefix) {
			id := strings.TrimPrefix(tagKey, TagNameKubernetesClusterPrefix)
			if newClusterID != "" {
				return "", "", fmt.Errorf("Found multiple cluster tags with prefix %s (%q and %q)", TagNameKubernetesClusterPrefix, newClusterID, id)
			}
			newClusterID = id
		}

		if tagKey == TagNameKubernetesClusterLegacy {
			id := aws.StringValue(tag.Value)
			if legacyClusterID != "" {
				return "", "", fmt.Errorf("Found multiple %s tags (%q and %q)", TagNameKubernetesClusterLegacy, legacyClusterID, id)
			}
			legacyClusterID = id
		}
	}

	return legacyClusterID, newClusterID, nil
}

func (t *awsTagging) clusterTagKey() string {
	return TagNameKubernetesClusterPrefix + t.ClusterID
}

func (t *awsTagging) hasClusterTag(tags []*ec2.Tag) bool {
	// if the clusterID is not configured -- we consider all instances.
	if len(t.ClusterID) == 0 {
		return true
	}
	clusterTagKey := t.clusterTagKey()
	for _, tag := range tags {
		tagKey := aws.StringValue(tag.Key)
		// For 1.6, we continue to recognize the legacy tags, for the 1.5 -> 1.6 upgrade
		if tagKey == TagNameKubernetesClusterLegacy {
			return aws.StringValue(tag.Value) == t.ClusterID
		}

		if tagKey == clusterTagKey {
			return true
		}
	}
	return false
}

// Ensure that a resource has the correct tags
// If it has no tags, we assume that this was a problem caused by an error in between creation and tagging,
// and we add the tags.  If it has a different cluster's tags, that is an error.
func (c *awsTagging) readRepairClusterTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string, observedTags []*ec2.Tag) error {
	actualTagMap := make(map[string]string)
	for _, tag := range observedTags {
		actualTagMap[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value)
	}

	expectedTags := c.buildTags(lifecycle, additionalTags)

	addTags := make(map[string]string)
	for k, expected := range expectedTags {
		actual := actualTagMap[k]
		if actual == expected {
			continue
		}
		if actual == "" {
			glog.Warningf("Resource %q was missing expected cluster tag %q.  Will add (with value %q)", resourceID, k, expected)
			addTags[k] = expected
		} else {
			return fmt.Errorf("resource %q has tag belonging to another cluster: %q=%q (expected %q)", resourceID, k, actual, expected)
		}
	}

	if len(addTags) == 0 {
		return nil
	}

	if err := c.createTags(client, resourceID, lifecycle, addTags); err != nil {
		return fmt.Errorf("error adding missing tags to resource %q: %q", resourceID, err)
	}

	return nil
}

// createTags calls EC2 CreateTags, but adds retry-on-failure logic
// We retry mainly because if we create an object, we cannot tag it until it is "fully created" (eventual consistency)
// The error code varies though (depending on what we are tagging), so we simply retry on all errors
func (t *awsTagging) createTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string) error {
	tags := t.buildTags(lifecycle, additionalTags)

	if tags == nil || len(tags) == 0 {
		return nil
	}

	var awsTags []*ec2.Tag
	for k, v := range tags {
		tag := &ec2.Tag{
			Key:   aws.String(k),
			Value: aws.String(v),
		}
		awsTags = append(awsTags, tag)
	}

	backoff := wait.Backoff{
		Duration: createTagInitialDelay,
		Factor:   createTagFactor,
		Steps:    createTagSteps,
	}
	request := &ec2.CreateTagsInput{}
	request.Resources = []*string{&resourceID}
	request.Tags = awsTags

	var lastErr error
	err := wait.ExponentialBackoff(backoff, func() (bool, error) {
		_, err := client.CreateTags(request)
		if err == nil {
			return true, nil
		}

		// We could check that the error is retryable, but the error code changes based on what we are tagging
		// SecurityGroup: InvalidGroup.NotFound
		glog.V(2).Infof("Failed to create tags; will retry.  Error was %q", err)
		lastErr = err
		return false, nil
	})
	if err == wait.ErrWaitTimeout {
		// return real CreateTags error instead of timeout
		err = lastErr
	}
	return err
}

// Add additional filters, to match on our tags
// This lets us run multiple k8s clusters in a single EC2 AZ
func (t *awsTagging) addFilters(filters []*ec2.Filter) []*ec2.Filter {
	// if there are no clusterID configured - no filtering by special tag names
	// should be applied to revert to legacy behaviour.
	if len(t.ClusterID) == 0 {
		if len(filters) == 0 {
			// We can't pass a zero-length Filters to AWS (it's an error)
			// So if we end up with no filters; just return nil
			return nil
		}
		return filters
	}
	// For 1.6, we always recognize the legacy tag, for the 1.5 -> 1.6 upgrade
	// There are no "or" filters by key, so we look for both the legacy and new key, and then we have to post-filter
	f := newEc2Filter("tag-key", TagNameKubernetesClusterLegacy, t.clusterTagKey())

	// We can't pass a zero-length Filters to AWS (it's an error)
	// So if we end up with no filters; we need to return nil
	filters = append(filters, f)
	return filters
}

func (t *awsTagging) buildTags(lifecycle ResourceLifecycle, additionalTags map[string]string) map[string]string {
	tags := make(map[string]string)
	for k, v := range additionalTags {
		tags[k] = v
	}

	// no clusterID is a sign of misconfigured cluster, but we can't be tagging the resources with empty
	// strings
	if len(t.ClusterID) == 0 {
		return tags
	}

	// We only create legacy tags if we are using legacy tags, i.e. if we have seen a legacy tag on our instance
	if t.usesLegacyTags {
		tags[TagNameKubernetesClusterLegacy] = t.ClusterID
	}
	tags[t.clusterTagKey()] = string(lifecycle)

	return tags
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package todotxt

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"time"
)

var (
	// DateLayout is used for formatting time.Time into todo.txt date format and vice-versa.
	DateLayout = "2006-01-02"

	priorityRx = regexp.MustCompile(`^(x|x \d{4}-\d{2}-\d{2}|)\s*\(([A-Z])\)\s+`) // Match priority: '(A) ...' or 'x (A) ...' or 'x 2012-12-12 (A) ...'
	// Match created date: '(A) 2012-12-12 ...' or 'x 2012-12-12 (A) 2012-12-12 ...' or 'x (A) 2012-12-12 ...'or 'x 2012-12-12 2012-12-12 ...' or '2012-12-12 ...'
	createdDateRx   = regexp.MustCompile(`^(\([A-Z]\)|x \d{4}-\d{2}-\d{2} \([A-Z]\)|x \([A-Z]\)|x \d{4}-\d{2}-\d{2}|)\s*(\d{4}-\d{2}-\d{2})\s+`)
	completedRx     = regexp.MustCompile(`^x\s+`)                       // Match completed: 'x ...'
	completedDateRx = regexp.MustCompile(`^x\s*(\d{4}-\d{2}-\d{2})\s+`) // Match completed date: 'x 2012-12-12 ...'
	addonTagRx      = regexp.MustCompile(`(^|\s+)([\w-]+):(\S+)`)       // Match additional tags date: '... due:2012-12-12 ...'
	contextRx       = regexp.MustCompile(`(^|\s+)@(\S+)`)               // Match contexts: '@Context ...' or '... @Context ...'
	projectRx       = regexp.MustCompile(`(^|\s+)\+(\S+)`)              // Match projects: '+Project...' or '... +Project ...')
)

// Task represents a todo.txt task entry.
type Task struct {
	Id             int    // Internal task id.
	Original       string // Original raw task text.
	Todo           string // Todo part of task text.
	Priority       string
	Projects       []string
	Contexts       []string
	AdditionalTags map[string]string // Addon tags will be available here.
	CreatedDate    time.Time
	DueDate        time.Time
	CompletedDate  time.Time
	Completed      bool
}

// String returns a complete task string in todo.txt format.
//
// Contexts,  Projects and additional tags are alphabetically sorted,
// and appendend at the end in the following order:
// Contexts, Projects, Tags
//
// For example:
//  "(A) 2013-07-23 Call Dad @Home @Phone +Family due:2013-07-31 customTag1:Important!"
func (task Task) String() string {
	var text string

	if task.Completed {
		text += "x "
		if task.HasCompletedDate() {
			text += fmt.Sprintf("%s ", task.CompletedDate.Format(DateLayout))
		}
	}

	if task.HasPriority() {
		text += fmt.Sprintf("(%s) ", task.Priority)
	}

	if task.HasCreatedDate() {
		text += fmt.Sprintf("%s ", task.CreatedDate.Format(DateLayout))
	}

	text += task.Todo

	if len(task.Contexts) > 0 {
		sort.Strings(task.Contexts)
		for _, context := range task.Contexts {
			text += fmt.Sprintf(" @%s", context)
		}
	}

	if len(task.Projects) > 0 {
		sort.Strings(task.Projects)
		for _, project := range task.Projects {
			text += fmt.Sprintf(" +%s", project)
		}
	}

	if len(task.AdditionalTags) > 0 {
		// Sort map alphabetically by keys
		keys := make([]string, 0, len(task.AdditionalTags))
		for key := range task.AdditionalTags {
			keys = append(keys, key)
		}
		sort.Strings(keys)
		for _, key := range keys {
			text += fmt.Sprintf(" %s:%s", key, task.AdditionalTags[key])
		}
	}

	if task.HasDueDate() {
		text += fmt.Sprintf(" due:%s", task.DueDate.Format(DateLayout))
	}

	return text
}

// NewTask creates a new empty Task with default values. (CreatedDate is set to Now())
func NewTask() Task {
	task := Task{}
	task.CreatedDate = time.Now()
	return task
}

// ParseTask parses the input text string into a Task struct.
func ParseTask(text string) (*Task, error) {
	var err error

	task := Task{}
	task.Original = strings.Trim(text, "\t\n\r ")
	task.Todo = task.Original

	// Check for completed
	if completedRx.MatchString(task.Original) {
		task.Completed = true
		// Check for completed date
		if completedDateRx.MatchString(task.Original) {
			if date, err := time.Parse(DateLayout, completedDateRx.FindStringSubmatch(task.Original)[1]); err == nil {
				task.CompletedDate = date
			} else {
				return nil, err
			}
		}

		// Remove from Todo text
		task.Todo = completedDateRx.ReplaceAllString(task.Todo, "") // Strip CompletedDate first, otherwise it wouldn't match anymore (^x date...)
		task.Todo = completedRx.ReplaceAllString(task.Todo, "")     // Strip 'x '
	}

	// Check for priority
	if priorityRx.MatchString(task.Original) {
		task.Priority = priorityRx.FindStringSubmatch(task.Original)[2]
		task.Todo = priorityRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for created date
	if createdDateRx.MatchString(task.Original) {
		if date, err := time.Parse(DateLayout, createdDateRx.FindStringSubmatch(task.Original)[2]); err == nil {
			task.CreatedDate = date
			task.Todo = createdDateRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
		} else {
			return nil, err
		}
	}

	// function for collecting projects/contexts as slices from text
	getSlice := func(rx *regexp.Regexp) []string {
		matches := rx.FindAllStringSubmatch(task.Original, -1)
		slice := make([]string, 0, len(matches))
		seen := make(map[string]bool, len(matches))
		for _, match := range matches {
			word := strings.Trim(match[2], "\t\n\r ")
			if _, found := seen[word]; !found {
				slice = append(slice, word)
				seen[word] = true
			}
		}
		sort.Strings(slice)
		return slice
	}

	// Check for contexts
	if contextRx.MatchString(task.Original) {
		task.Contexts = getSlice(contextRx)
		task.Todo = contextRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for projects
	if projectRx.MatchString(task.Original) {
		task.Projects = getSlice(projectRx)
		task.Todo = projectRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for additional tags
	if addonTagRx.MatchString(task.Original) {
		matches := addonTagRx.FindAllStringSubmatch(task.Original, -1)
		tags := make(map[string]string, len(matches))
		for _, match := range matches {
			key, value := match[2], match[3]
			if key == "due" { // due date is a known addon tag, it has its own struct field
				if date, err := time.Parse(DateLayout, value); err == nil {
					task.DueDate = date
				} else {
					return nil, err
				}
			} else if key != "" && value != "" {
				tags[key] = value
			}
		}
		task.AdditionalTags = tags
		task.Todo = addonTagRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Trim any remaining whitespaces from Todo text
	task.Todo = strings.Trim(task.Todo, "\t\n\r\f ")

	return &task, err
}

// Task returns a complete task string in todo.txt format.
// See *Task.String() for further information.
func (task *Task) Task() string {
	return task.String()
}

// HasPriority returns true if the task has a priority.
func (task *Task) HasPriority() bool {
	return task.Priority != ""
}

// HasCreatedDate returns true if the task has a created date.
func (task *Task) HasCreatedDate() bool {
	return !task.CreatedDate.IsZero()
}

// HasDueDate returns true if the task has a due date.
func (task *Task) HasDueDate() bool {
	return !task.DueDate.IsZero()
}

// HasCompletedDate returns true if the task has a completed date.
func (task *Task) HasCompletedDate() bool {
	return !task.CompletedDate.IsZero() && task.Completed
}

// Complete sets Task.Completed to 'true' if the task was not already completed.
// Also sets Task.CompletedDate to time.Now()
func (task *Task) Complete() {
	if !task.Completed {
		task.Completed = true
		task.CompletedDate = time.Now()
	}
}

// Reopen sets Task.Completed to 'false' if the task was completed.
// Also resets Task.CompletedDate.
func (task *Task) Reopen() {
	if task.Completed {
		task.Completed = false
		task.CompletedDate = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) // time.IsZero() value
	}
}

// IsOverdue returns true if due date is in the past.
//
// This function does not take the Completed flag into consideration.
// You should check Task.Completed first if needed.
func (task *Task) IsOverdue() bool {
	if task.HasDueDate() {
		return task.DueDate.Before(time.Now())
	}
	return false
}

// Due returns the duration passed since due date, or until due date from now.
// Check with IsOverdue() if the task is overdue or not.
//
// Just as with IsOverdue(), this function does also not take the Completed flag into consideration.
// You should check Task.Completed first if needed.
func (task *Task) Due() time.Duration {
	if task.IsOverdue() {
		return time.Now().Sub(task.DueDate)
	}
	return task.DueDate.Sub(time.Now())
}
<commit_msg>Fix addon regex so it doesnt strip urls<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package todotxt

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"time"
)

var (
	// DateLayout is used for formatting time.Time into todo.txt date format and vice-versa.
	DateLayout = "2006-01-02"

	priorityRx = regexp.MustCompile(`^(x|x \d{4}-\d{2}-\d{2}|)\s*\(([A-Z])\)\s+`) // Match priority: '(A) ...' or 'x (A) ...' or 'x 2012-12-12 (A) ...'
	// Match created date: '(A) 2012-12-12 ...' or 'x 2012-12-12 (A) 2012-12-12 ...' or 'x (A) 2012-12-12 ...'or 'x 2012-12-12 2012-12-12 ...' or '2012-12-12 ...'
	createdDateRx   = regexp.MustCompile(`^(\([A-Z]\)|x \d{4}-\d{2}-\d{2} \([A-Z]\)|x \([A-Z]\)|x \d{4}-\d{2}-\d{2}|)\s*(\d{4}-\d{2}-\d{2})\s+`)
	completedRx     = regexp.MustCompile(`^x\s+`)                       // Match completed: 'x ...'
	completedDateRx = regexp.MustCompile(`^x\s*(\d{4}-\d{2}-\d{2})\s+`) // Match completed date: 'x 2012-12-12 ...'
	addonTagRx      = regexp.MustCompile(`(^|\s+)([\w-]+):([^/]\S*)`)   // Match additional tags date: '... due:2012-12-12 ...'
	contextRx       = regexp.MustCompile(`(^|\s+)@(\S+)`)               // Match contexts: '@Context ...' or '... @Context ...'
	projectRx       = regexp.MustCompile(`(^|\s+)\+(\S+)`)              // Match projects: '+Project...' or '... +Project ...')
)

// Task represents a todo.txt task entry.
type Task struct {
	Id             int    // Internal task id.
	Original       string // Original raw task text.
	Todo           string // Todo part of task text.
	Priority       string
	Projects       []string
	Contexts       []string
	AdditionalTags map[string]string // Addon tags will be available here.
	CreatedDate    time.Time
	DueDate        time.Time
	CompletedDate  time.Time
	Completed      bool
}

// String returns a complete task string in todo.txt format.
//
// Contexts,  Projects and additional tags are alphabetically sorted,
// and appendend at the end in the following order:
// Contexts, Projects, Tags
//
// For example:
//  "(A) 2013-07-23 Call Dad @Home @Phone +Family due:2013-07-31 customTag1:Important!"
func (task Task) String() string {
	var text string

	if task.Completed {
		text += "x "
		if task.HasCompletedDate() {
			text += fmt.Sprintf("%s ", task.CompletedDate.Format(DateLayout))
		}
	}

	if task.HasPriority() {
		text += fmt.Sprintf("(%s) ", task.Priority)
	}

	if task.HasCreatedDate() {
		text += fmt.Sprintf("%s ", task.CreatedDate.Format(DateLayout))
	}

	text += task.Todo

	if len(task.Contexts) > 0 {
		sort.Strings(task.Contexts)
		for _, context := range task.Contexts {
			text += fmt.Sprintf(" @%s", context)
		}
	}

	if len(task.Projects) > 0 {
		sort.Strings(task.Projects)
		for _, project := range task.Projects {
			text += fmt.Sprintf(" +%s", project)
		}
	}

	if len(task.AdditionalTags) > 0 {
		// Sort map alphabetically by keys
		keys := make([]string, 0, len(task.AdditionalTags))
		for key := range task.AdditionalTags {
			keys = append(keys, key)
		}
		sort.Strings(keys)
		for _, key := range keys {
			text += fmt.Sprintf(" %s:%s", key, task.AdditionalTags[key])
		}
	}

	if task.HasDueDate() {
		text += fmt.Sprintf(" due:%s", task.DueDate.Format(DateLayout))
	}

	return text
}

// NewTask creates a new empty Task with default values. (CreatedDate is set to Now())
func NewTask() Task {
	task := Task{}
	task.CreatedDate = time.Now()
	return task
}

// ParseTask parses the input text string into a Task struct.
func ParseTask(text string) (*Task, error) {
	var err error

	task := Task{}
	task.Original = strings.Trim(text, "\t\n\r ")
	task.Todo = task.Original

	// Check for completed
	if completedRx.MatchString(task.Original) {
		task.Completed = true
		// Check for completed date
		if completedDateRx.MatchString(task.Original) {
			if date, err := time.Parse(DateLayout, completedDateRx.FindStringSubmatch(task.Original)[1]); err == nil {
				task.CompletedDate = date
			} else {
				return nil, err
			}
		}

		// Remove from Todo text
		task.Todo = completedDateRx.ReplaceAllString(task.Todo, "") // Strip CompletedDate first, otherwise it wouldn't match anymore (^x date...)
		task.Todo = completedRx.ReplaceAllString(task.Todo, "")     // Strip 'x '
	}

	// Check for priority
	if priorityRx.MatchString(task.Original) {
		task.Priority = priorityRx.FindStringSubmatch(task.Original)[2]
		task.Todo = priorityRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for created date
	if createdDateRx.MatchString(task.Original) {
		if date, err := time.Parse(DateLayout, createdDateRx.FindStringSubmatch(task.Original)[2]); err == nil {
			task.CreatedDate = date
			task.Todo = createdDateRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
		} else {
			return nil, err
		}
	}

	// function for collecting projects/contexts as slices from text
	getSlice := func(rx *regexp.Regexp) []string {
		matches := rx.FindAllStringSubmatch(task.Original, -1)
		slice := make([]string, 0, len(matches))
		seen := make(map[string]bool, len(matches))
		for _, match := range matches {
			word := strings.Trim(match[2], "\t\n\r ")
			if _, found := seen[word]; !found {
				slice = append(slice, word)
				seen[word] = true
			}
		}
		sort.Strings(slice)
		return slice
	}

	// Check for contexts
	if contextRx.MatchString(task.Original) {
		task.Contexts = getSlice(contextRx)
		task.Todo = contextRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for projects
	if projectRx.MatchString(task.Original) {
		task.Projects = getSlice(projectRx)
		task.Todo = projectRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Check for additional tags
	if addonTagRx.MatchString(task.Original) {
		matches := addonTagRx.FindAllStringSubmatch(task.Original, -1)
		tags := make(map[string]string, len(matches))
		for _, match := range matches {
			key, value := match[2], match[3]
			if key == "due" { // due date is a known addon tag, it has its own struct field
				if date, err := time.Parse(DateLayout, value); err == nil {
					task.DueDate = date
				} else {
					return nil, err
				}
			} else if key != "" && value != "" {
				tags[key] = value
			}
		}
		task.AdditionalTags = tags
		task.Todo = addonTagRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
	}

	// Trim any remaining whitespaces from Todo text
	task.Todo = strings.Trim(task.Todo, "\t\n\r\f ")

	return &task, err
}

// Task returns a complete task string in todo.txt format.
// See *Task.String() for further information.
func (task *Task) Task() string {
	return task.String()
}

// HasPriority returns true if the task has a priority.
func (task *Task) HasPriority() bool {
	return task.Priority != ""
}

// HasCreatedDate returns true if the task has a created date.
func (task *Task) HasCreatedDate() bool {
	return !task.CreatedDate.IsZero()
}

// HasDueDate returns true if the task has a due date.
func (task *Task) HasDueDate() bool {
	return !task.DueDate.IsZero()
}

// HasCompletedDate returns true if the task has a completed date.
func (task *Task) HasCompletedDate() bool {
	return !task.CompletedDate.IsZero() && task.Completed
}

// Complete sets Task.Completed to 'true' if the task was not already completed.
// Also sets Task.CompletedDate to time.Now()
func (task *Task) Complete() {
	if !task.Completed {
		task.Completed = true
		task.CompletedDate = time.Now()
	}
}

// Reopen sets Task.Completed to 'false' if the task was completed.
// Also resets Task.CompletedDate.
func (task *Task) Reopen() {
	if task.Completed {
		task.Completed = false
		task.CompletedDate = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) // time.IsZero() value
	}
}

// IsOverdue returns true if due date is in the past.
//
// This function does not take the Completed flag into consideration.
// You should check Task.Completed first if needed.
func (task *Task) IsOverdue() bool {
	if task.HasDueDate() {
		return task.DueDate.Before(time.Now())
	}
	return false
}

// Due returns the duration passed since due date, or until due date from now.
// Check with IsOverdue() if the task is overdue or not.
//
// Just as with IsOverdue(), this function does also not take the Completed flag into consideration.
// You should check Task.Completed first if needed.
func (task *Task) Due() time.Duration {
	if task.IsOverdue() {
		return time.Now().Sub(task.DueDate)
	}
	return task.DueDate.Sub(time.Now())
}
<|endoftext|>


<commit_before>package meritop

// These interface is useful for safety check, TG stands for TaskGraph.
type TGMeta interface {
	EpochID() uint64
	ToTaskID() uint64
	FromTaskID() uint64
	SetToTaskID(toID uint64)
	SetFromTaskID(fromID uint64)
	UUID() uint64
}

// Task is a logic repersentation of a computing unit.
// Each task contain at least one Node.
// Each task has exact one master Node and might have multiple salve Nodes.
type Task interface {
	// This is useful to bring the task up to speed from scratch or if it recovers.
	Init(taskID uint64, framework Framework, config Config)

	// Task need to finish up for exit, last chance to save work?
	Exit()

	// These are called by framework implementation so that task implementation can
	// reacts to parent or children restart.
	ParentRestart(parentID uint64)
	ChildRestart(childID uint64)

	ParentDie(parentID uint64)
	ChildDie(childID uint64)

	// Ideally, we should also have the following:
	ParentReady(parentID uint64, meta TGMeta)
	ChildReady(childID uint64, meta TGMeta)

	// This give the task an opportunity to cleanup and regroup.
	SetEpoch(epochID uint64)

	// These are payload for application purpose.
	ServeAsParent(req TGMeta) TGMeta
	ServeAsChild(reg TGMeta) TGMeta

	ParentDataReady(req, response TGMeta)
	ChildDataReady(req, response TGMeta)
}

// Backupable is an interface that task need to implement if they want to have
// hot standby copy. This is another can of beans.
type Backupable interface {
	// Some hooks that need for master slave etc.
	BecameMaster()
	BecameBackup()
}
<commit_msg>Add payload rpc interface<commit_after>package meritop

// These interface is useful for safety check, TG stands for TaskGraph.
type TGMeta interface {
	EpochID() uint64
	ToTaskID() uint64
	FromTaskID() uint64
	UUID() uint64
}

// Task is a logic repersentation of a computing unit.
// Each task contain at least one Node.
// Each task has exact one master Node and might have multiple salve Nodes.
type Task interface {
	// This is useful to bring the task up to speed from scratch or if it recovers.
	Init(taskID uint64, framework Framework, config Config)

	// Task need to finish up for exit, last chance to save work?
	Exit()

	// These are called by framework implementation so that task implementation can
	// reacts to parent or children restart.
	ParentRestart(parentID uint64)
	ChildRestart(childID uint64)

	ParentDie(parentID uint64)
	ChildDie(childID uint64)

	// Ideally, we should also have the following:
	ParentReady(parentID uint64, meta TGMeta)
	ChildReady(childID uint64, meta TGMeta)

	// This give the task an opportunity to cleanup and regroup.
	SetEpoch(epochID uint64)

	// These are payload for application purpose.
	ServeAsParent(req TGMeta) TGMeta
	ServeAsChild(reg TGMeta) TGMeta

	ParentDataReady(req, response TGMeta)
	ChildDataReady(req, response TGMeta)
}

// Backupable is an interface that task need to implement if they want to have
// hot standby copy. This is another can of beans.
type Backupable interface {
	// Some hooks that need for master slave etc.
	BecameMaster()
	BecameBackup()
}
<|endoftext|>