<commit_before>package zd

import "fmt"

// Article struct
type Article struct {
	ID               *float64 `json:"id,omitempty"`
	URL              *string  `json:"url,omitempty"`
	HtmlURL          *string  `json:"html_url,omitempty"`
	Title            *string  `json:"title,omitempty"`
	Name             *string  `json:"name,omitempty"`
	Body             *string  `json:"body,omitempty"`
	Locale           *string  `json:"locale,omitempty"`
	SourceLocale     *string  `json:"source_locale,omitempty"`
	AuthorID         *float64 `json:"author_id,omitempty"`
	CommentsDisabled *bool    `json:"comments_disabled,omitempty"`
	Outdated         *bool    `json:"outdated,omitempty"`
	Labels           []string `json:"label_names,omitempty"`
	Draft            *bool    `json:"draft,omitempty"`
	Promoted         *bool    `json:"promoted,omitempty"`
	Position         *int64   `json:"position,omitempty"`
	VoteSum          *int64   `json:"vote_sum,omitempty"`
	VoteCount        *int64   `json:"vote_count,omitempty"`
	SectionID        *float64 `json:"section_id,omitempty"`
	TranslationIds   []int64  `json:"translation_ids,omitempty"`
	CreatedAt        *string  `json:"created_at,omitempty"`
	UpdatedAt        *string  `json:"updated_at,omitempty"`
}

// ArticleResponse struct
type ArticleResponse struct {
	Article *Article `json:"article"`
}

// ArticleCollectionResponse struct
type ArticleCollectionResponse struct {
	Results   []Article `json:"articles,omitempty"`
	Count     *int64    `json:"count,omitempty"`
	Next      *string   `json:"next_page,omitempty"`
	Page      *int64    `json:"page,omitempty"`
	PageCount *int64    `json:"page_count,omitempty"`
	PerPage   *int64    `json:"per_page,omitempty"`
	Previous  *string   `json:"previous_page,omitempty"`
}

// ArticleService struct
type ArticleService struct {
	client *Client
}

// List function
func (s *ArticleService) List() ([]Article, error) {
	resource := []Article{}
	rp, next, _, err := s.getPage("")
	if err != nil {
		return resource, err
	}
	resource = append(resource, *rp...)

	for next != nil {
		rp, nx, _, err := s.getPage(*next)
		if err != nil {
			return resource, err
		}
		next = nx
		resource = append(resource, *rp...)
	}

	return resource, err
}

func (s *ArticleService) getPage(url string) (*[]Article, *string, *Response, error) {

	if url == "" {
		url = "help_center/articles.json"
	}

	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, nil, err
	}

	result := ArticleCollectionResponse{}
	resp, err := s.client.Do(req, result)
	if err != nil {
		return nil, nil, resp, err
	}

	next := result.Next
	resource := result.Results
	return &resource, next, resp, err
}

// Create func creates a single new article
func (s *ArticleService) Create(a *Article) (*Article, error) {
	article := &Article{}

	if a.SectionID == nil {
		return article, fmt.Errorf("missing required field: section id")
	}

	if a.Title == nil {
		return article, fmt.Errorf("missing required field: article title")
	}

	if a.Body == nil {
		return article, fmt.Errorf("missing required field: article body")
	}

	l := "en-us"

	if a.Locale == nil {
		a.Locale = &l
	}

	ar := &ArticleResponse{Article: a}

	url := fmt.Sprintf("help_center/sections/%v/articles.json", int(*a.SectionID))

	req, err := s.client.NewRequest("POST", url, ar)
	if err != nil {
		return article, err
	}

	result := ArticleResponse{}
	_, err = s.client.Do(req, result)
	if err != nil {
		return article, err
	}

	article = result.Article
	return article, err
}

// Update func updates a single article
func (s *ArticleService) Update(a *Article) (*Article, error) {
	article := &Article{}

	if a.ID == nil {
		return article, fmt.Errorf("missing required field: article id")
	}

	if a.Title == nil {
		return article, fmt.Errorf("missing required field: article title")
	}

	if a.Body == nil {
		return article, fmt.Errorf("missing required field: article body")
	}

	l := "en-us"

	if a.Locale == nil {
		a.Locale = &l
	}

	ar := &ArticleResponse{Article: a}

	url := fmt.Sprintf("help_center/articles/%v.json", int(*a.ID))

	req, err := s.client.NewRequest("PUT", url, ar)
	if err != nil {
		return article, err
	}

	result := &ArticleResponse{}
	_, err = s.client.Do(req, result)
	if err != nil {
		return article, err
	}

	article = result.Article
	return article, err
}

// Delete func deletes a single article
func (s *ArticleService) Delete(id *int64) error {
	if id == nil {
		return fmt.Errorf("missing article id")
	}

	url := fmt.Sprintf("help_center/articles/%v.json", int(*id))

	req, err := s.client.NewRequest("DELETE", url, nil)
	if err != nil {
		return fmt.Errorf("creating new request failed: %v\n", err)
	}

	_, err = s.client.Do(req, nil)
	if err != nil {
		return fmt.Errorf("request failed with: %v\n", err)
	}

	return err
}
<commit_msg>use translations endpoint when updating articles<commit_after>package zd

import "fmt"

// Article struct
type Article struct {
	ID               *float64 `json:"id,omitempty"`
	URL              *string  `json:"url,omitempty"`
	HtmlURL          *string  `json:"html_url,omitempty"`
	Title            *string  `json:"title,omitempty"`
	Name             *string  `json:"name,omitempty"`
	Body             *string  `json:"body,omitempty"`
	Locale           *string  `json:"locale,omitempty"`
	SourceLocale     *string  `json:"source_locale,omitempty"`
	AuthorID         *float64 `json:"author_id,omitempty"`
	CommentsDisabled *bool    `json:"comments_disabled,omitempty"`
	Outdated         *bool    `json:"outdated,omitempty"`
	Labels           []string `json:"label_names,omitempty"`
	Draft            *bool    `json:"draft,omitempty"`
	Promoted         *bool    `json:"promoted,omitempty"`
	Position         *int64   `json:"position,omitempty"`
	VoteSum          *int64   `json:"vote_sum,omitempty"`
	VoteCount        *int64   `json:"vote_count,omitempty"`
	SectionID        *float64 `json:"section_id,omitempty"`
	TranslationIds   []int64  `json:"translation_ids,omitempty"`
	CreatedAt        *string  `json:"created_at,omitempty"`
	UpdatedAt        *string  `json:"updated_at,omitempty"`
}

// ArticleResponse struct
type ArticleResponse struct {
	Article *Article `json:"article"`
}

// ArticleCollectionResponse struct
type ArticleCollectionResponse struct {
	Results   []Article `json:"articles,omitempty"`
	Count     *int64    `json:"count,omitempty"`
	Next      *string   `json:"next_page,omitempty"`
	Page      *int64    `json:"page,omitempty"`
	PageCount *int64    `json:"page_count,omitempty"`
	PerPage   *int64    `json:"per_page,omitempty"`
	Previous  *string   `json:"previous_page,omitempty"`
}

// ArticleService struct
type ArticleService struct {
	client *Client
}

// List function
func (s *ArticleService) List() ([]Article, error) {
	resource := []Article{}
	rp, next, _, err := s.getPage("")
	if err != nil {
		return resource, err
	}
	resource = append(resource, *rp...)

	for next != nil {
		rp, nx, _, err := s.getPage(*next)
		if err != nil {
			return resource, err
		}
		next = nx
		resource = append(resource, *rp...)
	}

	return resource, err
}

func (s *ArticleService) getPage(url string) (*[]Article, *string, *Response, error) {

	if url == "" {
		url = "help_center/articles.json"
	}

	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, nil, err
	}

	result := ArticleCollectionResponse{}
	resp, err := s.client.Do(req, result)
	if err != nil {
		return nil, nil, resp, err
	}

	next := result.Next
	resource := result.Results
	return &resource, next, resp, err
}

// Create func creates a single new article
func (s *ArticleService) Create(a *Article) (*Article, error) {
	article := &Article{}

	if a.SectionID == nil {
		return article, fmt.Errorf("missing required field: section id")
	}

	if a.Title == nil {
		return article, fmt.Errorf("missing required field: article title")
	}

	if a.Body == nil {
		return article, fmt.Errorf("missing required field: article body")
	}

	l := "en-us"

	if a.Locale == nil {
		a.Locale = &l
	}

	ar := &ArticleResponse{Article: a}

	url := fmt.Sprintf("help_center/sections/%v/articles.json", int(*a.SectionID))

	req, err := s.client.NewRequest("POST", url, ar)
	if err != nil {
		return article, err
	}

	result := ArticleResponse{}
	_, err = s.client.Do(req, result)
	if err != nil {
		return article, err
	}

	article = result.Article
	return article, err
}

// Update func updates a single article
func (s *ArticleService) Update(a *Article) (*Article, error) {
	article := &Article{}

	if a.ID == nil {
		return article, fmt.Errorf("missing required field: article id")
	}

	if a.Title == nil {
		return article, fmt.Errorf("missing required field: article title")
	}

	if a.Body == nil {
		return article, fmt.Errorf("missing required field: article body")
	}

	l := "en-us"

	if a.Locale == nil {
		a.Locale = &l
	}

	ar := &ArticleResponse{Article: a}

	url := fmt.Sprintf("help_center/articles/%v/translations/%v.json", int(*a.ID), *a.Locale)

	req, err := s.client.NewRequest("PUT", url, ar)
	if err != nil {
		return article, err
	}

	result := &ArticleResponse{}
	_, err = s.client.Do(req, result)
	if err != nil {
		return article, err
	}

	article = result.Article
	return article, err
}

// Delete func deletes a single article
func (s *ArticleService) Delete(id *int64) error {
	if id == nil {
		return fmt.Errorf("missing article id")
	}

	url := fmt.Sprintf("help_center/articles/%v.json", int(*id))

	req, err := s.client.NewRequest("DELETE", url, nil)
	if err != nil {
		return fmt.Errorf("creating new request failed: %v\n", err)
	}

	_, err = s.client.Do(req, nil)
	if err != nil {
		return fmt.Errorf("request failed with: %v\n", err)
	}

	return err
}
<|endoftext|>


<commit_before>package aws

import (
	"errors"
	"fmt"
	"time"
	"os"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/ec2"
)

type Client struct {
	*ec2.EC2
	Timeout time.Duration
	Interval time.Duration
}

func NewClient(region string, accessKey string, secretKey string, timeoutSec int, intervalSec int) (*Client, error) {
	if timeoutSec <= 0 {
		return nil, errors.New("invalid timeout")
	}
	if intervalSec <= 0 {
		return nil, errors.New("invalid interval")
	}
	if timeoutSec < intervalSec {
		return nil, errors.New("interval should be less than timeout")
	}

	config := aws.Config{}
	envRegion := os.Getenv("AWS_REGION")

	if region == "" && envRegion == "" {
		var err error

		config.Region, err = GetRegion()
		if err != nil {
			return nil, err
		}
	} else if region == "" && envRegion != "" {
		config.Region = envRegion
	} else {
		config.Region = region
	}

	if accessKey != "" && secretKey != "" {
		config.Credentials = aws.Creds(accessKey, secretKey, "")
	}

	return &Client{ec2.New(&config), time.Duration(timeoutSec) * time.Second, time.Duration(intervalSec) * time.Second}, nil
}

func (cli *Client) DescribeENIByID(eniID string) (*ec2.NetworkInterface, error) {
	params := &ec2.DescribeNetworkInterfacesInput{
		NetworkInterfaceIDs: []*string{
			aws.String(eniID),
		},
	}
	resp, err := cli.EC2.DescribeNetworkInterfaces(params)
	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return nil, errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return nil, err
	}

	if len(resp.NetworkInterfaces) < 1 {
		return nil, nil
	}

	return resp.NetworkInterfaces[0], nil
}

func (cli *Client) DescribeENIs() ([]*ec2.NetworkInterface, error) {
	resp, err := cli.EC2.DescribeNetworkInterfaces(nil)
	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return nil, errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return nil, err
	}

	if len(resp.NetworkInterfaces) < 1 {
		return nil, nil
	}

	return resp.NetworkInterfaces, nil
}

func (cli *Client) AttachENI(eniID string, instanceID string, deviceIndex int) error {
	params := &ec2.AttachNetworkInterfaceInput{
		NetworkInterfaceID: aws.String(eniID),
		InstanceID:         aws.String(instanceID),
		DeviceIndex:        aws.Long(int64(deviceIndex)),
	}
	_, err := cli.EC2.AttachNetworkInterface(params)

	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return err
	}

	return nil
}

func (cli *Client) AttachENIWithRetry(eniID string, instanceID string, deviceIndex int) error {
	if err := cli.AttachENI(eniID, instanceID, deviceIndex); err != nil {
		return err
	}

	// Retry until attach event completed or timeout
	for {
		select {
		case <-time.After(cli.Timeout):
			return errors.New(fmt.Sprintf("timeout occured. %d seconds elapsed.", cli.Timeout))
		case <-time.Tick(cli.Interval):
			eni, err := cli.DescribeENIByID(eniID)
			if err != nil {
				return err
			}
			if *eni.Status == "in-use" && eni.Attachment != nil && *eni.Attachment.Status == "attached" {
				return nil // detach completed
			}
		}
	}

	return nil
}

func (cli *Client) DetachENIByAttachmentID(attachmentID string) error {
	params := &ec2.DetachNetworkInterfaceInput{
		AttachmentID: aws.String(attachmentID),
		Force:        aws.Boolean(false),
	}
	_, err := cli.EC2.DetachNetworkInterface(params)

	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return err
	}

	return nil
}

func (cli *Client) DetachENI(eniID string) error {
	eni, err := cli.DescribeENIByID(eniID)
	if err != nil {
		return err
	}

	if eni.Attachment == nil {
		// already detached
		return nil
	}

	return cli.DetachENIByAttachmentID(*eni.Attachment.AttachmentID)
}

func (cli *Client) DetachENIWithRetry(eniID string) error {
	if err := cli.DetachENI(eniID); err != nil {
		return err
	}

	// Retry until detach event completed or timeout
	for {
		select {
		case <-time.After(cli.Timeout):
			return errors.New(fmt.Sprintf("timeout occured. %d seconds elapsed.", cli.Timeout))
		case <-time.Tick(cli.Interval):
			eni, err := cli.DescribeENIByID(eniID)
			if err != nil {
				return err
			}
			if *eni.Status == "available" {
				return nil // detach completed
			}
		}
	}

	return nil
}

func (cli *Client) GrabENI(eniID string, instanceID string, deviceIndex int) (error, bool) {
	eni, err := cli.DescribeENIByID(eniID)
	if err != nil {
		return err, false
	}

	// Skip detaching if the target ENI has still not attached with the other instance
	if eni.Attachment != nil {
		// Do nothing if the target ENI already attached with the target instance
		if *eni.Attachment.InstanceID == instanceID {
			return nil, false
		}

		err = cli.DetachENIByAttachmentID(*eni.Attachment.AttachmentID)
		if err != nil {
			return err, false
		}
	}

	err = cli.AttachENI(eniID, instanceID, deviceIndex)
	if err != nil {
		return err, false
	}

	return nil, true
}
<commit_msg>Improve GrabENI with retry<commit_after>package aws

import (
	"errors"
	"fmt"
	"time"
	"os"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/ec2"
)

type Client struct {
	*ec2.EC2
	Timeout time.Duration
	Interval time.Duration
}

func NewClient(region string, accessKey string, secretKey string, timeoutSec int, intervalSec int) (*Client, error) {
	if timeoutSec <= 0 {
		return nil, errors.New("invalid timeout")
	}
	if intervalSec <= 0 {
		return nil, errors.New("invalid interval")
	}
	if timeoutSec < intervalSec {
		return nil, errors.New("interval should be less than timeout")
	}

	config := aws.Config{}
	envRegion := os.Getenv("AWS_REGION")

	if region == "" && envRegion == "" {
		var err error

		config.Region, err = GetRegion()
		if err != nil {
			return nil, err
		}
	} else if region == "" && envRegion != "" {
		config.Region = envRegion
	} else {
		config.Region = region
	}

	if accessKey != "" && secretKey != "" {
		config.Credentials = aws.Creds(accessKey, secretKey, "")
	}

	return &Client{ec2.New(&config), time.Duration(timeoutSec) * time.Second, time.Duration(intervalSec) * time.Second}, nil
}

func (cli *Client) DescribeENIByID(eniID string) (*ec2.NetworkInterface, error) {
	params := &ec2.DescribeNetworkInterfacesInput{
		NetworkInterfaceIDs: []*string{
			aws.String(eniID),
		},
	}
	resp, err := cli.EC2.DescribeNetworkInterfaces(params)
	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return nil, errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return nil, err
	}

	if len(resp.NetworkInterfaces) < 1 {
		return nil, nil
	}

	return resp.NetworkInterfaces[0], nil
}

func (cli *Client) DescribeENIs() ([]*ec2.NetworkInterface, error) {
	resp, err := cli.EC2.DescribeNetworkInterfaces(nil)
	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return nil, errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return nil, err
	}

	if len(resp.NetworkInterfaces) < 1 {
		return nil, nil
	}

	return resp.NetworkInterfaces, nil
}

func (cli *Client) AttachENI(eniID string, instanceID string, deviceIndex int) error {
	params := &ec2.AttachNetworkInterfaceInput{
		NetworkInterfaceID: aws.String(eniID),
		InstanceID:         aws.String(instanceID),
		DeviceIndex:        aws.Long(int64(deviceIndex)),
	}
	_, err := cli.EC2.AttachNetworkInterface(params)

	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return err
	}

	return nil
}

func (cli *Client) AttachENIWithRetry(eniID string, instanceID string, deviceIndex int) error {
	if err := cli.AttachENI(eniID, instanceID, deviceIndex); err != nil {
		return err
	}

	// Retry until attach event completed or timeout
	for {
		select {
		case <-time.After(cli.Timeout):
			return errors.New(fmt.Sprintf("timeout occured. %d seconds elapsed.", cli.Timeout))
		case <-time.Tick(cli.Interval):
			eni, err := cli.DescribeENIByID(eniID)
			if err != nil {
				return err
			}
			if *eni.Status == "in-use" && eni.Attachment != nil && *eni.Attachment.Status == "attached" {
				return nil // detach completed
			}
		}
	}

	return nil
}

func (cli *Client) DetachENIByAttachmentID(attachmentID string) error {
	params := &ec2.DetachNetworkInterfaceInput{
		AttachmentID: aws.String(attachmentID),
		Force:        aws.Boolean(false),
	}
	_, err := cli.EC2.DetachNetworkInterface(params)

	if awserr := aws.Error(err); awserr != nil {
		// A service error occurred.
		return errors.New(awserr.Error())
	} else if err != nil {
		// A non-service error occurred.
		return err
	}

	return nil
}

func (cli *Client) DetachENI(eniID string) error {
	eni, err := cli.DescribeENIByID(eniID)
	if err != nil {
		return err
	}

	if eni.Attachment == nil {
		// already detached
		return nil
	}

	return cli.DetachENIByAttachmentID(*eni.Attachment.AttachmentID)
}

func (cli *Client) DetachENIWithRetry(eniID string) error {
	if err := cli.DetachENI(eniID); err != nil {
		return err
	}

	// Retry until detach event completed or timeout
	for {
		select {
		case <-time.After(cli.Timeout):
			return errors.New(fmt.Sprintf("timeout occured. %d seconds elapsed.", cli.Timeout))
		case <-time.Tick(cli.Interval):
			eni, err := cli.DescribeENIByID(eniID)
			if err != nil {
				return err
			}
			if *eni.Status == "available" {
				return nil // detach completed
			}
		}
	}

	return nil
}

func (cli *Client) GrabENI(eniID string, instanceID string, deviceIndex int) (error, bool) {
	eni, err := cli.DescribeENIByID(eniID)
	if err != nil {
		return err, false
	}

	// Skip detaching if the target ENI has still not attached with the other instance
	if eni.Attachment != nil {
		// Do nothing if the target ENI already attached with the target instance
		if *eni.Attachment.InstanceID == instanceID {
			return nil, false
		}

		err = cli.DetachENIWithRetry(eniID)
		if err != nil {
			return err, false
		}
	}

	err = cli.AttachENIWithRetry(eniID, instanceID, deviceIndex)
	if err != nil {
		return err, false
	}

	return nil, true
}
<|endoftext|>


<commit_before>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"math"
	"math/rand"
	"sync"
	"time"
)

// BackoffFunc specifies the signature of a function that returns the
// time to wait before the next call to a resource. To stop retrying
// return false in the 2nd return value.
type BackoffFunc func(retry int) (time.Duration, bool)

// Backoff allows callers to implement their own Backoff strategy.
type Backoff interface {
	// Next implements a BackoffFunc.
	Next(retry int) (time.Duration, bool)
}

// -- ZeroBackoff --

// ZeroBackoff is a fixed backoff policy whose backoff time is always zero,
// meaning that the operation is retried immediately without waiting,
// indefinitely.
type ZeroBackoff struct{}

// Next implements BackoffFunc for ZeroBackoff.
func (b ZeroBackoff) Next(retry int) (time.Duration, bool) {
	return 0, true
}

// -- StopBackoff --

// StopBackoff is a fixed backoff policy that always returns false for
// Next(), meaning that the operation should never be retried.
type StopBackoff struct{}

// Next implements BackoffFunc for StopBackoff.
func (b StopBackoff) Next(retry int) (time.Duration, bool) {
	return 0, false
}

// -- ConstantBackoff --

// ConstantBackoff is a backoff policy that always returns the same delay.
type ConstantBackoff struct {
	interval time.Duration
}

// NewConstantBackoff returns a new ConstantBackoff.
func NewConstantBackoff(interval time.Duration) *ConstantBackoff {
	return &ConstantBackoff{interval: interval}
}

// Next implements BackoffFunc for ConstantBackoff.
func (b *ConstantBackoff) Next(retry int) (time.Duration, bool) {
	return b.interval, true
}

// -- Exponential --

// ExponentialBackoff implements the simple exponential backoff described by
// Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html.
type ExponentialBackoff struct {
	sync.Mutex
	t float64 // initial timeout (in msec)
	f float64 // exponential factor (e.g. 2)
	m float64 // maximum timeout (in msec)
}

// NewExponentialBackoff returns a ExponentialBackoff backoff policy.
// Use initialTimeout to set the first/minimal interval
// and maxTimeout to set the maximum wait interval.
func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration) *ExponentialBackoff {
	return &ExponentialBackoff{
		t: float64(int64(initialTimeout / time.Millisecond)),
		f: 2.0,
		m: float64(int64(maxTimeout / time.Millisecond)),
	}
}

// Next implements BackoffFunc for ExponentialBackoff.
func (b *ExponentialBackoff) Next(retry int) (time.Duration, bool) {
	b.Lock()
	defer b.Unlock()

	r := 1.0 + rand.Float64() // random number in [1..2]
	m := math.Min(r*b.t*math.Pow(b.f, float64(retry)), b.m)
	if m >= b.m {
		return 0, false
	}
	d := time.Duration(int64(m)) * time.Millisecond
	return d, true
}

// -- Simple Backoff --

// SimpleBackoff takes a list of fixed values for backoff intervals.
// Each call to Next returns the next value from that fixed list.
// After each value is returned, subsequent calls to Next will only return
// the last element. The values are optionally "jittered" (off by default).
type SimpleBackoff struct {
	sync.Mutex
	ticks  []int
	jitter bool
}

// NewSimpleBackoff creates a SimpleBackoff algorithm with the specified
// list of fixed intervals in milliseconds.
func NewSimpleBackoff(ticks ...int) *SimpleBackoff {
	return &SimpleBackoff{
		ticks:  ticks,
		jitter: false,
	}
}

// Jitter enables or disables jittering values.
func (b *SimpleBackoff) Jitter(flag bool) *SimpleBackoff {
	b.Lock()
	b.jitter = flag
	b.Unlock()
	return b
}

// jitter randomizes the interval to return a value of [0.5*millis .. 1.5*millis].
func jitter(millis int) int {
	if millis <= 0 {
		return 0
	}
	return millis/2 + rand.Intn(millis)
}

// Next implements BackoffFunc for SimpleBackoff.
func (b *SimpleBackoff) Next(retry int) (time.Duration, bool) {
	b.Lock()
	defer b.Unlock()

	if retry >= len(b.ticks) {
		return 0, false
	}

	ms := b.ticks[retry]
	if b.jitter {
		ms = jitter(ms)
	}
	return time.Duration(ms) * time.Millisecond, true
}
<commit_msg>Remove needless mutex<commit_after>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"math"
	"math/rand"
	"sync"
	"time"
)

// BackoffFunc specifies the signature of a function that returns the
// time to wait before the next call to a resource. To stop retrying
// return false in the 2nd return value.
type BackoffFunc func(retry int) (time.Duration, bool)

// Backoff allows callers to implement their own Backoff strategy.
type Backoff interface {
	// Next implements a BackoffFunc.
	Next(retry int) (time.Duration, bool)
}

// -- ZeroBackoff --

// ZeroBackoff is a fixed backoff policy whose backoff time is always zero,
// meaning that the operation is retried immediately without waiting,
// indefinitely.
type ZeroBackoff struct{}

// Next implements BackoffFunc for ZeroBackoff.
func (b ZeroBackoff) Next(retry int) (time.Duration, bool) {
	return 0, true
}

// -- StopBackoff --

// StopBackoff is a fixed backoff policy that always returns false for
// Next(), meaning that the operation should never be retried.
type StopBackoff struct{}

// Next implements BackoffFunc for StopBackoff.
func (b StopBackoff) Next(retry int) (time.Duration, bool) {
	return 0, false
}

// -- ConstantBackoff --

// ConstantBackoff is a backoff policy that always returns the same delay.
type ConstantBackoff struct {
	interval time.Duration
}

// NewConstantBackoff returns a new ConstantBackoff.
func NewConstantBackoff(interval time.Duration) *ConstantBackoff {
	return &ConstantBackoff{interval: interval}
}

// Next implements BackoffFunc for ConstantBackoff.
func (b *ConstantBackoff) Next(retry int) (time.Duration, bool) {
	return b.interval, true
}

// -- Exponential --

// ExponentialBackoff implements the simple exponential backoff described by
// Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html.
type ExponentialBackoff struct {
	t float64 // initial timeout (in msec)
	f float64 // exponential factor (e.g. 2)
	m float64 // maximum timeout (in msec)
}

// NewExponentialBackoff returns a ExponentialBackoff backoff policy.
// Use initialTimeout to set the first/minimal interval
// and maxTimeout to set the maximum wait interval.
func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration) *ExponentialBackoff {
	return &ExponentialBackoff{
		t: float64(int64(initialTimeout / time.Millisecond)),
		f: 2.0,
		m: float64(int64(maxTimeout / time.Millisecond)),
	}
}

// Next implements BackoffFunc for ExponentialBackoff.
func (b *ExponentialBackoff) Next(retry int) (time.Duration, bool) {
	r := 1.0 + rand.Float64() // random number in [1..2]
	m := math.Min(r*b.t*math.Pow(b.f, float64(retry)), b.m)
	if m >= b.m {
		return 0, false
	}
	d := time.Duration(int64(m)) * time.Millisecond
	return d, true
}

// -- Simple Backoff --

// SimpleBackoff takes a list of fixed values for backoff intervals.
// Each call to Next returns the next value from that fixed list.
// After each value is returned, subsequent calls to Next will only return
// the last element. The values are optionally "jittered" (off by default).
type SimpleBackoff struct {
	sync.Mutex
	ticks  []int
	jitter bool
}

// NewSimpleBackoff creates a SimpleBackoff algorithm with the specified
// list of fixed intervals in milliseconds.
func NewSimpleBackoff(ticks ...int) *SimpleBackoff {
	return &SimpleBackoff{
		ticks:  ticks,
		jitter: false,
	}
}

// Jitter enables or disables jittering values.
func (b *SimpleBackoff) Jitter(flag bool) *SimpleBackoff {
	b.Lock()
	b.jitter = flag
	b.Unlock()
	return b
}

// jitter randomizes the interval to return a value of [0.5*millis .. 1.5*millis].
func jitter(millis int) int {
	if millis <= 0 {
		return 0
	}
	return millis/2 + rand.Intn(millis)
}

// Next implements BackoffFunc for SimpleBackoff.
func (b *SimpleBackoff) Next(retry int) (time.Duration, bool) {
	b.Lock()
	defer b.Unlock()

	if retry >= len(b.ticks) {
		return 0, false
	}

	ms := b.ticks[retry]
	if b.jitter {
		ms = jitter(ms)
	}
	return time.Duration(ms) * time.Millisecond, true
}
<|endoftext|>


<commit_before>package lcs

import "github.com/catorpilor/LeetCode/utils"

func longestCommonSeq(text1, text2 string) int {
	m, n := len(text1), len(text2)
	if text1 == text2 {
		return m
	}
	dp := make([]int, n+1)
	var upleft int
	for i := 1; i <= m; i++ {
		upleft = dp[0]
		for j := 1; j <= n; j++ {
			prev, cur := dp[j-1], dp[j]
			if text1[i-1] == text2[j-1] {
				dp[j] = upleft + 1
			} else {
				dp[j] = utils.Max(prev, cur)
			}
			upleft = cur
		}
	}
	return dp[n]
}
<commit_msg>doc: add some comments<commit_after>package lcs

import "github.com/catorpilor/LeetCode/utils"

// longestCommonSeq using dp to solve the problem
// time complexity is O(mn)
// space compelxity is o(n)
func longestCommonSeq(text1, text2 string) int {
	m, n := len(text1), len(text2)
	if text1 == text2 {
		return m
	}
	dp := make([]int, n+1)
	var upleft int
	for i := 1; i <= m; i++ {
		upleft = dp[0]
		for j := 1; j <= n; j++ {
			prev, cur := dp[j-1], dp[j]
			if text1[i-1] == text2[j-1] {
				dp[j] = upleft + 1
			} else {
				dp[j] = utils.Max(prev, cur)
			}
			upleft = cur
		}
	}
	return dp[n]
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/adolphlwq/tinyurl/entity"
	"github.com/gin-gonic/gin"
)

// ParseURL parse shorten path and return source url
func ParseURL(c *gin.Context, appService *ServiceProvider) {
	shortPath := c.Param("short_path")
	if shortPath == "" {
		logrus.Warnf("shortpath is nil, return default home path.\n")
		c.Redirect(http.StatusMovedPermanently, "http://tinyurl.adolphlwq.xyz")
	}

	var url entity.URL
	appService.MysqlClient.DB.Where("short_path = ?", shortPath).First(&url)
	if url.OriginUrl == "" {
		logrus.Warnf("short url has no record in db.\n")
		c.Redirect(http.StatusMovedPermanently, "http://tinyurl.adolphlwq.xyz")
	}

	c.Redirect(http.StatusMovedPermanently, url.OriginUrl)
}
<commit_msg>OriginUrl -> OriginURL<commit_after>package server

import (
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/adolphlwq/tinyurl/entity"
	"github.com/gin-gonic/gin"
)

// ParseURL parse shorten path and return source url
func ParseURL(c *gin.Context, appService *ServiceProvider) {
	shortPath := c.Param("short_path")
	if shortPath == "" {
		logrus.Warnf("shortpath is nil, return default home path.\n")
		c.Redirect(http.StatusMovedPermanently, "http://tinyurl.adolphlwq.xyz")
	}

	var url entity.URL
	appService.MysqlClient.DB.Where("short_path = ?", shortPath).First(&url)
	if url.OriginURL == "" {
		logrus.Warnf("short url has no record in db.\n")
		c.Redirect(http.StatusMovedPermanently, "http://tinyurl.adolphlwq.xyz")
	}

	c.Redirect(http.StatusMovedPermanently, url.OriginURL)
}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"testing"
	"unicode/utf8"
)

func TestDraw(t *testing.T) {
	// key is termWidth
	testSuite := map[int][]struct {
		name           string
		style          string
		total, current int64
		barWidth       int
		trim           bool
		reverse        bool
		refill         int64
		want           string
	}{
		0: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "…  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "",
			},
		},
		1: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "…  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "",
			},
		},
		2: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[]",
			},
		},
		3: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[-]",
			},
		},
		4: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>-]",
			},
		},
		5: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [-] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>--]",
			},
		},
		6: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>-] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>---]",
			},
		},
		7: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>--] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=>---]",
			},
		},
		8: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>---] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=>----]",
			},
		},
		80: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [========================>---------------------------------------------------] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=========================>----------------------------------------------------]",
			},
			{
				name:     "t,c,bw{60,20,60}",
				total:    60,
				current:  20,
				barWidth: 60,
				want:     " [==================>---------------------------------------] ",
			},
			{
				name:     "t,c,bw{60,20,60}trim",
				total:    60,
				current:  20,
				barWidth: 60,
				trim:     true,
				want:     "[==================>---------------------------------------]",
			},
		},
		100: {
			{
				name:    "t,c{100,0}",
				total:   100,
				current: 0,
				want:    " [------------------------------------------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,0}trim",
				total:   100,
				current: 0,
				trim:    true,
				want:    "[--------------------------------------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,1}",
				total:   100,
				current: 1,
				want:    " [>-----------------------------------------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,1}trim",
				total:   100,
				current: 1,
				trim:    true,
				want:    "[>-------------------------------------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,33}",
				total:   100,
				current: 33,
				want:    " [===============================>----------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,33}trim",
				total:   100,
				current: 33,
				trim:    true,
				want:    "[===============================>------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,33}trim,rev",
				total:   100,
				current: 33,
				trim:    true,
				reverse: true,
				want:    "[------------------------------------------------------------------<===============================]",
			},
			{
				name:    "t,c,r{100,33,33}",
				total:   100,
				current: 33,
				refill:  33,
				want:    " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
			},
			{
				name:    "t,c,r{100,33,33}trim",
				total:   100,
				current: 33,
				refill:  33,
				trim:    true,
				want:    "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
			},
			{
				name:    "t,c,r{100,33,33}trim,rev",
				total:   100,
				current: 33,
				refill:  33,
				trim:    true,
				reverse: true,
				want:    "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]",
			},
			{
				name:    "t,c,r{100,40,33}",
				total:   100,
				current: 40,
				refill:  33,
				want:    " [+++++++++++++++++++++++++++++++======>----------------------------------------------------------] ",
			},
			{
				name:    "t,c,r{100,40,33}trim",
				total:   100,
				current: 40,
				refill:  33,
				trim:    true,
				want:    "[+++++++++++++++++++++++++++++++=======>-----------------------------------------------------------]",
			},
			{
				name:    "t,c{100,99}",
				total:   100,
				current: 99,
				want:    " [==============================================================================================>-] ",
			},
			{
				name:    "t,c{100,99}trim",
				total:   100,
				current: 99,
				trim:    true,
				want:    "[================================================================================================>-]",
			},
			{
				name:    "t,c{100,100}",
				total:   100,
				current: 100,
				want:    " [================================================================================================] ",
			},
			{
				name:    "t,c{100,100}trim",
				total:   100,
				current: 100,
				trim:    true,
				want:    "[==================================================================================================]",
			},
			{
				name:    "[=の-] t,c{100,1}",
				style:   "[=の-]",
				total:   100,
				current: 1,
				want:    " [の---------------------------------------------------------------------------------------------…] ",
			},
		},
	}

	var tmpBuf bytes.Buffer
	for tw, cases := range testSuite {
		for _, tc := range cases {
			s := newTestState(tc.style, tc.reverse)
			s.reqWidth = tc.barWidth
			s.total = tc.total
			s.current = tc.current
			s.trimSpace = tc.trim
			if tc.refill > 0 {
				if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
					f.SetRefill(tc.refill)
				}
			}
			tmpBuf.Reset()
			tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
			by := tmpBuf.Bytes()

			got := string(by[:len(by)-1])
			if !utf8.ValidString(got) {
				t.Fail()
			}
			if got != tc.want {
				t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got))
			}
		}
	}
}

func newTestState(style string, reverse bool) *bState {
	if style == "" {
		style = DefaultBarStyle
	}
	s := &bState{
		filler: NewBarFiller(style, reverse),
		bufP:   new(bytes.Buffer),
		bufB:   new(bytes.Buffer),
		bufA:   new(bytes.Buffer),
	}
	return s
}
<commit_msg>extra test case<commit_after>package mpb

import (
	"bytes"
	"testing"
	"unicode/utf8"
)

func TestDraw(t *testing.T) {
	// key is termWidth
	testSuite := map[int][]struct {
		name           string
		style          string
		total, current int64
		barWidth       int
		trim           bool
		reverse        bool
		refill         int64
		want           string
	}{
		0: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "…  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "",
			},
		},
		1: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "…  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "",
			},
		},
		2: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[]",
			},
		},
		3: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    "  ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[-]",
			},
		},
		4: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>-]",
			},
		},
		5: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [-] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>--]",
			},
		},
		6: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>-] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[>---]",
			},
		},
		7: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>--] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=>---]",
			},
		},
		8: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [>---] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=>----]",
			},
		},
		80: {
			{
				name:    "t,c{60,20}",
				total:   60,
				current: 20,
				want:    " [========================>---------------------------------------------------] ",
			},
			{
				name:    "t,c{60,20}trim",
				total:   60,
				current: 20,
				trim:    true,
				want:    "[=========================>----------------------------------------------------]",
			},
			{
				name:     "t,c,bw{60,20,60}",
				total:    60,
				current:  20,
				barWidth: 60,
				want:     " [==================>---------------------------------------] ",
			},
			{
				name:     "t,c,bw{60,20,60}trim",
				total:    60,
				current:  20,
				barWidth: 60,
				trim:     true,
				want:     "[==================>---------------------------------------]",
			},
		},
		100: {
			{
				name:    "t,c{100,0}",
				total:   100,
				current: 0,
				want:    " [------------------------------------------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,0}trim",
				total:   100,
				current: 0,
				trim:    true,
				want:    "[--------------------------------------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,1}",
				total:   100,
				current: 1,
				want:    " [>-----------------------------------------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,1}trim",
				total:   100,
				current: 1,
				trim:    true,
				want:    "[>-------------------------------------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,33}",
				total:   100,
				current: 33,
				want:    " [===============================>----------------------------------------------------------------] ",
			},
			{
				name:    "t,c{100,33}trim",
				total:   100,
				current: 33,
				trim:    true,
				want:    "[===============================>------------------------------------------------------------------]",
			},
			{
				name:    "t,c{100,33}trim,rev",
				total:   100,
				current: 33,
				trim:    true,
				reverse: true,
				want:    "[------------------------------------------------------------------<===============================]",
			},
			{
				name:    "t,c,r{100,33,33}",
				total:   100,
				current: 33,
				refill:  33,
				want:    " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
			},
			{
				name:    "t,c,r{100,33,33}trim",
				total:   100,
				current: 33,
				refill:  33,
				trim:    true,
				want:    "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
			},
			{
				name:    "t,c,r{100,33,33}trim,rev",
				total:   100,
				current: 33,
				refill:  33,
				trim:    true,
				reverse: true,
				want:    "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]",
			},
			{
				name:    "t,c,r{100,40,33}",
				total:   100,
				current: 40,
				refill:  33,
				want:    " [+++++++++++++++++++++++++++++++======>----------------------------------------------------------] ",
			},
			{
				name:    "t,c,r{100,40,33}trim",
				total:   100,
				current: 40,
				refill:  33,
				trim:    true,
				want:    "[+++++++++++++++++++++++++++++++=======>-----------------------------------------------------------]",
			},
			{
				name:    "t,c{100,99}",
				total:   100,
				current: 99,
				want:    " [==============================================================================================>-] ",
			},
			{
				name:    "t,c{100,99}trim",
				total:   100,
				current: 99,
				trim:    true,
				want:    "[================================================================================================>-]",
			},
			{
				name:    "t,c{100,100}",
				total:   100,
				current: 100,
				want:    " [================================================================================================] ",
			},
			{
				name:    "t,c{100,100}trim",
				total:   100,
				current: 100,
				trim:    true,
				want:    "[==================================================================================================]",
			},
			{
				name:    "[=の-] t,c{100,1}",
				style:   "[=の-]",
				total:   100,
				current: 1,
				want:    " [の---------------------------------------------------------------------------------------------…] ",
			},
		},
		197: {
			{
				name:     "t,c,r{97486999,2805950,2805483}trim",
				total:    97486999,
				current:  2805950,
				refill:   2805483,
				barWidth: 60,
				trim:     true,
				want:     "[+>--------------------------------------------------------]",
			},
		},
	}

	var tmpBuf bytes.Buffer
	for tw, cases := range testSuite {
		for _, tc := range cases {
			s := newTestState(tc.style, tc.reverse)
			s.reqWidth = tc.barWidth
			s.total = tc.total
			s.current = tc.current
			s.trimSpace = tc.trim
			if tc.refill > 0 {
				if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
					f.SetRefill(tc.refill)
				}
			}
			tmpBuf.Reset()
			tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
			by := tmpBuf.Bytes()

			got := string(by[:len(by)-1])
			if !utf8.ValidString(got) {
				t.Fail()
			}
			if got != tc.want {
				t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got))
			}
		}
	}
}

func newTestState(style string, reverse bool) *bState {
	if style == "" {
		style = DefaultBarStyle
	}
	s := &bState{
		filler: NewBarFiller(style, reverse),
		bufP:   new(bytes.Buffer),
		bufB:   new(bytes.Buffer),
		bufA:   new(bytes.Buffer),
	}
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/ninjasphere/sphere-go-led-controller/model"
	"io/ioutil"
	"os"
	"os/exec"
	"reflect"
	"strings"
	"time"
)

// There are now 5 states: rest, select, grace, commit and abort.

// The device is in the rest state until the button is depressed. It then
// moves to the select state.

// In the select state, the LED cycles between the following modes:

// - halt (grey)
// - reboot (green)
// - reset-userdata (yellow)
// - reset-root (red)
// - abort (white)

// When the user releases the button, the device enters the grace state for the specified mode unless the selection
// was abort, in which case it enters the abort state.

// In the grace state, the color fades. During this time, if the user
// presses the button again, the device moves into the abort state. Otherwise,
// the device proceeds to the commit state and the action selected action
// is taken.

// In the abort state, the color fades from white to black and then the device returns to the rest state.

const (
	shortDelay        = time.Millisecond * time.Duration(100)
	selectionDelay    = time.Second * time.Duration(3)
	graceDelay        = time.Second * time.Duration(30)
	abortDelay        = time.Second * time.Duration(3)
	factoryResetMagic = 168
)

// the modes that we cycle between when we are in the 'select' state.

// the modes form a pendulum that swings from halt -> reboot -> reset-userdata ->reset-root -> abort and back again.

var (
	modeCycle = []string{"halt", "reboot", "reset-userdata", "reset-root", "abort", "reset-root", "reset-userdata", "reboot", "halt", "abort"}
)

// a resetButton is a state machine that listens to the reset button
type resetButton struct {
	current   state                    // the current state of the reset button controller
	modeIndex int                      // the currently selected mode
	callback  func(m *model.ResetMode) // the callback used to display the state of the controller to the user
	timeout   *time.Timer              // the timer for the current state
	ticks     *time.Timer              // the tick timer - we sample hardware button on these ticks
}

// a state of the resetButton state machine
type state interface {
	onEnter(r *resetButton)         // method called on entry to a new state.
	onUp(r *resetButton) state      // method called when the reset button is released
	onDown(r *resetButton) state    // method called when the down button is released
	onTimeout(r *resetButton) state // method called when the timeout expires.
}

// start a new reset button monitor
func startResetMonitor(callback func(m *model.ResetMode)) {
	r := &resetButton{
		current:   &stateRest{},
		modeIndex: 0,
		callback:  callback,
		timeout:   time.NewTimer(0),
		ticks:     time.NewTimer(shortDelay),
	}
	r.timeout.Stop()
	select {
	case _ = <-r.timeout.C:
	default:
	}
	go r.run()
}

// transition the receiver to the new state, if the new state is not nil.
func (r *resetButton) transition(s state) {
	if s != nil {
		r.current = s
		r.timeout.Stop()
		select {
		case _ = <-r.timeout.C:
		default:
		}
		s.onEnter(r)
	}
}

// run the state machine
func (r *resetButton) run() {
	down := false
	r.transition(r.current)
	for {
		if reset, err := os.Open("/sys/class/gpio/gpio20/value"); err != nil {
			logger.Warningf("failed to open /sys/class/gpio/gpio20/value - reset loop aborting: %v", err)
			return
		} else {
			if bytes, err := ioutil.ReadAll(reset); err != nil {
				logger.Warningf("aborting read loop - failed to read reset button: %v", err)
				return
			} else {
				contents := string(bytes)
				contents = strings.TrimSpace(contents)
				if contents == "0" {
					if !down {
						down = true
						r.transition(r.current.onDown(r))
					}
				} else if contents == "1" {
					if down {
						down = false
						r.transition(r.current.onUp(r))
					}
				} else {
					logger.Warningf("unexpected input from button %s", contents)
				}
			}

			reset.Close()
		}

		select {
		case _ = <-r.ticks.C:
			r.ticks.Reset(shortDelay)
		case _ = <-r.timeout.C:
			r.transition(r.current.onTimeout(r))
		}
	}
}

// describe the state machine
func (r *resetButton) String() string {
	return fmt.Sprintf("%s - %s\n", reflect.ValueOf(r.current).Type().Elem(), modeCycle[r.modeIndex])
}

// commit the currently selected mode
func (r *resetButton) commit() {
	if path, err := exec.LookPath("reset-helper.sh"); err != nil {
		logger.Warningf("could not find reset-helper.sh: %v", err)
	} else {
		if err := exec.Command(path, modeCycle[r.modeIndex]).Run(); err != nil {
			logger.Warningf("failed to launch reset-helper.sh: %v", err)
		}
	}
	if modeCycle[r.modeIndex] == "reset-root" {
		os.Exit(factoryResetMagic)
	}
}

//
// The baseState is a pseudo-state from which other states inherit the default
// implementation of the state interface.
//
type baseState struct {
}

func (s *baseState) onEnter(r *resetButton) {
}

func (s *baseState) onDown(r *resetButton) state {
	return nil
}

func (s *baseState) onUp(r *resetButton) state {
	return nil
}

func (s *baseState) onTimeout(r *resetButton) state {
	return nil
}

// stateRest

//
// This is the rest state for the reset button. In this state, we wait for
// the reset button to be pressed.
//
type stateRest struct {
	baseState
}

func (s *stateRest) onEnter(r *resetButton) {
	r.modeIndex = 0
	r.callback(&model.ResetMode{
		Mode:     "none",
		Hold:     true,
		Duration: 0,
	})
}

func (s *stateRest) onDown(r *resetButton) state {
	return &stateSelect{}
}

// stateSelect

//
// In this state we cycle between the different possible
// modes until the user releases the button, we then
// move into the grace state or, if the currently displayed
// mode is "abort", the abort state.
//

type stateSelect struct {
	baseState
}

func (s *stateSelect) onEnter(r *resetButton) {
	r.timeout.Reset(selectionDelay)
	r.callback(&model.ResetMode{
		Mode:     modeCycle[r.modeIndex],
		Hold:     true,
		Duration: selectionDelay,
	})
}

func (s *stateSelect) onUp(r *resetButton) state {
	if modeCycle[r.modeIndex] == "abort" {
		return &stateAbort{}
	} else {
		return &stateGrace{}
	}
}

func (s *stateSelect) onTimeout(r *resetButton) state {
	r.modeIndex += 1
	r.modeIndex = r.modeIndex % len(modeCycle)
	return &stateSelect{}
}

// stateGrace

//
// In this state, we give the user an opportunity to cancel the their
// selected action by reacting to down presses. If the user presses
// the button, we move into the abort state, otherwise we
// move to the commit state where the selected mode is committed.
//

type stateGrace struct {
	baseState
}

func (s *stateGrace) onEnter(r *resetButton) {
	r.timeout.Reset(graceDelay)
	r.callback(&model.ResetMode{
		Mode:     modeCycle[r.modeIndex],
		Hold:     false,
		Duration: graceDelay,
	})
}

func (s *stateGrace) onTimeout(r *resetButton) state {
	return &stateCommit{}
}

func (s *stateGrace) onDown(r *resetButton) state {
	return &stateAbort{}
}

// stateAbort

//
// In this state, we provide a visual indication to the user that
// their abort request was respected. In particular, we flash white, then fade.
//
type stateAbort struct {
	baseState
}

func (s *stateAbort) onEnter(r *resetButton) {
	r.timeout.Reset(abortDelay)
	r.callback(&model.ResetMode{
		Mode:     "abort",
		Hold:     true,
		Duration: abortDelay,
	})
}

func (s *stateAbort) onTimeout(r *resetButton) state {
	return &stateRest{}
}

// onEnter - commit the action.
type stateCommit struct {
	baseState
}

func (s *stateCommit) onEnter(r *resetButton) {
	r.commit()
}
<commit_msg>Fade white on abort and do it quickly.<commit_after>package main

import (
	"fmt"
	"github.com/ninjasphere/sphere-go-led-controller/model"
	"io/ioutil"
	"os"
	"os/exec"
	"reflect"
	"strings"
	"time"
)

// There are now 5 states: rest, select, grace, commit and abort.

// The device is in the rest state until the button is depressed. It then
// moves to the select state.

// In the select state, the LED cycles between the following modes:

// - halt (grey)
// - reboot (green)
// - reset-userdata (yellow)
// - reset-root (red)
// - abort (white)

// When the user releases the button, the device enters the grace state for the specified mode unless the selection
// was abort, in which case it enters the abort state.

// In the grace state, the color fades. During this time, if the user
// presses the button again, the device moves into the abort state. Otherwise,
// the device proceeds to the commit state and the action selected action
// is taken.

// In the abort state, the color fades from white to black and then the device returns to the rest state.

const (
	shortDelay        = time.Millisecond * time.Duration(100)
	selectionDelay    = time.Second * time.Duration(3)
	graceDelay        = time.Second * time.Duration(30)
	abortDelay        = time.Second * time.Duration(1)
	factoryResetMagic = 168
)

// the modes that we cycle between when we are in the 'select' state.

// the modes form a pendulum that swings from halt -> reboot -> reset-userdata ->reset-root -> abort and back again.

var (
	modeCycle = []string{"halt", "reboot", "reset-userdata", "reset-root", "abort", "reset-root", "reset-userdata", "reboot", "halt", "abort"}
)

// a resetButton is a state machine that listens to the reset button
type resetButton struct {
	current   state                    // the current state of the reset button controller
	modeIndex int                      // the currently selected mode
	callback  func(m *model.ResetMode) // the callback used to display the state of the controller to the user
	timeout   *time.Timer              // the timer for the current state
	ticks     *time.Timer              // the tick timer - we sample hardware button on these ticks
}

// a state of the resetButton state machine
type state interface {
	onEnter(r *resetButton)         // method called on entry to a new state.
	onUp(r *resetButton) state      // method called when the reset button is released
	onDown(r *resetButton) state    // method called when the down button is released
	onTimeout(r *resetButton) state // method called when the timeout expires.
}

// start a new reset button monitor
func startResetMonitor(callback func(m *model.ResetMode)) {
	r := &resetButton{
		current:   &stateRest{},
		modeIndex: 0,
		callback:  callback,
		timeout:   time.NewTimer(0),
		ticks:     time.NewTimer(shortDelay),
	}
	r.timeout.Stop()
	select {
	case _ = <-r.timeout.C:
	default:
	}
	go r.run()
}

// transition the receiver to the new state, if the new state is not nil.
func (r *resetButton) transition(s state) {
	if s != nil {
		r.current = s
		r.timeout.Stop()
		select {
		case _ = <-r.timeout.C:
		default:
		}
		s.onEnter(r)
	}
}

// run the state machine
func (r *resetButton) run() {
	down := false
	r.transition(r.current)
	for {
		if reset, err := os.Open("/sys/class/gpio/gpio20/value"); err != nil {
			logger.Warningf("failed to open /sys/class/gpio/gpio20/value - reset loop aborting: %v", err)
			return
		} else {
			if bytes, err := ioutil.ReadAll(reset); err != nil {
				logger.Warningf("aborting read loop - failed to read reset button: %v", err)
				return
			} else {
				contents := string(bytes)
				contents = strings.TrimSpace(contents)
				if contents == "0" {
					if !down {
						down = true
						r.transition(r.current.onDown(r))
					}
				} else if contents == "1" {
					if down {
						down = false
						r.transition(r.current.onUp(r))
					}
				} else {
					logger.Warningf("unexpected input from button %s", contents)
				}
			}

			reset.Close()
		}

		select {
		case _ = <-r.ticks.C:
			r.ticks.Reset(shortDelay)
		case _ = <-r.timeout.C:
			r.transition(r.current.onTimeout(r))
		}
	}
}

// describe the state machine
func (r *resetButton) String() string {
	return fmt.Sprintf("%s - %s\n", reflect.ValueOf(r.current).Type().Elem(), modeCycle[r.modeIndex])
}

// commit the currently selected mode
func (r *resetButton) commit() {
	if path, err := exec.LookPath("reset-helper.sh"); err != nil {
		logger.Warningf("could not find reset-helper.sh: %v", err)
	} else {
		if err := exec.Command(path, modeCycle[r.modeIndex]).Run(); err != nil {
			logger.Warningf("failed to launch reset-helper.sh: %v", err)
		}
	}
	if modeCycle[r.modeIndex] == "reset-root" {
		os.Exit(factoryResetMagic)
	}
}

//
// The baseState is a pseudo-state from which other states inherit the default
// implementation of the state interface.
//
type baseState struct {
}

func (s *baseState) onEnter(r *resetButton) {
}

func (s *baseState) onDown(r *resetButton) state {
	return nil
}

func (s *baseState) onUp(r *resetButton) state {
	return nil
}

func (s *baseState) onTimeout(r *resetButton) state {
	return nil
}

// stateRest

//
// This is the rest state for the reset button. In this state, we wait for
// the reset button to be pressed.
//
type stateRest struct {
	baseState
}

func (s *stateRest) onEnter(r *resetButton) {
	r.modeIndex = 0
	r.callback(&model.ResetMode{
		Mode:     "none",
		Hold:     true,
		Duration: 0,
	})
}

func (s *stateRest) onDown(r *resetButton) state {
	return &stateSelect{}
}

// stateSelect

//
// In this state we cycle between the different possible
// modes until the user releases the button, we then
// move into the grace state or, if the currently displayed
// mode is "abort", the abort state.
//

type stateSelect struct {
	baseState
}

func (s *stateSelect) onEnter(r *resetButton) {
	r.timeout.Reset(selectionDelay)
	r.callback(&model.ResetMode{
		Mode:     modeCycle[r.modeIndex],
		Hold:     true,
		Duration: selectionDelay,
	})
}

func (s *stateSelect) onUp(r *resetButton) state {
	if modeCycle[r.modeIndex] == "abort" {
		return &stateAbort{}
	} else {
		return &stateGrace{}
	}
}

func (s *stateSelect) onTimeout(r *resetButton) state {
	r.modeIndex += 1
	r.modeIndex = r.modeIndex % len(modeCycle)
	return &stateSelect{}
}

// stateGrace

//
// In this state, we give the user an opportunity to cancel the their
// selected action by reacting to down presses. If the user presses
// the button, we move into the abort state, otherwise we
// move to the commit state where the selected mode is committed.
//

type stateGrace struct {
	baseState
}

func (s *stateGrace) onEnter(r *resetButton) {
	r.timeout.Reset(graceDelay)
	r.callback(&model.ResetMode{
		Mode:     modeCycle[r.modeIndex],
		Hold:     false,
		Duration: graceDelay,
	})
}

func (s *stateGrace) onTimeout(r *resetButton) state {
	return &stateCommit{}
}

func (s *stateGrace) onDown(r *resetButton) state {
	return &stateAbort{}
}

// stateAbort

//
// In this state, we provide a visual indication to the user that
// their abort request was respected. In particular, we flash white, then fade.
//
type stateAbort struct {
	baseState
}

func (s *stateAbort) onEnter(r *resetButton) {
	r.timeout.Reset(abortDelay)
	r.callback(&model.ResetMode{
		Mode:     "abort",
		Hold:     fade,
		Duration: abortDelay,
	})
}

func (s *stateAbort) onTimeout(r *resetButton) state {
	return &stateRest{}
}

// onEnter - commit the action.
type stateCommit struct {
	baseState
}

func (s *stateCommit) onEnter(r *resetButton) {
	r.commit()
}
<|endoftext|>


<commit_before>/*
 * Copyright ©1998-2022 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 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/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package about

import (
	_ "embed"
	"fmt"

	"github.com/richardwilkes/gcs/v5/setup/trampolines"
	"github.com/richardwilkes/toolbox/cmdline"
	"github.com/richardwilkes/toolbox/errs"
	"github.com/richardwilkes/toolbox/i18n"
	"github.com/richardwilkes/toolbox/log/jot"
	"github.com/richardwilkes/toolbox/xmath"
	"github.com/richardwilkes/unison"
)

var (
	//go:embed "about.png"
	aboutImageData []byte
	aboutWnd       = &aboutWindow{}
)

type aboutWindow struct {
	*unison.Window
	img *unison.Image
}

// Show the about box.
func Show(_ unison.MenuItem) {
	if aboutWnd.Window == nil {
		if err := aboutWnd.prepare(); err != nil {
			jot.Error(err)
			return
		}
	}
	aboutWnd.ToFront()
}

func (w *aboutWindow) prepare() error {
	var err error
	if w.img == nil {
		if w.img, err = unison.NewImageFromBytes(aboutImageData, 0.5); err != nil {
			return errs.NewWithCause("unable to load about image", err)
		}
	}
	if w.Window, err = unison.NewWindow(fmt.Sprintf(i18n.Text("About %s"), cmdline.AppName),
		unison.NotResizableWindowOption()); err != nil {
		return errs.NewWithCause("unable to create about window", err)
	}
	trampolines.CallMenuSetup(w.Window)
	content := w.Content()
	content.DrawCallback = w.drawContentBackground
	content.SetLayout(w)
	w.Pack()
	r := w.ContentRect()
	usable := unison.PrimaryDisplay().Usable
	r.X = usable.X + (usable.Width-r.Width)/2
	r.Y = usable.Y + (usable.Height-r.Height)/2
	r.Point.Align()
	w.SetContentRect(r)
	return nil
}

func (w *aboutWindow) LayoutSizes(_ *unison.Panel, _ unison.Size) (min, pref, max unison.Size) {
	pref = w.img.LogicalSize()
	return pref, pref, pref
}

func (w *aboutWindow) PerformLayout(target *unison.Panel) {
	target.SetFrameRect(unison.Rect{Size: w.img.LogicalSize()})
}

func (w *aboutWindow) drawContentBackground(gc *unison.Canvas, _ unison.Rect) {
	r := w.Content().ContentRect(true)
	gc.DrawImageInRect(w.img, r, nil, nil)
	gc.DrawRect(r, unison.NewEvenlySpacedGradient(unison.Point{Y: 0.25}, unison.Point{Y: 1}, 0, 0,
		unison.Transparent, unison.Black).Paint(gc, r, unison.Fill))

	face := unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.NormalFontWeight, unison.StandardSpacing, unison.NoSlant)
	boldFace := unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.BlackFontWeight, unison.StandardSpacing, unison.NoSlant)
	paint := unison.Gray.Paint(gc, unison.Rect{}, unison.Fill)
	dec := &unison.TextDecoration{
		Font:  face.Font(7),
		Paint: paint,
	}
	text := unison.NewText(i18n.Text("This product includes copyrighted material from the "), dec)
	text.AddString("GURPS", &unison.TextDecoration{
		Font:  boldFace.Font(7),
		Paint: paint,
	})
	text.AddString(i18n.Text(" game, which is used by permission of Steve Jackson Games."), dec)
	y := r.Height - 10
	text.Draw(gc, (r.Width-text.Width())/2, y)
	y -= text.Height()

	font := face.Font(8)
	dec = &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	}
	text = unison.NewText("GURPS", &unison.TextDecoration{
		Font:  boldFace.Font(8),
		Paint: paint,
	})
	text.AddString(i18n.Text(" is a trademark of Steve Jackson Games, used by permission. All rights reserved."), dec)
	text.Draw(gc, (r.Width-text.Width())/2, y)
	lineHeight := text.Height()
	y -= lineHeight * 1.5

	paint = unison.RGB(204, 204, 204).Paint(gc, unison.Rect{}, unison.Fill)
	text = unison.NewText(cmdline.Copyright(), &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	})
	text.Draw(gc, (r.Width-text.Width())/2, y)

	buildText := unison.NewText(i18n.Text("Build ")+cmdline.BuildNumber, &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	})
	var t string
	if cmdline.AppVersion != "" && cmdline.AppVersion != "0.0" {
		t = i18n.Text("Version ") + cmdline.AppVersion
	} else {
		t = i18n.Text("Development")
	}
	versionText := unison.NewText(t, &unison.TextDecoration{
		Font: unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.BlackFontWeight,
			unison.StandardSpacing, unison.NoSlant).Font(10),
		Paint: unison.White.Paint(gc, unison.Rect{}, unison.Fill),
	})

	const (
		hMargin = 8
		vMargin = 4
	)
	var backing unison.Rect
	backing.Width = xmath.Max(versionText.Width(), buildText.Width()) + hMargin*2
	backing.Height = versionText.Height() + buildText.Height() + vMargin*2
	backing.X = (r.Width - backing.Width) / 2
	backing.Y = 65
	gc.DrawRoundedRect(backing, 8, 8, unison.Black.SetAlphaIntensity(0.7).Paint(gc, backing, unison.Fill))
	p := unison.Black.Paint(gc, backing, unison.Stroke)
	p.SetStrokeWidth(2)
	gc.DrawRoundedRect(backing, 8, 8, p)

	versionText.Draw(gc, (r.Width-versionText.Width())/2, backing.Y+vMargin+versionText.Baseline())
	buildText.Draw(gc, (r.Width-buildText.Width())/2, backing.Y+vMargin+versionText.Height()+buildText.Baseline())
}
<commit_msg>Fix about box layout on Linux<commit_after>/*
 * Copyright ©1998-2022 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 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/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package about

import (
	_ "embed"
	"fmt"

	"github.com/richardwilkes/gcs/v5/setup/trampolines"
	"github.com/richardwilkes/toolbox/cmdline"
	"github.com/richardwilkes/toolbox/errs"
	"github.com/richardwilkes/toolbox/i18n"
	"github.com/richardwilkes/toolbox/log/jot"
	"github.com/richardwilkes/toolbox/xmath"
	"github.com/richardwilkes/unison"
)

var (
	//go:embed "about.png"
	aboutImageData []byte
	aboutWnd       = &aboutWindow{}
)

type aboutWindow struct {
	*unison.Window
	img *unison.Image
}

// Show the about box.
func Show(_ unison.MenuItem) {
	if aboutWnd.Window == nil {
		if err := aboutWnd.prepare(); err != nil {
			jot.Error(err)
			return
		}
	}
	aboutWnd.ToFront()
}

func (w *aboutWindow) prepare() error {
	var err error
	if w.img == nil {
		if w.img, err = unison.NewImageFromBytes(aboutImageData, 0.5); err != nil {
			return errs.NewWithCause("unable to load about image", err)
		}
	}
	if w.Window, err = unison.NewWindow(fmt.Sprintf(i18n.Text("About %s"), cmdline.AppName),
		unison.NotResizableWindowOption()); err != nil {
		return errs.NewWithCause("unable to create about window", err)
	}
	trampolines.CallMenuSetup(w.Window)
	content := w.Content()
	content.SetSizer(func(hint unison.Size) (min, pref, max unison.Size) {
		pref = w.img.LogicalSize()
		return pref, pref, pref
	})
	content.SetLayout(nil)
	content.DrawCallback = w.drawContentBackground
	w.Pack()
	r := w.ContentRect()
	usable := unison.PrimaryDisplay().Usable
	r.X = usable.X + (usable.Width-r.Width)/2
	r.Y = usable.Y + (usable.Height-r.Height)/2
	r.Point.Align()
	w.SetContentRect(r)
	return nil
}

func (w *aboutWindow) drawContentBackground(gc *unison.Canvas, _ unison.Rect) {
	r := w.Content().ContentRect(true)
	gc.DrawImageInRect(w.img, r, nil, nil)
	gc.DrawRect(r, unison.NewEvenlySpacedGradient(unison.Point{Y: 0.25}, unison.Point{Y: 1}, 0, 0,
		unison.Transparent, unison.Black).Paint(gc, r, unison.Fill))

	face := unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.NormalFontWeight, unison.StandardSpacing, unison.NoSlant)
	boldFace := unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.BlackFontWeight, unison.StandardSpacing, unison.NoSlant)
	paint := unison.Gray.Paint(gc, unison.Rect{}, unison.Fill)
	dec := &unison.TextDecoration{
		Font:  face.Font(7),
		Paint: paint,
	}
	text := unison.NewText(i18n.Text("This product includes copyrighted material from the "), dec)
	text.AddString("GURPS", &unison.TextDecoration{
		Font:  boldFace.Font(7),
		Paint: paint,
	})
	text.AddString(i18n.Text(" game, which is used by permission of Steve Jackson Games."), dec)
	y := r.Height - 10
	text.Draw(gc, (r.Width-text.Width())/2, y)
	y -= text.Height()

	font := face.Font(8)
	dec = &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	}
	text = unison.NewText("GURPS", &unison.TextDecoration{
		Font:  boldFace.Font(8),
		Paint: paint,
	})
	text.AddString(i18n.Text(" is a trademark of Steve Jackson Games, used by permission. All rights reserved."), dec)
	text.Draw(gc, (r.Width-text.Width())/2, y)
	lineHeight := text.Height()
	y -= lineHeight * 1.5

	paint = unison.RGB(204, 204, 204).Paint(gc, unison.Rect{}, unison.Fill)
	text = unison.NewText(cmdline.Copyright(), &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	})
	text.Draw(gc, (r.Width-text.Width())/2, y)

	buildText := unison.NewText(i18n.Text("Build ")+cmdline.BuildNumber, &unison.TextDecoration{
		Font:  font,
		Paint: paint,
	})
	var t string
	if cmdline.AppVersion != "" && cmdline.AppVersion != "0.0" {
		t = i18n.Text("Version ") + cmdline.AppVersion
	} else {
		t = i18n.Text("Development")
	}
	versionText := unison.NewText(t, &unison.TextDecoration{
		Font: unison.MatchFontFace(unison.DefaultSystemFamilyName, unison.BlackFontWeight,
			unison.StandardSpacing, unison.NoSlant).Font(10),
		Paint: unison.White.Paint(gc, unison.Rect{}, unison.Fill),
	})

	const (
		hMargin = 8
		vMargin = 4
	)
	var backing unison.Rect
	backing.Width = xmath.Max(versionText.Width(), buildText.Width()) + hMargin*2
	backing.Height = versionText.Height() + buildText.Height() + vMargin*2
	backing.X = (r.Width - backing.Width) / 2
	backing.Y = 65
	gc.DrawRoundedRect(backing, 8, 8, unison.Black.SetAlphaIntensity(0.7).Paint(gc, backing, unison.Fill))
	p := unison.Black.Paint(gc, backing, unison.Stroke)
	p.SetStrokeWidth(2)
	gc.DrawRoundedRect(backing, 8, 8, p)

	versionText.Draw(gc, (r.Width-versionText.Width())/2, backing.Y+vMargin+versionText.Baseline())
	buildText.Draw(gc, (r.Width-buildText.Width())/2, backing.Y+vMargin+versionText.Height()+buildText.Baseline())
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"
)

var url = flag.String("url", "http://107.178.208.130:80/burn", "Url to serve traffic to.")
var initQps = flag.Int("init_qps", 1, "Number of queries per second to initially send.")

var nsInSecond = int64(1000 * 1000 * 1000)
var nanoDelay = nsInSecond
var uptime = "Initial Uptime (not updated.)"
var latency = "None."

func sendQueries() {
	for true {
		time.Sleep(time.Duration(nanoDelay))
		go sendOneQuery()
	}
}

func sendOneQuery() {
	sendTime := time.Now()
	resp, err := http.Get(*url)
	latency = time.Since(sendTime).String()
	if err != nil {
		log.Printf("%v", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("%v", err)
		return
	}
	uptime = string(body)
	log.Printf("New Uptime: %s", uptime)
}

type queryInfo struct {
	Uptime  string `json:"uptime"`
	Latency string `json:"latency"`
}

func RegisterServiceHandlers() {
	nanoDelay = nsInSecond / int64(*initQps)
	http.HandleFunc("/api/qps", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		qps, err := strconv.Atoi(r.FormValue("qps"))
		if err != nil {
			log.Printf("%v", err)
			return
		}
		nanoDelay = nsInSecond / int64(qps)
		log.Printf("Request(/api/qps) took %s", time.Since(start))
	})
	http.HandleFunc("/api/uptime", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		encoder := json.NewEncoder(w)
		err := encoder.Encode(&queryInfo{
			Uptime:  uptime,
			Latency: latency,
		})
		// fmt.Fprintf(w, "{\"uptime\":%q \"latency\":%q}", uptime, latency)
		if err != nil {
			log.Printf("unalbe to marshal json: %v", err)
		}
		log.Printf("Request(/api/uptime) took %s", time.Since(start))
	})
	go sendQueries()
}
<commit_msg>Update ui server to accept 0 qps.<commit_after>package api

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"
)

var url = flag.String("url", "http://107.178.208.130:80/burn", "Url to serve traffic to.")
var initQps = flag.Int("init_qps", 1, "Number of queries per second to initially send.")

var nsInSecond = int64(1000 * 1000 * 1000)
var nanoDelay = nsInSecond
var uptime = "Initial Uptime (not updated.)"
var latency = "None."

func sendQueries() {
	for true {
		for nanoDelay == 0 {
			time.Sleep(nsInSecond)
		}
		time.Sleep(time.Duration(nanoDelay))
		go sendOneQuery()
	}
}

func sendOneQuery() {
	sendTime := time.Now()
	resp, err := http.Get(*url)
	latency = time.Since(sendTime).String()
	if err != nil {
		log.Printf("%v", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("%v", err)
		return
	}
	uptime = string(body)
	log.Printf("New Uptime: %s", uptime)
}

type queryInfo struct {
	Uptime  string `json:"uptime"`
	Latency string `json:"latency"`
}

func RegisterServiceHandlers() {
	nanoDelay = nsInSecond / int64(*initQps)
	http.HandleFunc("/api/qps", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		qps, err := strconv.Atoi(r.FormValue("qps"))
		if err != nil {
			log.Printf("%v", err)
			return
		}
		if (qps == 0) {
			nanoDelay = 0
		} else {
			nanoDelay = nsInSecond / int64(qps)
		}
		log.Printf("Request(/api/qps) took %s", time.Since(start))
	})
	http.HandleFunc("/api/uptime", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		encoder := json.NewEncoder(w)
		err := encoder.Encode(&queryInfo{
			Uptime:  uptime,
			Latency: latency,
		})
		// fmt.Fprintf(w, "{\"uptime\":%q \"latency\":%q}", uptime, latency)
		if err != nil {
			log.Printf("unalbe to marshal json: %v", err)
		}
		log.Printf("Request(/api/uptime) took %s", time.Since(start))
	})
	go sendQueries()
}
<|endoftext|>


<commit_before>package web

// TODO: Create package 'oauth'

import (
	"net/http"
	"os"
	"time"

	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/util"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	google_auth2 "google.golang.org/api/oauth2/v2"
)

var googleOAuthConfig = &oauth2.Config{
	ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
	ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
	Endpoint:     google.Endpoint,
	RedirectURL:  "http://localhost:4000/oauth/google/callback",
	Scopes: []string{
		"openid email",
		"openid profile",
	},
}

func OAuthGoogle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	state := util.RandomString(32)
	cookie := &http.Cookie{
		Name:     "oauthState",
		Value:    state,
		Path:     "/",
		Expires:  time.Now().Add(time.Minute * 30),
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, googleOAuthConfig.AuthCodeURL(state), http.StatusFound)
}

func OAuthGoogleCallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if err := checkState(r); err != nil {
		InternalServerError(w, err)
		return
	}
	token, idToken, err := exchange(r)
	if err != nil {
		InternalServerError(w, err)
		return
	}
	name, email, err := getNameAndEmail(token, idToken)
	if err != nil {
		InternalServerError(w, err)
		return
	}

	db := model.MustDb(ctx)
	user := model.User{Name: name, Email: email}
	if err := db.FirstOrCreate(&user, model.User{Email: email}).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to get or create User"))
		return
	}

	// Create and save API Token
	apiToken := util.RandomString(64)
	userApiToken := model.UserApiToken{
		UserId: user.Id,
		Token:  apiToken,
	}
	if err := db.Create(&userApiToken).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to create UserApiToken"))
		return
	}
	cookie := &http.Cookie{
		Name:     ApiTokenCookieName,
		Value:    apiToken,
		Path:     "/",
		Expires:  time.Now().Add(time.Hour * 24 * 30),
		HttpOnly: false,
	}
	http.SetCookie(w, cookie)

	//data := map[string]interface{}{
	//	"id":          user.Id,
	//	"name":        user.Name,
	//	"email":       user.Email,
	//	"accessToken": token.AccessToken,
	//	"idToken":     idToken,
	//}
	//if err := json.NewEncoder(w).Encode(data); err != nil {
	//	InternalServerError(w, errors.Errorf("Failed to encode JSON"))
	//	return
	//}

	http.Redirect(w, r, "/", http.StatusFound)
}

func checkState(r *http.Request) error {
	state := r.FormValue("state")
	oauthState, err := r.Cookie("oauthState")
	if err != nil {
		return errors.Wrap(err, "Failed to get cookie oauthState")
	}
	if state != oauthState.Value {
		return errors.Wrap(err, "state mismatch")
	}
	return nil
}

func exchange(r *http.Request) (*oauth2.Token, string, error) {
	code := r.FormValue("code")
	token, err := googleOAuthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		return nil, "", errors.Wrap(err, "Failed to exchange")
	}
	idToken, ok := token.Extra("id_token").(string)
	if !ok {
		return nil, "", errors.Errorf("Failed to get id_token")
	}
	return token, idToken, nil
}

func getNameAndEmail(token *oauth2.Token, idToken string) (string, string, error) {
	oauth2Client := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(token))
	service, err := google_auth2.New(oauth2Client)
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to create oauth2.Client")
	}

	userinfo, err := service.Userinfo.V2.Me.Get().Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get userinfo")
	}

	tokeninfo, err := service.Tokeninfo().IdToken(idToken).Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get tokeninfo")
	}

	return userinfo.Name, tokeninfo.Email, nil
}
<commit_msg>Dynamic OAuth callback URL<commit_after>package web

// TODO: Create package 'oauth'

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/util"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	google_auth2 "google.golang.org/api/oauth2/v2"
)

var _ = fmt.Print
var googleOAuthConfig = oauth2.Config{
	ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
	ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
	Endpoint:     google.Endpoint,
	RedirectURL:  "",
	Scopes: []string{
		"openid email",
		"openid profile",
	},
}

func OAuthGoogle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	state := util.RandomString(32)
	cookie := &http.Cookie{
		Name:     "oauthState",
		Value:    state,
		Path:     "/",
		Expires:  time.Now().Add(time.Minute * 30),
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)
	c := getGoogleOAuthConfig(fmt.Sprintf("http://%s/oauth/google/callback", r.Host)) // TODO: scheme
	http.Redirect(w, r, c.AuthCodeURL(state), http.StatusFound)
}

func OAuthGoogleCallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if err := checkState(r); err != nil {
		InternalServerError(w, err)
		return
	}
	token, idToken, err := exchange(r)
	if err != nil {
		InternalServerError(w, err)
		return
	}
	name, email, err := getNameAndEmail(token, idToken)
	if err != nil {
		InternalServerError(w, err)
		return
	}

	db := model.MustDb(ctx)
	user := model.User{Name: name, Email: email}
	if err := db.FirstOrCreate(&user, model.User{Email: email}).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to get or create User"))
		return
	}

	// Create and save API Token
	apiToken := util.RandomString(64)
	userApiToken := model.UserApiToken{
		UserId: user.Id,
		Token:  apiToken,
	}
	if err := db.Create(&userApiToken).Error; err != nil {
		InternalServerError(w, errors.Wrap(err, "Failed to create UserApiToken"))
		return
	}
	cookie := &http.Cookie{
		Name:     ApiTokenCookieName,
		Value:    apiToken,
		Path:     "/",
		Expires:  time.Now().Add(time.Hour * 24 * 30),
		HttpOnly: false,
	}
	http.SetCookie(w, cookie)

	//data := map[string]interface{}{
	//	"id":          user.Id,
	//	"name":        user.Name,
	//	"email":       user.Email,
	//	"accessToken": token.AccessToken,
	//	"idToken":     idToken,
	//}
	//if err := json.NewEncoder(w).Encode(data); err != nil {
	//	InternalServerError(w, errors.Errorf("Failed to encode JSON"))
	//	return
	//}

	http.Redirect(w, r, "/", http.StatusFound)
}

func checkState(r *http.Request) error {
	state := r.FormValue("state")
	oauthState, err := r.Cookie("oauthState")
	if err != nil {
		return errors.Wrap(err, "Failed to get cookie oauthState")
	}
	if state != oauthState.Value {
		return errors.Wrap(err, "state mismatch")
	}
	return nil
}

func exchange(r *http.Request) (*oauth2.Token, string, error) {
	code := r.FormValue("code")
	c := getGoogleOAuthConfig(fmt.Sprintf("http://%s/oauth/google/callback", r.Host)) // TODO: scheme
	token, err := c.Exchange(oauth2.NoContext, code)
	if err != nil {
		return nil, "", errors.Wrap(err, "Failed to exchange")
	}
	idToken, ok := token.Extra("id_token").(string)
	if !ok {
		return nil, "", errors.Errorf("Failed to get id_token")
	}
	return token, idToken, nil
}

func getNameAndEmail(token *oauth2.Token, idToken string) (string, string, error) {
	oauth2Client := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(token))
	service, err := google_auth2.New(oauth2Client)
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to create oauth2.Client")
	}

	userinfo, err := service.Userinfo.V2.Me.Get().Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get userinfo")
	}

	tokeninfo, err := service.Tokeninfo().IdToken(idToken).Do()
	if err != nil {
		return "", "", errors.Wrap(err, "Failed to get tokeninfo")
	}

	return userinfo.Name, tokeninfo.Email, nil
}

func getGoogleOAuthConfig(redirectUrl string) oauth2.Config {
	c := googleOAuthConfig
	c.RedirectURL = redirectUrl
	return c
}
<|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 gcetasks

import (
	"fmt"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/klog/v2"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/gce"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter"
)

// TargetPool represents a GCE TargetPool
// +kops:fitask
type TargetPool struct {
	Name      *string
	Lifecycle fi.Lifecycle
}

var _ fi.CompareWithID = &TargetPool{}

func (e *TargetPool) CompareWithID() *string {
	return e.Name
}

func (e *TargetPool) Find(c *fi.Context) (*TargetPool, error) {
	cloud := c.Cloud.(gce.GCECloud)
	name := fi.StringValue(e.Name)

	r, err := cloud.Compute().TargetPools().Get(cloud.Project(), cloud.Region(), name)
	if err != nil {
		if gce.IsNotFound(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error getting TargetPool %q: %v", name, err)
	}

	actual := &TargetPool{}
	actual.Name = fi.String(r.Name)

	return actual, nil
}

func (e *TargetPool) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (_ *TargetPool) CheckChanges(a, e, changes *TargetPool) error {
	if fi.StringValue(e.Name) == "" {
		return fi.RequiredField("Name")
	}
	return nil
}

func (e *TargetPool) URL(cloud gce.GCECloud) string {
	name := fi.StringValue(e.Name)

	return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/regions/%s/targetPools/%s", cloud.Project(), cloud.Region(), name)
}

func (_ *TargetPool) RenderGCE(t *gce.GCEAPITarget, a, e, changes *TargetPool) error {
	name := fi.StringValue(e.Name)

	o := &compute.TargetPool{
		Name: name,
	}

	if a == nil {
		klog.V(4).Infof("Creating TargetPool %q", o.Name)

		op, err := t.Cloud.Compute().TargetPools().Insert(t.Cloud.Project(), t.Cloud.Region(), o)
		if err != nil {
			return fmt.Errorf("error creating TargetPool %q: %v", name, err)
		}

		if err := t.Cloud.WaitForOp(op); err != nil {
			return fmt.Errorf("error creating TargetPool: %v", err)
		}
	} else {
		return fmt.Errorf("cannot apply changes to TargetPool: %v", changes)
	}

	return nil
}

type terraformTargetPool struct {
	Name            string   `json:"name" cty:"name"`
	Description     string   `json:"description,omitempty" cty:"description"`
	HealthChecks    []string `json:"health_checks,omitempty" cty:"health_checks"`
	Instances       []string `json:"instances,omitempty" cty:"instances"`
	SessionAffinity string   `json:"session_affinity,omitempty" cty:"session_affinity"`
}

func (_ *TargetPool) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *TargetPool) error {
	name := fi.StringValue(e.Name)

	tf := &terraformTargetPool{
		Name: name,
	}

	return t.RenderResource("google_compute_target_pool", name, tf)
}

func (e *TargetPool) TerraformLink() *terraformWriter.Literal {
	name := fi.StringValue(e.Name)

	return terraformWriter.LiteralSelfLink("google_compute_target_pool", name)
}
<commit_msg>GCE: TargetPool should ignore Lifecycle field<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 gcetasks

import (
	"fmt"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/klog/v2"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/gce"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter"
)

// TargetPool represents a GCE TargetPool
// +kops:fitask
type TargetPool struct {
	Name      *string
	Lifecycle fi.Lifecycle
}

var _ fi.CompareWithID = &TargetPool{}

func (e *TargetPool) CompareWithID() *string {
	return e.Name
}

func (e *TargetPool) Find(c *fi.Context) (*TargetPool, error) {
	cloud := c.Cloud.(gce.GCECloud)
	name := fi.StringValue(e.Name)

	r, err := cloud.Compute().TargetPools().Get(cloud.Project(), cloud.Region(), name)
	if err != nil {
		if gce.IsNotFound(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error getting TargetPool %q: %v", name, err)
	}

	actual := &TargetPool{}
	actual.Name = fi.String(r.Name)

	// Avoid spurious changes
	actual.Lifecycle = e.Lifecycle

	return actual, nil
}

func (e *TargetPool) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (_ *TargetPool) CheckChanges(a, e, changes *TargetPool) error {
	if fi.StringValue(e.Name) == "" {
		return fi.RequiredField("Name")
	}
	return nil
}

func (e *TargetPool) URL(cloud gce.GCECloud) string {
	name := fi.StringValue(e.Name)

	return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/regions/%s/targetPools/%s", cloud.Project(), cloud.Region(), name)
}

func (_ *TargetPool) RenderGCE(t *gce.GCEAPITarget, a, e, changes *TargetPool) error {
	name := fi.StringValue(e.Name)

	o := &compute.TargetPool{
		Name: name,
	}

	if a == nil {
		klog.V(4).Infof("Creating TargetPool %q", o.Name)

		op, err := t.Cloud.Compute().TargetPools().Insert(t.Cloud.Project(), t.Cloud.Region(), o)
		if err != nil {
			return fmt.Errorf("error creating TargetPool %q: %v", name, err)
		}

		if err := t.Cloud.WaitForOp(op); err != nil {
			return fmt.Errorf("error creating TargetPool: %v", err)
		}
	} else {
		return fmt.Errorf("cannot apply changes to TargetPool: %v", changes)
	}

	return nil
}

type terraformTargetPool struct {
	Name            string   `json:"name" cty:"name"`
	Description     string   `json:"description,omitempty" cty:"description"`
	HealthChecks    []string `json:"health_checks,omitempty" cty:"health_checks"`
	Instances       []string `json:"instances,omitempty" cty:"instances"`
	SessionAffinity string   `json:"session_affinity,omitempty" cty:"session_affinity"`
}

func (_ *TargetPool) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *TargetPool) error {
	name := fi.StringValue(e.Name)

	tf := &terraformTargetPool{
		Name: name,
	}

	return t.RenderResource("google_compute_target_pool", name, tf)
}

func (e *TargetPool) TerraformLink() *terraformWriter.Literal {
	name := fi.StringValue(e.Name)

	return terraformWriter.LiteralSelfLink("google_compute_target_pool", name)
}
<|endoftext|>


<commit_before>package configr_test

import (
	"io/ioutil"
	"os"
	"testing"

	. "github.com/adrianduke/configr"

	"github.com/stretchr/testify/assert"
)

func writeTempFile(t *testing.T, filePath string, content string) {
	err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm)
	assert.NoError(t, err)
}

func Test_ItParsesAllValuesFromJSONConfig(t *testing.T) {
	filePath := "/tmp/test.json"
	writeTempFile(t, filePath, `{
	"t1": "1",
	"t2": {
		"t21": 2,
		"t22": {
			"t221": true
		}
	}
}`)
	defer os.Remove(filePath)
	f := NewFileSource(filePath)

	config := New()
	config.AddSource(f)
	config.RequireKey("t1", "")
	config.RequireKey("t2.t21", "")
	config.RequireKey("t2.t22.t221", "")
	config.RegisterKey("t3", "", 3)

	assert.NoError(t, config.Parse())

	t1, err := config.String("t1")
	assert.NoError(t, err)
	t2t21, err := config.Float64("t2.t21")
	assert.NoError(t, err)
	t2t22t221, err := config.Bool("t2.t22.t221")
	assert.NoError(t, err)
	t3, err := config.Int("t3")
	assert.NoError(t, err)

	assert.Equal(t, "1", t1)
	assert.Equal(t, float64(2), t2t21)
	assert.Equal(t, true, t2t22t221)
	assert.Equal(t, 3, t3)
}

func Test_ItParsesAllValuesFromTOMLConfig(t *testing.T) {
	filePath := "/tmp/test.toml"
	writeTempFile(t, filePath, `
[t1]
t11 = "1"
t12 = 2

[t2]
	[t2.t21]
	t211 = false
		[t2.t21.t212]
		t2121 = "4"
`)
	defer os.Remove(filePath)
	f := NewFileSource(filePath)

	config := New()
	config.AddSource(f)
	config.RequireKey("t1.t11", "")
	config.RequireKey("t1.t12", "")
	config.RequireKey("t2.t21.t211", "")
	config.RequireKey("t2.t21.t212.t2121", "")
	config.RegisterKey("t3", "", "sup")

	assert.NoError(t, config.Parse())

	t1t11, err := config.String("t1.t11")
	assert.NoError(t, err)
	t1t12, err := config.Int("t1.t12")
	assert.NoError(t, err)
	t2t21t211, err := config.Bool("t2.t21.t211")
	assert.NoError(t, err)
	t2t21t212t2121, err := config.Get("t2.t21.t212.t2121")
	assert.NoError(t, err)
	t3, err := config.String("t3")
	assert.NoError(t, err)

	assert.Equal(t, "1", t1t11)
	assert.Equal(t, 2, t1t12)
	assert.Equal(t, false, t2t21t211)
	assert.Equal(t, "4", t2t21t212t2121.(string))
	assert.Equal(t, "sup", t3)
}

func Test_ItGeneratesBlankJSONConfig(t *testing.T) {
	config := New()
	expectedOutput := `{
	"t1": {
		"t11": "*** You need this ***",
		"t12": "*** Me too ***"
	},
	"t2": {
		"t21": {
			"t211": "*** And me ***",
			"t212": {
				"t2121": "*** Also me! ***"
			}
		}
	},
	"t3": 0
}`
	config.RequireKey("t1.t11", "You need this")
	config.RequireKey("t1.t12", "Me too")
	config.RequireKey("t2.t21.t211", "And me")
	config.RequireKey("t2.t21.t212.t2121", "Also me!")
	config.RegisterKey("t3", "", 0)

	f := NewFileSource("")
	f.SetEncoding(JSON)

	configBytes, err := config.GenerateBlank(f)
	assert.NoError(t, err)

	assert.Equal(t, expectedOutput, string(configBytes))
}

func Test_ItGeneratesBlankTOMLConfig(t *testing.T) {
	config := New()
	expectedOutput := `t3 = 0

[t1]
  t11 = "*** You need this ***"
  t12 = "*** Me too ***"

[t2]
  [t2.t21]
    t211 = "*** And me ***"
    [t2.t21.t212]
      t2121 = "*** Also me! ***"
`
	config.RequireKey("t1.t11", "You need this")
	config.RequireKey("t1.t12", "Me too")
	config.RequireKey("t2.t21.t211", "And me")
	config.RequireKey("t2.t21.t212.t2121", "Also me!")
	config.RegisterKey("t3", "", 0)

	f := NewFileSource("")
	f.SetEncoding(TOML)

	configBytes, err := config.GenerateBlank(f)
	assert.NoError(t, err)

	assert.Equal(t, expectedOutput, string(configBytes))
}
<commit_msg>Updated feature tests<commit_after>package configr_test

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/adrianduke/configr"
	"github.com/adrianduke/configr/sources/file/json"
	"github.com/adrianduke/configr/sources/file/toml"
	"github.com/stretchr/testify/assert"
)

func writeTempFile(t *testing.T, filePath string, content string) {
	err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm)
	assert.NoError(t, err)
}

func Test_ItParsesAllValuesFromJSONConfig(t *testing.T) {
	// Not required outside of this package
	json.Register()

	filePath := "/tmp/test.json"
	writeTempFile(t, filePath, `{
	"t1": "1",
	"t2": {
		"t21": 2,
		"t22": {
			"t221": true
		}
	}
}`)
	defer os.Remove(filePath)
	f := configr.NewFile(filePath)

	config := configr.New()
	config.AddSource(f)
	config.RequireKey("t1", "")
	config.RequireKey("t2.t21", "")
	config.RequireKey("t2.t22.t221", "")
	config.RegisterKey("t3", "", 3)

	assert.NoError(t, config.Parse())

	t1, err := config.String("t1")
	assert.NoError(t, err)
	t2t21, err := config.Float64("t2.t21")
	assert.NoError(t, err)
	t2t22t221, err := config.Bool("t2.t22.t221")
	assert.NoError(t, err)
	t3, err := config.Int("t3")
	assert.NoError(t, err)

	assert.Equal(t, "1", t1)
	assert.Equal(t, float64(2), t2t21)
	assert.Equal(t, true, t2t22t221)
	assert.Equal(t, 3, t3)
}

func Test_ItParsesAllValuesFromTOMLConfig(t *testing.T) {
	// Not required outside of this package
	toml.Register()

	filePath := "/tmp/test.toml"
	writeTempFile(t, filePath, `
[t1]
t11 = "1"
t12 = 2

[t2]
	[t2.t21]
	t211 = false
		[t2.t21.t212]
		t2121 = "4"
`)
	defer os.Remove(filePath)
	f := configr.NewFile(filePath)

	config := configr.New()
	config.AddSource(f)
	config.RequireKey("t1.t11", "")
	config.RequireKey("t1.t12", "")
	config.RequireKey("t2.t21.t211", "")
	config.RequireKey("t2.t21.t212.t2121", "")
	config.RegisterKey("t3", "", "sup")

	assert.NoError(t, config.Parse())

	t1t11, err := config.String("t1.t11")
	assert.NoError(t, err)
	t1t12, err := config.Int("t1.t12")
	assert.NoError(t, err)
	t2t21t211, err := config.Bool("t2.t21.t211")
	assert.NoError(t, err)
	t2t21t212t2121, err := config.Get("t2.t21.t212.t2121")
	assert.NoError(t, err)
	t3, err := config.String("t3")
	assert.NoError(t, err)

	assert.Equal(t, "1", t1t11)
	assert.Equal(t, 2, t1t12)
	assert.Equal(t, false, t2t21t211)
	assert.Equal(t, "4", t2t21t212t2121.(string))
	assert.Equal(t, "sup", t3)
}

func Test_ItGeneratesBlankJSONConfig(t *testing.T) {
	// Not required outside of this package
	json.Register()

	config := configr.New()
	expectedOutput := `{
	"t1": {
		"t11": "*** You need this ***",
		"t12": "*** Me too ***"
	},
	"t2": {
		"t21": {
			"t211": "*** And me ***",
			"t212": {
				"t2121": "*** Also me! ***"
			}
		}
	},
	"t3": 0
}`
	config.RequireKey("t1.t11", "You need this")
	config.RequireKey("t1.t12", "Me too")
	config.RequireKey("t2.t21.t211", "And me")
	config.RequireKey("t2.t21.t212.t2121", "Also me!")
	config.RegisterKey("t3", "", 0)

	f := configr.NewFile("")
	f.SetEncodingName("json")

	configBytes, err := config.GenerateBlank(f)
	assert.NoError(t, err)

	assert.Equal(t, expectedOutput, string(configBytes))
}

func Test_ItGeneratesBlankTOMLConfig(t *testing.T) {
	// Not required outside of this package
	toml.Register()

	config := configr.New()
	expectedOutput := `t3 = 0

[t1]
  t11 = "*** You need this ***"
  t12 = "*** Me too ***"

[t2]
  [t2.t21]
    t211 = "*** And me ***"
    [t2.t21.t212]
      t2121 = "*** Also me! ***"
`
	config.RequireKey("t1.t11", "You need this")
	config.RequireKey("t1.t12", "Me too")
	config.RequireKey("t2.t21.t211", "And me")
	config.RequireKey("t2.t21.t212.t2121", "Also me!")
	config.RegisterKey("t3", "", 0)

	f := configr.NewFile("")
	f.SetEncodingName("toml")

	configBytes, err := config.GenerateBlank(f)
	assert.NoError(t, err)

	assert.Equal(t, expectedOutput, string(configBytes))
}
<|endoftext|>