<commit_before>package db_test

import (
	"context"
	"fmt"
	"time"

	"code.cloudfoundry.org/clock/fakeclock"
	"github.com/concourse/concourse/atc/db"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"golang.org/x/time/rate"
)

var _ = Describe("ResourceCheckRateLimiter", func() {
	var (
		checkInterval   time.Duration
		checksPerSecond int
		refreshInterval time.Duration
		fakeClock       *fakeclock.FakeClock

		checkableCount int

		ctx context.Context

		limiter *db.ResourceCheckRateLimiter
	)

	BeforeEach(func() {
		checkInterval = time.Minute
		checksPerSecond = 0
		refreshInterval = 5 * time.Minute
		fakeClock = fakeclock.NewFakeClock(time.Now())

		_, err := psql.Delete("resources").
			RunWith(dbConn).
			Exec()
		Expect(err).ToNot(HaveOccurred())

		checkableCount = 0

		ctx = context.Background()
	})

	JustBeforeEach(func() {
		limiter = db.NewResourceCheckRateLimiter(
			rate.Limit(checksPerSecond),
			checkInterval,
			dbConn,
			refreshInterval,
			fakeClock,
		)
	})

	wait := func(limiter *db.ResourceCheckRateLimiter) <-chan error {
		errs := make(chan error)
		go func() {
			errs <- limiter.Wait(ctx)
		}()
		return errs
	}

	createCheckable := func() {
		es := dbConn.EncryptionStrategy()
		encryptedPayload, nonce, err := es.Encrypt([]byte("{}"))
		Expect(err).ToNot(HaveOccurred())

		_, err = psql.Insert("resources").
			Columns("name", "pipeline_id", "config", "active", "nonce", "type").
			Values(fmt.Sprintf("resource-%d", checkableCount), defaultPipeline.ID(), encryptedPayload, true, nonce, defaultWorkerResourceType.Type).
			RunWith(dbConn).
			Exec()
		Expect(err).ToNot(HaveOccurred())

		checkableCount++
	}

	Context("with no static limit provided", func() {
		BeforeEach(func() {
			checksPerSecond = 0
		})

		It("rate limits while adjusting to the amount of checkables", func() {
			By("immediately returning with 0 checkables")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Inf))

			By("creating one checkable")
			createCheckable()

			By("continuing to return immediately, as the refresh interval has not elapsed")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Inf))

			By("waiting for the refresh interval")
			fakeClock.Increment(refreshInterval)

			By("adjusting the limit but returning immediately for the first time")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds())))

			done := wait(limiter)
			select {
			case <-done:
				Fail("should not have returned yet")
			case <-time.After(100 * time.Millisecond):
			}

			By("unblocking after the rate limit elapses")
			fakeClock.Increment(checkInterval / time.Duration(checkableCount))
			Expect(<-done).To(Succeed())

			By("creating more checkables")
			for i := 0; i < 10; i++ {
				createCheckable()
			}

			By("waiting for the refresh interval")
			fakeClock.Increment(refreshInterval)

			By("adjusting the limit but returning immediately for the first time")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds())))

			done = wait(limiter)
			select {
			case <-done:
				Fail("should not have returned yet")
			case <-time.After(100 * time.Millisecond):
			}

			By("unblocking after the the new rate limit elapses")
			fakeClock.Increment(checkInterval / time.Duration(checkableCount))
			Expect(<-done).To(Succeed())
		})
	})

	Context("when a static checks per second value is provided", func() {
		BeforeEach(func() {
			checksPerSecond = 42
		})

		It("respects the value rather than determining it dynamically", func() {
			Expect(limiter.Limit()).To(Equal(rate.Limit(checksPerSecond)))
		})
	})

	Context("when a negative static checks per second value is provided", func() {
		BeforeEach(func() {
			checksPerSecond = -1
		})

		It("results in an infinite rate limit that ignores checkable count", func() {
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf)))

			By("creating a few (ignored) checkables")
			for i := 0; i < 10; i++ {
				createCheckable()
			}

			By("waiting for the (ignored) refresh interval")
			fakeClock.Increment(refreshInterval)

			By("still returning immediately and retaining the infinite rate")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf)))
		})
	})
})
<commit_msg>revise tests.<commit_after>package db_test

import (
	"context"
	"fmt"
	"time"

	"code.cloudfoundry.org/clock/fakeclock"
	"golang.org/x/time/rate"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/dbtest"
)

var _ = Describe("ResourceCheckRateLimiter", func() {
	var (
		checkInterval   time.Duration
		checksPerSecond int
		refreshInterval time.Duration
		fakeClock       *fakeclock.FakeClock

		scenario       *dbtest.Scenario
		checkableCount int

		ctx context.Context

		limiter *db.ResourceCheckRateLimiter
	)

	BeforeEach(func() {
		checkInterval = time.Minute
		checksPerSecond = 0
		refreshInterval = 5 * time.Minute
		fakeClock = fakeclock.NewFakeClock(time.Now())

		_, err := dbConn.Exec("update resources set active=false")
		Expect(err).ToNot(HaveOccurred())

		checkableCount = 0
		scenario = &dbtest.Scenario{}

		ctx = context.Background()
	})

	JustBeforeEach(func() {
		limiter = db.NewResourceCheckRateLimiter(
			rate.Limit(checksPerSecond),
			checkInterval,
			dbConn,
			refreshInterval,
			fakeClock,
		)
	})

	wait := func(limiter *db.ResourceCheckRateLimiter) <-chan error {
		errs := make(chan error)
		go func() {
			errs <- limiter.Wait(ctx)
		}()
		return errs
	}

	createCheckable := func() {
		checkableCount++

		var resources atc.ResourceConfigs
		for i := 0; i < checkableCount; i++ {
			resources = append(resources, atc.ResourceConfig{
				Name:   fmt.Sprintf("resource-%d", i),
				Type:   dbtest.BaseResourceType,
				Source: atc.Source{"some": "source"},
			})
		}

		scenario.Run(builder.WithPipeline(atc.Config{
			Resources: resources,
		}))
	}

	Context("with no static limit provided", func() {
		BeforeEach(func() {
			checksPerSecond = 0
		})

		It("rate limits while adjusting to the amount of checkables", func() {
			By("immediately returning with 0 checkables")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Inf))

			By("creating one checkable")
			createCheckable()

			By("continuing to return immediately, as the refresh interval has not elapsed")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Inf))

			By("waiting for the refresh interval")
			fakeClock.Increment(refreshInterval)

			By("adjusting the limit but returning immediately for the first time")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds())))

			done := wait(limiter)
			select {
			case <-done:
				Fail("should not have returned yet")
			case <-time.After(100 * time.Millisecond):
			}

			By("unblocking after the rate limit elapses")
			fakeClock.Increment(checkInterval / time.Duration(checkableCount))
			Expect(<-done).To(Succeed())

			By("creating more checkables")
			for i := 0; i < 10; i++ {
				createCheckable()
			}

			By("waiting for the refresh interval")
			fakeClock.Increment(refreshInterval)

			By("adjusting the limit but returning immediately for the first time")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds())))

			done = wait(limiter)
			select {
			case <-done:
				Fail("should not have returned yet")
			case <-time.After(100 * time.Millisecond):
			}

			By("unblocking after the the new rate limit elapses")
			fakeClock.Increment(checkInterval / time.Duration(checkableCount))
			Expect(<-done).To(Succeed())

			By("inactiving all resources by reset the pipeline with no resource")
			scenario.Run(builder.WithPipeline(atc.Config{
				Resources: atc.ResourceConfigs{},
			}))

			By("waiting for the refresh interval")
			fakeClock.Increment(refreshInterval)

			By("returning immediately and retaining the infinite rate")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf)))
		})
	})

	Context("when a static checks per second value is provided", func() {
		BeforeEach(func() {
			checksPerSecond = 42
		})

		It("respects the value rather than determining it dynamically", func() {
			Expect(limiter.Limit()).To(Equal(rate.Limit(checksPerSecond)))
		})
	})

	Context("when a negative static checks per second value is provided", func() {
		BeforeEach(func() {
			checksPerSecond = -1
		})

		It("results in an infinite rate limit that ignores checkable count", func() {
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf)))

			By("creating a few (ignored) checkables")
			for i := 0; i < 10; i++ {
				createCheckable()
			}

			By("waiting for the (ignored) refresh interval")
			fakeClock.Increment(refreshInterval)

			By("still returning immediately and retaining the infinite rate")
			Expect(<-wait(limiter)).To(Succeed())
			Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf)))
		})
	})
})
<|endoftext|>


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

// Package pubsub is a Google Cloud Pub/Sub client.
//
// More information about Google Cloud Pub/Sub is available on
// https://cloud.google.com/pubsub/docs
package pubsub

import (
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	raw "code.google.com/p/google-api-go-client/pubsub/v1beta1"
)

const (
	// ScopePubSub grants permissions to view and manage Pub/Sub
	// topics and subscriptions.
	ScopePubSub = "https://www.googleapis.com/auth/pubsub"

	// ScopeCloudPlatform grants permissions to view and manage your data
	// across Google Cloud Platform services.
	ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform"
)

// Client is a Google Cloud Pub/Sub (Pub/Sub) client.
type Client struct {
	proj string
	s    *raw.Service
}

// Message represents a Pub/Sub message.
type Message struct {
	// AckID is the identifier to acknowledge this message.
	AckID string

	// Data is the actual data in the message.
	Data []byte

	// Labels represents the key-value pairs the current message
	// is labelled with.
	Labels map[string]string
}

// New creates a new Pub/Sub client to manage topics and subscriptions
// under the provided project. The provided RoundTripper should be
// authorized and authenticated to make calls to Google Cloud Pub/Sub API.
// See the package examples for how to create an authorized http.RoundTripper.
func New(projID string, tr http.RoundTripper) *Client {
	return NewWithClient(projID, &http.Client{Transport: tr})
}

// NewWithClient creates a new Pub/Sub client to manage topics and
// subscriptions under the provided project. The client's
// Transport should be authorized and authenticated to make
// calls to Google Cloud Pub/Sub API.
// See the package examples for how to create an authorized http.RoundTripper.
func NewWithClient(projID string, c *http.Client) *Client {
	// TODO(jbd): Add user-agent.
	s, _ := raw.New(c)
	return &Client{proj: projID, s: s}
}

// TODO(jbd): Add subscription and topic listing.

// CreateSub creates a Pub/Sub subscription on the backend.
// A subscription should subscribe to an existing topic.
//
// The messages that haven't acknowledged will be pushed back to the
// subscription again when the default acknowledgement deadline is
// reached. You can override the default deadline by providing a
// non-zero deadline. Deadline must not be specified to
// precision greater than one second.
//
// As new messages are being queued on the subscription, you
// may recieve push notifications regarding to the new arrivals.
// To receive notifications of new messages in the queue,
// specify an endpoint callback URL.
// If endpoint is an empty string the backend will not notify the
// client of new messages.
//
// If the subscription already exists an error will be returned.
func (c *Client) CreateSub(name string, topic string, deadline time.Duration, endpoint string) error {
	sub := &raw.Subscription{
		Topic: fullTopicName(c.proj, topic),
		Name:  fullSubName(c.proj, name),
	}
	if int64(deadline) > 0 {
		if !isSec(deadline) {
			return errors.New("pubsub: deadline must not be specified to precision greater than one second")
		}
		sub.AckDeadlineSeconds = int64(deadline / time.Second)
	}
	if endpoint != "" {
		sub.PushConfig = &raw.PushConfig{PushEndpoint: endpoint}
	}
	_, err := c.s.Subscriptions.Create(sub).Do()
	return err
}

// DeleteSub deletes the subscription.
func (s *Client) DeleteSub(name string) error {
	return s.s.Subscriptions.Delete(fullSubName(s.proj, name)).Do()
}

// ModifyAckDeadline modifies the acknowledgement deadline
// for the messages retrieved from the specified subscription.
// Deadline must not be specified to precision greater than one second.
func (c *Client) ModifyAckDeadline(sub string, deadline time.Duration) error {
	if !isSec(deadline) {
		return errors.New("pubsub: deadline must not be specified to precision greater than one second")
	}
	return c.s.Subscriptions.ModifyAckDeadline(&raw.ModifyAckDeadlineRequest{
		Subscription:       fullSubName(c.proj, sub),
		AckDeadlineSeconds: int64(deadline),
	}).Do()
}

// ModifyPushEndpoint modifies the URL endpoint to modify the resource
// to handle push notifications coming from the Pub/Sub backend
// for the specified subscription.
func (c *Client) ModifyPushEndpoint(sub, endpoint string) error {
	return c.s.Subscriptions.ModifyPushConfig(&raw.ModifyPushConfigRequest{
		Subscription: fullSubName(c.proj, sub),
		PushConfig: &raw.PushConfig{
			PushEndpoint: endpoint,
		},
	}).Do()
}

// SubExists returns true if subscription exists.
func (s *Client) SubExists(name string) (bool, error) {
	panic("not yet implemented")
}

// Ack acknowledges one or more Pub/Sub messages on the
// specified subscription.
func (c *Client) Ack(sub string, id ...string) error {
	return c.s.Subscriptions.Acknowledge(&raw.AcknowledgeRequest{
		Subscription: fullSubName(c.proj, sub),
		AckId:        id,
	}).Do()
}

// Pull pulls a new message from the specified subscription queue.
func (c *Client) Pull(sub string) (*Message, error) {
	return c.pull(sub, true)
}

// PullWait pulls a new message from the specified subscription queue.
// If there are no messages left in the subscription queue, it will
// block until a new message arrives or timeout occurs.
func (c *Client) PullWait(sub string) (*Message, error) {
	return c.pull(sub, false)
}

func (c *Client) pull(sub string, retImmediately bool) (*Message, error) {
	resp, err := c.s.Subscriptions.Pull(&raw.PullRequest{
		Subscription:      fullSubName(c.proj, sub),
		ReturnImmediately: retImmediately,
	}).Do()
	if err != nil {
		return nil, err
	}
	data, err := base64.StdEncoding.DecodeString(resp.PubsubEvent.Message.Data)
	if err != nil {
		return nil, err
	}

	labels := make(map[string]string)
	for _, l := range resp.PubsubEvent.Message.Label {
		if l.StrValue != "" {
			labels[l.Key] = l.StrValue
		} else {
			labels[l.Key] = strconv.FormatInt(l.NumValue, 10)
		}
	}
	return &Message{
		AckID:  resp.AckId,
		Data:   data,
		Labels: labels,
	}, nil
}

// CreateTopic creates a new topic with the specified name on the backend.
// It will return an error if topic already exists.
func (c *Client) CreateTopic(name string) error {
	_, err := c.s.Topics.Create(&raw.Topic{
		Name: fullTopicName(c.proj, name),
	}).Do()
	return err
}

// DeleteTopic deletes the specified topic.
func (c *Client) DeleteTopic(name string) error {
	return c.s.Topics.Delete(fullTopicName(c.proj, name)).Do()
}

// TopicExists returns true if a topic exists with the specified name.
func (c *Client) TopicExists(name string) (bool, error) {
	panic("not yet implemented")
}

// Publish publishes a new message to the specified topic's subscribers.
// You don't have to label your message. Use nil if there are no labels.
// Label values could be either int64 or string. It will return an error
// if you provide a value of another kind.
func (c *Client) Publish(topic string, data []byte, labels map[string]string) error {
	var rawLabels []*raw.Label
	if labels != nil {
		rawLabels := []*raw.Label{}
		for k, v := range labels {
			rawLabels = append(rawLabels, &raw.Label{Key: k, StrValue: v})
		}
	}
	return c.s.Topics.Publish(&raw.PublishRequest{
		Topic: fullTopicName(c.proj, topic),
		Message: &raw.PubsubMessage{
			Data:  base64.StdEncoding.EncodeToString(data),
			Label: rawLabels,
		},
	}).Do()
}

// fullSubName returns the fully qualified name for a subscription.
// E.g. /subscriptions/project-id/subscription-name.
func fullSubName(proj, name string) string {
	return fmt.Sprintf("/subscriptions/%s/%s", proj, name)
}

// fullTopicName returns the fully qualified name for a topic.
// E.g. /topics/project-id/topic-name.
func fullTopicName(proj, name string) string {
	return fmt.Sprintf("/topics/%s/%s", proj, name)
}

func isSec(dur time.Duration) bool {
	return dur%time.Second == 0
}
<commit_msg>Fixed npd when the Message == nil.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package pubsub is a Google Cloud Pub/Sub client.
//
// More information about Google Cloud Pub/Sub is available on
// https://cloud.google.com/pubsub/docs
package pubsub

import (
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	raw "code.google.com/p/google-api-go-client/pubsub/v1beta1"
)

const (
	// ScopePubSub grants permissions to view and manage Pub/Sub
	// topics and subscriptions.
	ScopePubSub = "https://www.googleapis.com/auth/pubsub"

	// ScopeCloudPlatform grants permissions to view and manage your data
	// across Google Cloud Platform services.
	ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform"
)

// Client is a Google Cloud Pub/Sub (Pub/Sub) client.
type Client struct {
	proj string
	s    *raw.Service
}

// Message represents a Pub/Sub message.
type Message struct {
	// AckID is the identifier to acknowledge this message.
	AckID string

	// Data is the actual data in the message.
	Data []byte

	// Labels represents the key-value pairs the current message
	// is labelled with.
	Labels map[string]string
}

// New creates a new Pub/Sub client to manage topics and subscriptions
// under the provided project. The provided RoundTripper should be
// authorized and authenticated to make calls to Google Cloud Pub/Sub API.
// See the package examples for how to create an authorized http.RoundTripper.
func New(projID string, tr http.RoundTripper) *Client {
	return NewWithClient(projID, &http.Client{Transport: tr})
}

// NewWithClient creates a new Pub/Sub client to manage topics and
// subscriptions under the provided project. The client's
// Transport should be authorized and authenticated to make
// calls to Google Cloud Pub/Sub API.
// See the package examples for how to create an authorized http.RoundTripper.
func NewWithClient(projID string, c *http.Client) *Client {
	// TODO(jbd): Add user-agent.
	s, _ := raw.New(c)
	return &Client{proj: projID, s: s}
}

// TODO(jbd): Add subscription and topic listing.

// CreateSub creates a Pub/Sub subscription on the backend.
// A subscription should subscribe to an existing topic.
//
// The messages that haven't acknowledged will be pushed back to the
// subscription again when the default acknowledgement deadline is
// reached. You can override the default deadline by providing a
// non-zero deadline. Deadline must not be specified to
// precision greater than one second.
//
// As new messages are being queued on the subscription, you
// may recieve push notifications regarding to the new arrivals.
// To receive notifications of new messages in the queue,
// specify an endpoint callback URL.
// If endpoint is an empty string the backend will not notify the
// client of new messages.
//
// If the subscription already exists an error will be returned.
func (c *Client) CreateSub(name string, topic string, deadline time.Duration, endpoint string) error {
	sub := &raw.Subscription{
		Topic: fullTopicName(c.proj, topic),
		Name:  fullSubName(c.proj, name),
	}
	if int64(deadline) > 0 {
		if !isSec(deadline) {
			return errors.New("pubsub: deadline must not be specified to precision greater than one second")
		}
		sub.AckDeadlineSeconds = int64(deadline / time.Second)
	}
	if endpoint != "" {
		sub.PushConfig = &raw.PushConfig{PushEndpoint: endpoint}
	}
	_, err := c.s.Subscriptions.Create(sub).Do()
	return err
}

// DeleteSub deletes the subscription.
func (s *Client) DeleteSub(name string) error {
	return s.s.Subscriptions.Delete(fullSubName(s.proj, name)).Do()
}

// ModifyAckDeadline modifies the acknowledgement deadline
// for the messages retrieved from the specified subscription.
// Deadline must not be specified to precision greater than one second.
func (c *Client) ModifyAckDeadline(sub string, deadline time.Duration) error {
	if !isSec(deadline) {
		return errors.New("pubsub: deadline must not be specified to precision greater than one second")
	}
	return c.s.Subscriptions.ModifyAckDeadline(&raw.ModifyAckDeadlineRequest{
		Subscription:       fullSubName(c.proj, sub),
		AckDeadlineSeconds: int64(deadline),
	}).Do()
}

// ModifyPushEndpoint modifies the URL endpoint to modify the resource
// to handle push notifications coming from the Pub/Sub backend
// for the specified subscription.
func (c *Client) ModifyPushEndpoint(sub, endpoint string) error {
	return c.s.Subscriptions.ModifyPushConfig(&raw.ModifyPushConfigRequest{
		Subscription: fullSubName(c.proj, sub),
		PushConfig: &raw.PushConfig{
			PushEndpoint: endpoint,
		},
	}).Do()
}

// SubExists returns true if subscription exists.
func (s *Client) SubExists(name string) (bool, error) {
	panic("not yet implemented")
}

// Ack acknowledges one or more Pub/Sub messages on the
// specified subscription.
func (c *Client) Ack(sub string, id ...string) error {
	return c.s.Subscriptions.Acknowledge(&raw.AcknowledgeRequest{
		Subscription: fullSubName(c.proj, sub),
		AckId:        id,
	}).Do()
}

// Pull pulls a new message from the specified subscription queue.
func (c *Client) Pull(sub string) (*Message, error) {
	return c.pull(sub, true)
}

// PullWait pulls a new message from the specified subscription queue.
// If there are no messages left in the subscription queue, it will
// block until a new message arrives or timeout occurs.
func (c *Client) PullWait(sub string) (*Message, error) {
	return c.pull(sub, false)
}

func (c *Client) pull(sub string, retImmediately bool) (*Message, error) {
	resp, err := c.s.Subscriptions.Pull(&raw.PullRequest{
		Subscription:      fullSubName(c.proj, sub),
		ReturnImmediately: retImmediately,
	}).Do()
	if err != nil {
		return nil, err
	}
	if resp.PubsubEvent.Message == nil {
		return nil, fmt.Errorf("No message available")
	}
	data, err := base64.StdEncoding.DecodeString(resp.PubsubEvent.Message.Data)
	if err != nil {
		return nil, err
	}

	labels := make(map[string]string)
	for _, l := range resp.PubsubEvent.Message.Label {
		if l.StrValue != "" {
			labels[l.Key] = l.StrValue
		} else {
			labels[l.Key] = strconv.FormatInt(l.NumValue, 10)
		}
	}
	return &Message{
		AckID:  resp.AckId,
		Data:   data,
		Labels: labels,
	}, nil
}

// CreateTopic creates a new topic with the specified name on the backend.
// It will return an error if topic already exists.
func (c *Client) CreateTopic(name string) error {
	_, err := c.s.Topics.Create(&raw.Topic{
		Name: fullTopicName(c.proj, name),
	}).Do()
	return err
}

// DeleteTopic deletes the specified topic.
func (c *Client) DeleteTopic(name string) error {
	return c.s.Topics.Delete(fullTopicName(c.proj, name)).Do()
}

// TopicExists returns true if a topic exists with the specified name.
func (c *Client) TopicExists(name string) (bool, error) {
	panic("not yet implemented")
}

// Publish publishes a new message to the specified topic's subscribers.
// You don't have to label your message. Use nil if there are no labels.
// Label values could be either int64 or string. It will return an error
// if you provide a value of another kind.
func (c *Client) Publish(topic string, data []byte, labels map[string]string) error {
	var rawLabels []*raw.Label
	if labels != nil {
		rawLabels := []*raw.Label{}
		for k, v := range labels {
			rawLabels = append(rawLabels, &raw.Label{Key: k, StrValue: v})
		}
	}
	return c.s.Topics.Publish(&raw.PublishRequest{
		Topic: fullTopicName(c.proj, topic),
		Message: &raw.PubsubMessage{
			Data:  base64.StdEncoding.EncodeToString(data),
			Label: rawLabels,
		},
	}).Do()
}

// fullSubName returns the fully qualified name for a subscription.
// E.g. /subscriptions/project-id/subscription-name.
func fullSubName(proj, name string) string {
	return fmt.Sprintf("/subscriptions/%s/%s", proj, name)
}

// fullTopicName returns the fully qualified name for a topic.
// E.g. /topics/project-id/topic-name.
func fullTopicName(proj, name string) string {
	return fmt.Sprintf("/topics/%s/%s", proj, name)
}

func isSec(dur time.Duration) bool {
	return dur%time.Second == 0
}
<|endoftext|>


<commit_before>package alidayu

import "time"

var postbox *Postbox

// 初始化服务
// prod:   是否应用于正式环境
// key:    alidayu账号分配给应用的AppKey
// secret: alidayu账号分配给应用的AppSecret
func InitAlidayu(prod bool, key, secret string) {
	if prod {
		AppURL = URL_PRODUCTION
	}
	AppKey = key
	AppSecret = secret
	if postbox == nil {
		SetAlidayuQueue(DEFAULT_COURIER_SIZE, DEFAULT_CAPACITY_SIZE)
	}
}

// 设置服务队列
// thread_count:   同时处理线程数
// queue_capacity: 每个线程的承载数量能力
func SetAlidayuQueue(thread_count, queue_capacity int) {
	postbox = NewPostbox(thread_count, queue_capacity).Start()
}

func closeAlidayu() error {
	if postbox != nil {
		return postbox.Close()
	}
	return nil
}

// 关闭服务
// waitSeconds: 最大的等待时间
func CloseAlidayu(waitSeconds ...int) error {
	if len(waitSeconds) >= 1 {
		if ws := waitSeconds[0]; ws > 0 {
			for i := 0; i < ws; i++ {
				if err := closeAlidayu(); err == nil {
					return nil
				}
				time.Sleep(time.Second)
			}
		}
	}
	return closeAlidayu()
}

// 服务是否闲置(一般用于测试)
func IsIdle() bool {
	if postbox != nil {
		return postbox.IsIdle()
	}
	return true
}

// 默认发送信息
func SendMessage(msg IMessage, f ...MessageErrorFunc) error {
	return SendMessageQueue(msg)
}

// 通过队列发送信息
func SendMessageQueue(msg IMessage, f ...MessageErrorFunc) error {
	if postbox != nil {
		return postbox.StuffMessage(msg, f...)
	}
	return ErrServiceClosed
}

// 通过队列发送信息
func SendMessageDirect(msg IMessage) error {
	if msg == nil {
		return ErrMessageNil
	} else if err := msg.Error(); err != nil {
		return err
	} else if ok, resp := post(msg); !ok {
		return NewAlidayuResponseError(resp)
	}
	return nil
}
<commit_msg>feat: new function - send directly<commit_after>package alidayu

import "time"

var postbox *Postbox

// 初始化服务
// prod:   是否应用于正式环境
// key:    alidayu账号分配给应用的AppKey
// secret: alidayu账号分配给应用的AppSecret
func InitAlidayu(prod bool, key, secret string) {
	if prod {
		AppURL = URL_PRODUCTION
	}
	AppKey = key
	AppSecret = secret
	if postbox == nil {
		SetAlidayuQueue(DEFAULT_COURIER_SIZE, DEFAULT_CAPACITY_SIZE)
	}
}

// 设置服务队列
// thread_count:   同时处理线程数
// queue_capacity: 每个线程的承载数量能力
func SetAlidayuQueue(thread_count, queue_capacity int) {
	postbox = NewPostbox(thread_count, queue_capacity).Start()
}

func closeAlidayu() error {
	if postbox != nil {
		return postbox.Close()
	}
	return nil
}

// 关闭服务
// waitSeconds: 最大的等待时间
func CloseAlidayu(waitSeconds ...int) error {
	if len(waitSeconds) >= 1 {
		if ws := waitSeconds[0]; ws > 0 {
			for i := 0; i < ws; i++ {
				if err := closeAlidayu(); err == nil {
					return nil
				}
				time.Sleep(time.Second)
			}
		}
	}
	return closeAlidayu()
}

// 服务是否闲置(一般用于测试)
func IsIdle() bool {
	if postbox != nil {
		return postbox.IsIdle()
	}
	return true
}

// 默认发送信息
func SendMessage(msg IMessage, f ...MessageErrorFunc) error {
	return SendMessageQueue(msg)
}

// 异步发送信息
func SendMessageQueue(msg IMessage, f ...MessageErrorFunc) error {
	if postbox != nil {
		return postbox.StuffMessage(msg, f...)
	}
	return ErrServiceClosed
}

// 同步发送信息
func SendMessageDirect(msg IMessage) error {
	if msg == nil {
		return ErrMessageNil
	} else if err := msg.Error(); err != nil {
		return err
	} else if ok, resp := post(msg); !ok {
		return NewAlidayuResponseError(resp)
	}
	return nil
}
<|endoftext|>


<commit_before>package pwr

import (
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/go-errors/errors"
	"github.com/itchio/wharf/pools"
	"github.com/itchio/wharf/pools/nullpool"
	"github.com/itchio/wharf/wsync"
)

type ValidatorContext struct {
	WoundsPath string
	NumWorkers int

	Consumer *StateConsumer

	// FailFast makes Validate return Wounds as errors and stop checking
	FailFast bool

	// internal
	TargetPool wsync.Pool
	Wounds     chan *Wound
}

func (vctx *ValidatorContext) Validate(target string, signature *SignatureInfo) error {
	var woundsWriter *WoundsWriter
	vctx.Wounds = make(chan *Wound)
	errs := make(chan error)
	done := make(chan bool)

	if vctx.FailFast {
		if vctx.WoundsPath != "" {
			return fmt.Errorf("Validate: FailFast is not compatibel with WoundsPath")
		}

		go func() {
			for w := range vctx.Wounds {
				errs <- fmt.Errorf(w.PrettyString(signature.Container))
			}
			done <- true
		}()
	} else if vctx.WoundsPath == "" {
		woundsPrinter := &WoundsPrinter{
			Wounds: vctx.Wounds,
		}

		go func() {
			err := woundsPrinter.Do(signature, vctx.Consumer)
			if err != nil {
				errs <- err
				return
			}
			done <- true
		}()
	} else {
		woundsWriter = &WoundsWriter{
			Wounds: vctx.Wounds,
		}

		go func() {
			err := woundsWriter.Do(signature, vctx.WoundsPath)
			if err != nil {
				errs <- err
				return
			}
			done <- true
		}()
	}

	numWorkers := vctx.NumWorkers
	if numWorkers == 0 {
		numWorkers = runtime.NumCPU() + 1
	}

	fileIndices := make(chan int64)

	for i := 0; i < numWorkers; i++ {
		go vctx.validate(target, signature, fileIndices, done, errs)
	}

	for fileIndex := range signature.Container.Files {
		fileIndices <- int64(fileIndex)
	}

	close(fileIndices)

	// wait for all workers to finish
	for i := 0; i < numWorkers; i++ {
		select {
		case err := <-errs:
			return err
		case <-done:
			// good!
		}
	}

	close(vctx.Wounds)

	// wait for wounds writer to finish
	select {
	case err := <-errs:
		return err
	case <-done:
		// good!
	}

	return nil
}

func (vctx *ValidatorContext) validate(target string, signature *SignatureInfo, fileIndices chan int64, done chan bool, errs chan error) {
	targetPool, err := pools.New(signature.Container, target)
	if err != nil {
		errs <- err
		return
	}

	validatingPool := &ValidatingPool{
		Pool:      nullpool.New(signature.Container),
		Container: signature.Container,
		Signature: signature,

		Wounds: vctx.Wounds,
	}

	for fileIndex := range fileIndices {
		file := signature.Container.Files[fileIndex]

		reader, err := targetPool.GetReader(fileIndex)
		if err != nil {
			if os.IsNotExist(err) {
				// that's one big wound
				vctx.Wounds <- &Wound{
					FileIndex: fileIndex,
					Start:     0,
					End:       file.Size,
				}
				continue
			} else {
				errs <- err
				return
			}
		}

		var writer io.WriteCloser
		writer, err = validatingPool.GetWriter(fileIndex)
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}

		writtenBytes, err := io.Copy(writer, reader)
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}

		if writtenBytes != file.Size {
			vctx.Wounds <- &Wound{
				FileIndex: fileIndex,
				Start:     writtenBytes,
				End:       file.Size,
			}
			vctx.Consumer.Infof("short file: expected %d, got %d", writtenBytes, file.Size)
		}

		err = writer.Close()
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}
	}

	err = targetPool.Close()
	if err != nil {
		errs <- errors.Wrap(err, 1)
		return
	}

	done <- true
}
<commit_msg>debug--<commit_after>package pwr

import (
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/go-errors/errors"
	"github.com/itchio/wharf/pools"
	"github.com/itchio/wharf/pools/nullpool"
	"github.com/itchio/wharf/wsync"
)

type ValidatorContext struct {
	WoundsPath string
	NumWorkers int

	Consumer *StateConsumer

	// FailFast makes Validate return Wounds as errors and stop checking
	FailFast bool

	// internal
	TargetPool wsync.Pool
	Wounds     chan *Wound
}

func (vctx *ValidatorContext) Validate(target string, signature *SignatureInfo) error {
	var woundsWriter *WoundsWriter
	vctx.Wounds = make(chan *Wound)
	errs := make(chan error)
	done := make(chan bool)

	if vctx.FailFast {
		if vctx.WoundsPath != "" {
			return fmt.Errorf("Validate: FailFast is not compatibel with WoundsPath")
		}

		go func() {
			for w := range vctx.Wounds {
				errs <- fmt.Errorf(w.PrettyString(signature.Container))
			}
			done <- true
		}()
	} else if vctx.WoundsPath == "" {
		woundsPrinter := &WoundsPrinter{
			Wounds: vctx.Wounds,
		}

		go func() {
			err := woundsPrinter.Do(signature, vctx.Consumer)
			if err != nil {
				errs <- err
				return
			}
			done <- true
		}()
	} else {
		woundsWriter = &WoundsWriter{
			Wounds: vctx.Wounds,
		}

		go func() {
			err := woundsWriter.Do(signature, vctx.WoundsPath)
			if err != nil {
				errs <- err
				return
			}
			done <- true
		}()
	}

	numWorkers := vctx.NumWorkers
	if numWorkers == 0 {
		numWorkers = runtime.NumCPU() + 1
	}

	fileIndices := make(chan int64)

	for i := 0; i < numWorkers; i++ {
		go vctx.validate(target, signature, fileIndices, done, errs)
	}

	for fileIndex := range signature.Container.Files {
		fileIndices <- int64(fileIndex)
	}

	close(fileIndices)

	// wait for all workers to finish
	for i := 0; i < numWorkers; i++ {
		select {
		case err := <-errs:
			return err
		case <-done:
			// good!
		}
	}

	close(vctx.Wounds)

	// wait for wounds writer to finish
	select {
	case err := <-errs:
		return err
	case <-done:
		// good!
	}

	return nil
}

func (vctx *ValidatorContext) validate(target string, signature *SignatureInfo, fileIndices chan int64, done chan bool, errs chan error) {
	targetPool, err := pools.New(signature.Container, target)
	if err != nil {
		errs <- err
		return
	}

	validatingPool := &ValidatingPool{
		Pool:      nullpool.New(signature.Container),
		Container: signature.Container,
		Signature: signature,

		Wounds: vctx.Wounds,
	}

	for fileIndex := range fileIndices {
		file := signature.Container.Files[fileIndex]

		reader, err := targetPool.GetReader(fileIndex)
		if err != nil {
			if os.IsNotExist(err) {
				// that's one big wound
				vctx.Wounds <- &Wound{
					FileIndex: fileIndex,
					Start:     0,
					End:       file.Size,
				}
				continue
			} else {
				errs <- err
				return
			}
		}

		var writer io.WriteCloser
		writer, err = validatingPool.GetWriter(fileIndex)
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}

		writtenBytes, err := io.Copy(writer, reader)
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}

		if writtenBytes != file.Size {
			vctx.Wounds <- &Wound{
				FileIndex: fileIndex,
				Start:     writtenBytes,
				End:       file.Size,
			}
		}

		err = writer.Close()
		if err != nil {
			errs <- errors.Wrap(err, 1)
			return
		}
	}

	err = targetPool.Close()
	if err != nil {
		errs <- errors.Wrap(err, 1)
		return
	}

	done <- true
}
<|endoftext|>


<commit_before>package orders

import (
	"github.com/ticketmatic/tm-go/ticketmatic"
)

// Get a list of orders
func Getlist(client *ticketmatic.Client, params *ticketmatic.OrderQuery) ([]*ticketmatic.Order, error) {
	r := client.NewRequest("GET", "/{accountname}/orders")
	r.AddParameter("filter", params.Filter)
	r.AddParameter("includearchived", params.Includearchived)
	r.AddParameter("lastupdatesince", params.Lastupdatesince)
	r.AddParameter("limit", params.Limit)
	r.AddParameter("offset", params.Offset)
	r.AddParameter("orderby", params.Orderby)
	r.AddParameter("output", params.Output)
	r.AddParameter("searchterm", params.Searchterm)
	r.AddParameter("simplefilter", params.Simplefilter)

	var obj []*ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Get a single order
func Get(client *ticketmatic.Client, id int64) (*ticketmatic.Order, error) {
	r := client.NewRequest("GET", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Create a new order
//
// Creates a new empty order.
//
// Each order is linked to a sales channel
// (https://apps.ticketmatic.com/#/knowledgebase/api/types/SalesChannel), which
// needs to be supplied when creating.
func Create(client *ticketmatic.Client, data *ticketmatic.CreateOrder) (*ticketmatic.Order, error) {
	r := client.NewRequest("POST", "/{accountname}/orders")
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Update an order
func Update(client *ticketmatic.Client, id int64, data *ticketmatic.UpdateOrder) (*ticketmatic.Order, error) {
	r := client.NewRequest("PUT", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Confirm an order
//
// Marks the order as confirmed.
func Confirm(client *ticketmatic.Client, id int64) (*ticketmatic.Order, error) {
	r := client.NewRequest("POST", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Add tickets to order
func Addtickets(client *ticketmatic.Client, id int64, data *ticketmatic.AddTickets) (*ticketmatic.Order, error) {
	r := client.NewRequest("POST", "/{accountname}/orders/{id}/tickets")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}
<commit_msg>Add an additional order method.<commit_after>package orders

import (
	"github.com/ticketmatic/tm-go/ticketmatic"
)

// Get a list of orders
func Getlist(client *ticketmatic.Client, params *ticketmatic.OrderQuery) ([]*ticketmatic.Order, error) {
	r := client.NewRequest("GET", "/{accountname}/orders")
	r.AddParameter("filter", params.Filter)
	r.AddParameter("includearchived", params.Includearchived)
	r.AddParameter("lastupdatesince", params.Lastupdatesince)
	r.AddParameter("limit", params.Limit)
	r.AddParameter("offset", params.Offset)
	r.AddParameter("orderby", params.Orderby)
	r.AddParameter("output", params.Output)
	r.AddParameter("searchterm", params.Searchterm)
	r.AddParameter("simplefilter", params.Simplefilter)

	var obj []*ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Get a single order
func Get(client *ticketmatic.Client, id int64) (*ticketmatic.Order, error) {
	r := client.NewRequest("GET", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Create a new order
//
// Creates a new empty order.
//
// Each order is linked to a sales channel
// (https://apps.ticketmatic.com/#/knowledgebase/api/types/SalesChannel), which
// needs to be supplied when creating.
func Create(client *ticketmatic.Client, data *ticketmatic.CreateOrder) (*ticketmatic.Order, error) {
	r := client.NewRequest("POST", "/{accountname}/orders")
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Update an order
func Update(client *ticketmatic.Client, id int64, data *ticketmatic.UpdateOrder) (*ticketmatic.Order, error) {
	r := client.NewRequest("PUT", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Confirm an order
//
// Marks the order as confirmed.
func Confirm(client *ticketmatic.Client, id int64) (*ticketmatic.Order, error) {
	r := client.NewRequest("POST", "/{accountname}/orders/{id}")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Add tickets to order
func Addtickets(client *ticketmatic.Client, id int64, data *ticketmatic.AddTickets) (*ticketmatic.AddTicketsResult, error) {
	r := client.NewRequest("POST", "/{accountname}/orders/{id}/tickets")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})
	r.Body(data)

	var obj *ticketmatic.AddTicketsResult
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}

// Modify tickets in order
func Updatetickets(client *ticketmatic.Client, id int64, data *ticketmatic.UpdateTickets) (*ticketmatic.Order, error) {
	r := client.NewRequest("PUT", "/{accountname}/orders/{id}/tickets")
	r.UrlParameters(map[string]interface{}{
		"id": id,
	})
	r.Body(data)

	var obj *ticketmatic.Order
	err := r.Run(&obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}
<|endoftext|>


<commit_before>package main

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

type grid struct {
	trees [][]bool
}

func (g *grid) IsTree(row int, column int) bool {
	rowLine := g.trees[row]
	column = column % len(rowLine)
	return rowLine[column]
}

func (g *grid) RowCount() int {
	return len(g.trees)
}

func (g *grid) String() string {
	var b strings.Builder
	for _, lineData := range g.trees {
		for _, t := range lineData {
			if t {
				b.WriteRune('#')
			} else {
				b.WriteRune('.')
			}
		}

		b.WriteRune('\n')
	}

	return b.String()
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func ReadGrid() grid {
	var trees [][]bool

	file, err := os.Open("map.txt")
	check(err)
	reader := bufio.NewReader(file)
	scanner := bufio.NewScanner(reader)

	for {
		if !scanner.Scan() {
			break
		}

		line := scanner.Text()
		lineData := make([]bool, 0, len(line))
		for _, r := range line {
			c := false
			if r == '#' {
				c = true
			}
			lineData = append(lineData, c)
		}

		trees = append(trees, lineData)
	}

	return grid{trees: trees}
}

func checkSlope(g *grid, rowIncrement int, columnIncrement int) int {
	treeCount := 0
	row := 0
	column := 0
	for {
		row += rowIncrement
		column += columnIncrement
		if row >= g.RowCount() {
			break
		}

		if g.IsTree(row, column) {
			treeCount++
		}
	}

	return treeCount
}

func main() {
	grid := ReadGrid()

	total := checkSlope(&grid, 1, 1) * checkSlope(&grid, 1, 3) * checkSlope(&grid, 1, 5) * checkSlope(&grid, 1, 7) * checkSlope(&grid, 2, 1)

	fmt.Printf("Hit %d trees\n", total)
}
<commit_msg>Better code formatting<commit_after>package main

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

type grid struct {
	trees [][]bool
}

func (g *grid) IsTree(row int, column int) bool {
	rowLine := g.trees[row]
	column = column % len(rowLine)
	return rowLine[column]
}

func (g *grid) RowCount() int {
	return len(g.trees)
}

func (g *grid) String() string {
	var b strings.Builder
	for _, lineData := range g.trees {
		for _, t := range lineData {
			if t {
				b.WriteRune('#')
			} else {
				b.WriteRune('.')
			}
		}

		b.WriteRune('\n')
	}

	return b.String()
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func ReadGrid() grid {
	var trees [][]bool

	file, err := os.Open("map.txt")
	check(err)
	reader := bufio.NewReader(file)
	scanner := bufio.NewScanner(reader)

	for {
		if !scanner.Scan() {
			break
		}

		line := scanner.Text()
		lineData := make([]bool, 0, len(line))
		for _, r := range line {
			c := false
			if r == '#' {
				c = true
			}
			lineData = append(lineData, c)
		}

		trees = append(trees, lineData)
	}

	return grid{trees: trees}
}

func checkSlope(g *grid, rowIncrement int, columnIncrement int) int {
	treeCount := 0
	row := 0
	column := 0
	for {
		row += rowIncrement
		column += columnIncrement
		if row >= g.RowCount() {
			break
		}

		if g.IsTree(row, column) {
			treeCount++
		}
	}

	return treeCount
}

func main() {
	grid := ReadGrid()

	total := checkSlope(&grid, 1, 1) *
		checkSlope(&grid, 1, 3) *
		checkSlope(&grid, 1, 5) *
		checkSlope(&grid, 1, 7) *
		checkSlope(&grid, 2, 1)

	fmt.Printf("Hit %d trees\n", total)
}
<|endoftext|>


<commit_before>package aphid

import (
  // External requirements.
  _ "github.com/microcosm-cc/bluemonday"
  _ "github.com/russross/blackfriday"
  _ "github.com/gdamore/mangos"
)

import (
  // Internal requirements.
  _ "github.com/strickyak/rye"
)

import (
	_ "io"
)
<commit_msg>gofmt<commit_after>package aphid

import (
	// External requirements.
	_ "github.com/gdamore/mangos"
	_ "github.com/microcosm-cc/bluemonday"
	_ "github.com/russross/blackfriday"
)

import (
	// Internal requirements.
	_ "github.com/strickyak/rye"
)

import (
	_ "io"
)
<|endoftext|>


<commit_before>// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package chain

import (
	"bytes"
	"context"
	"sync"

	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/errors"
	"github.com/decred/dcrwallet/wallet"
	"golang.org/x/sync/errgroup"
)

// RPCSyncer implements wallet synchronization services by processing
// notifications from a dcrd JSON-RPC server.
type RPCSyncer struct {
	wallet    *wallet.Wallet
	rpcClient *RPCClient

	discoverAccts bool
	mu            sync.Mutex
}

// NewRPCSyncer creates an RPCSyncer that will sync the wallet using the RPC
// client.
func NewRPCSyncer(w *wallet.Wallet, rpcClient *RPCClient) *RPCSyncer {
	return &RPCSyncer{
		wallet:        w,
		rpcClient:     rpcClient,
		discoverAccts: !w.Locked(),
	}
}

// Run synchronizes the wallet, returning when synchronization fails or the
// context is cancelled.  If startupSync is true, all synchronization tasks
// needed to fully register the wallet for notifications and synchronize it with
// the dcrd server are performed.  Otherwise, it will listen for notifications
// but not register for any updates.
func (s *RPCSyncer) Run(ctx context.Context, startupSync bool) error {
	const op errors.Op = "rpcsyncer.Run"

	tipHash, tipHeight := s.wallet.MainChainTip()
	rescanPoint, err := s.wallet.RescanPoint()
	if err != nil {
		return errors.E(op, err)
	}
	log.Infof("Headers synced through block %v height %d", &tipHash, tipHeight)
	if rescanPoint != nil {
		h, err := s.wallet.BlockHeader(rescanPoint)
		if err != nil {
			return errors.E(op, err)
		}
		// The rescan point is the first block that does not have synced
		// transactions, so we are synced with the parent.
		log.Infof("Transactions synced through block %v height %d", &h.PrevBlock, h.Height-1)
	} else {
		log.Infof("Transactions synced through block %v height %d", &tipHash, tipHeight)
	}

	// TODO: handling of voting notifications should be done sequentially with
	// every other notification (voters must know the blocks they are voting
	// on).  Until then, a couple notification processing goroutines must be
	// started and errors merged.
	g, ctx := errgroup.WithContext(ctx)
	g.Go(func() error {
		if startupSync {
			err := s.startupSync(ctx)
			if err != nil {
				return err
			}
		}
		return s.handleNotifications(ctx)
	})
	g.Go(func() error {
		return s.handleVoteNotifications(ctx)
	})
	g.Go(func() error {
		err := s.rpcClient.NotifySpentAndMissedTickets()
		if err != nil {
			const op errors.Op = "dcrd.jsonrpc.notifyspentandmissedtickets"
			return errors.E(op, err)
		}

		if s.wallet.VotingEnabled() {
			// Request notifications for winning tickets.
			err := s.rpcClient.NotifyWinningTickets()
			if err != nil {
				const op errors.Op = "dcrd.jsonrpc.notifywinningtickets"
				return errors.E(op, err)
			}

			vb := s.wallet.VoteBits()
			log.Infof("Wallet voting enabled: vote bits = %#04x, "+
				"extended vote bits = %x", vb.Bits, vb.ExtendedBits)
			log.Infof("Please ensure your wallet remains unlocked so it may vote")
		}

		return nil
	})
	err = g.Wait()
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

func (s *RPCSyncer) handleNotifications(ctx context.Context) error {
	// connectingBlocks keeps track of whether any blocks have been successfully
	// attached to the main chain.  Once any blocks have attached, if a future
	// block fails to attach, the error is fatal.  Otherwise, errors are logged.
	connectingBlocks := false

	sidechains := new(wallet.SidechainForest)
	var relevantTxs map[chainhash.Hash][]*wire.MsgTx

	c := s.rpcClient.notifications()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()

		case n, ok := <-c:
			if !ok {
				return errors.E(errors.NoPeers, "RPC client disconnected")
			}

			var op errors.Op
			var err error
			nonFatal := false
			switch n := n.(type) {
			case blockConnected:
				op = "dcrd.jsonrpc.blockconnected"
				header := new(wire.BlockHeader)
				err = header.Deserialize(bytes.NewReader(n.blockHeader))
				if err != nil {
					break
				}
				blockHash := header.BlockHash()
				getCfilter := s.rpcClient.GetCFilterAsync(&blockHash, wire.GCSFilterRegular)
				var txs []*wire.MsgTx
				rpt, err := s.wallet.RescanPoint()
				if err != nil {
					break
				}
				if rpt == nil {
					txs = make([]*wire.MsgTx, 0, len(n.transactions))
					for _, tx := range n.transactions {
						msgTx := new(wire.MsgTx)
						err = msgTx.Deserialize(bytes.NewReader(tx))
						if err != nil {
							break
						}
						txs = append(txs, msgTx)
					}
					if relevantTxs == nil {
						relevantTxs = make(map[chainhash.Hash][]*wire.MsgTx)
					}
					relevantTxs[blockHash] = txs
				}

				f, err := getCfilter.Receive()
				if err != nil {
					break
				}

				blockNode := wallet.NewBlockNode(header, &blockHash, f)
				sidechains.AddBlockNode(blockNode)

				bestChain, err := s.wallet.EvaluateBestChain(sidechains)
				if err != nil {
					break
				}
				if len(bestChain) != 0 {
					var prevChain []*wallet.BlockNode
					prevChain, err = s.wallet.ChainSwitch(sidechains, bestChain, relevantTxs)
					if err == nil {
						connectingBlocks = true
					}
					nonFatal = !connectingBlocks
					relevantTxs = nil

					if len(prevChain) != 0 {
						log.Infof("Reorganize from %v to %v (total %d block(s) reorged)",
							prevChain[len(prevChain)-1].Hash, bestChain[len(bestChain)-1].Hash, len(prevChain))
						for _, n := range prevChain {
							sidechains.AddBlockNode(n)
						}
					}
					for _, n := range bestChain {
						log.Infof("Connected block %v, height %d, %d wallet transaction(s)",
							n.Hash, n.Header.Height, len(relevantTxs[*n.Hash]))
					}
				}

			case blockDisconnected, reorganization:
				continue // These notifications are ignored

			case relevantTxAccepted:
				op = "dcrd.jsonrpc.relevanttxaccepted"
				nonFatal = true
				var rpt *chainhash.Hash
				rpt, err = s.wallet.RescanPoint()
				if err != nil || rpt != nil {
					break
				}
				err = s.wallet.AcceptMempoolTx(n.transaction)

			case missedTickets:
				op = "dcrd.jsonrpc.spentandmissedtickets"
				err = s.wallet.RevokeOwnedTickets(n.tickets)
				nonFatal = true

			default:
				log.Warnf("Notification handler received unknown notification type %T", n)
				continue
			}

			if err == nil {
				continue
			}

			err = errors.E(op, err)
			if nonFatal {
				log.Errorf("Failed to process consensus server notification: %v", err)
				continue
			}

			return err
		}
	}
}

func (s *RPCSyncer) handleVoteNotifications(ctx context.Context) error {
	c := s.rpcClient.notificationsVoting()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()

		case n, ok := <-c:
			if !ok {
				return errors.E(errors.NoPeers, "RPC client disconnected")
			}

			var op errors.Op
			var err error
			switch n := n.(type) {
			case winningTickets:
				op = "dcrd.jsonrpc.winningtickets"
				err = s.wallet.VoteOnOwnedTickets(n.tickets, n.blockHash, int32(n.blockHeight))
			default:
				log.Warnf("Voting handler received unknown notification type %T", n)
			}
			if err != nil {
				err = errors.E(op, err)
				log.Errorf("Failed to process consensus server notification: %v", err)
			}
		}
	}
}

// hashStop is a zero value stop hash for fetching all possible data using
// locators.
var hashStop chainhash.Hash

// startupSync brings the wallet up to date with the current chain server
// connection.  It creates a rescan request and blocks until the rescan has
// finished.
func (s *RPCSyncer) startupSync(ctx context.Context) error {
	n := BackendFromRPCClient(s.rpcClient.Client)

	// Fetch any missing main chain compact filters.
	err := s.wallet.FetchMissingCFilters(ctx, n)
	if err != nil {
		return err
	}

	// Request notifications for connected and disconnected blocks.
	err = s.rpcClient.NotifyBlocks()
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.notifyblocks"
		return errors.E(op, err)
	}

	// Fetch new headers and cfilters from the server.
	var sidechains wallet.SidechainForest
	locators, err := s.wallet.BlockLocators(nil)
	if err != nil {
		return err
	}
	for {
		headersMsg, err := s.rpcClient.GetHeaders(locators, &hashStop)
		if err != nil {
			return err
		}
		headers := make([]*wire.BlockHeader, 0, len(headersMsg.Headers))
		for _, h := range headersMsg.Headers {
			header := new(wire.BlockHeader)
			err := header.Deserialize(newHexReader(h))
			if err != nil {
				const op errors.Op = "dcrd.jsonrpc.getheaders"
				return errors.E(op, err)
			}
			headers = append(headers, header)
		}
		if len(headers) == 0 {
			break
		}

		nodes := make([]*wallet.BlockNode, len(headers))
		var g errgroup.Group
		for i := range headers {
			i := i
			g.Go(func() error {
				header := headers[i]
				hash := header.BlockHash()
				filter, err := s.rpcClient.GetCFilter(&hash, wire.GCSFilterRegular)
				if err != nil {
					return err
				}
				nodes[i] = wallet.NewBlockNode(header, &hash, filter)
				return nil
			})
		}
		err = g.Wait()
		if err != nil {
			return err
		}

		var added int
		for _, n := range nodes {
			haveBlock, _, _ := s.wallet.BlockInMainChain(n.Hash)
			if haveBlock {
				continue
			}
			if sidechains.AddBlockNode(n) {
				added++
			}
		}

		log.Infof("Fetched %d new header(s) ending at height %d from %v",
			added, nodes[len(nodes)-1].Header.Height, s.rpcClient)
		bestChain, err := s.wallet.EvaluateBestChain(&sidechains)
		if err != nil {
			return err
		}
		if len(bestChain) == 0 {
			continue
		}

		_, err = s.wallet.ValidateHeaderChainDifficulties(bestChain, 0)
		if err != nil {
			return err
		}

		prevChain, err := s.wallet.ChainSwitch(&sidechains, bestChain, nil)
		if err != nil {
			return err
		}

		if len(prevChain) != 0 {
			log.Infof("Reorganize from %v to %v (total %d block(s) reorged)",
				prevChain[len(prevChain)-1].Hash, bestChain[len(bestChain)-1].Hash, len(prevChain))
			for _, n := range prevChain {
				sidechains.AddBlockNode(n)
			}
		}
		tip := bestChain[len(bestChain)-1]
		if len(bestChain) == 1 {
			log.Infof("Connected block %v, height %d", tip.Hash, tip.Header.Height)
		} else {
			log.Infof("Connected %d blocks, new tip block %v, height %d, date %v",
				len(bestChain), tip.Hash, tip.Header.Height, tip.Header.Timestamp)
		}

		locators, err = s.wallet.BlockLocators(nil)
		if err != nil {
			return err
		}
	}

	rescanPoint, err := s.wallet.RescanPoint()
	if err != nil {
		return err
	}
	if rescanPoint != nil {
		s.mu.Lock()
		discoverAccts := s.discoverAccts
		s.mu.Unlock()
		err = s.wallet.DiscoverActiveAddresses(ctx, n, rescanPoint, discoverAccts)
		if err != nil {
			return err
		}
		s.mu.Lock()
		s.discoverAccts = false
		s.mu.Unlock()
		err = s.wallet.LoadActiveDataFilters(ctx, n, true)
		if err != nil {
			return err
		}
		err = s.wallet.Rescan(ctx, n, rescanPoint)
		if err != nil {
			return err
		}
	} else {
		err = s.wallet.LoadActiveDataFilters(ctx, n, true)
		if err != nil {
			return err
		}
	}

	// Rebroadcast unmined transactions
	err = s.wallet.PublishUnminedTransactions(ctx, n)
	if err != nil {
		// Returning this error would end and (likely) restart sync in
		// an endless loop.  It's possible a transaction should be
		// removed, but this is difficult to reliably detect over RPC.
		log.Warnf("Could not publish one or more unmined transactions: %v", err)
	}

	_, err = s.rpcClient.RawRequest("rebroadcastwinners", nil)
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.rebroadcastwinners"
		return errors.E(op, err)
	}
	_, err = s.rpcClient.RawRequest("rebroadcastmissed", nil)
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.rebroadcastmissed"
		return errors.E(op, err)
	}

	log.Infof("Blockchain sync completed, wallet ready for general usage.")

	return nil
}
<commit_msg>Allow RPC startup sync cancellation<commit_after>// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package chain

import (
	"bytes"
	"context"
	"sync"

	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/gcs"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/errors"
	"github.com/decred/dcrwallet/wallet"
	"golang.org/x/sync/errgroup"
)

// RPCSyncer implements wallet synchronization services by processing
// notifications from a dcrd JSON-RPC server.
type RPCSyncer struct {
	wallet    *wallet.Wallet
	rpcClient *RPCClient

	discoverAccts bool
	mu            sync.Mutex
}

// NewRPCSyncer creates an RPCSyncer that will sync the wallet using the RPC
// client.
func NewRPCSyncer(w *wallet.Wallet, rpcClient *RPCClient) *RPCSyncer {
	return &RPCSyncer{
		wallet:        w,
		rpcClient:     rpcClient,
		discoverAccts: !w.Locked(),
	}
}

// Run synchronizes the wallet, returning when synchronization fails or the
// context is cancelled.  If startupSync is true, all synchronization tasks
// needed to fully register the wallet for notifications and synchronize it with
// the dcrd server are performed.  Otherwise, it will listen for notifications
// but not register for any updates.
func (s *RPCSyncer) Run(ctx context.Context, startupSync bool) error {
	const op errors.Op = "rpcsyncer.Run"

	tipHash, tipHeight := s.wallet.MainChainTip()
	rescanPoint, err := s.wallet.RescanPoint()
	if err != nil {
		return errors.E(op, err)
	}
	log.Infof("Headers synced through block %v height %d", &tipHash, tipHeight)
	if rescanPoint != nil {
		h, err := s.wallet.BlockHeader(rescanPoint)
		if err != nil {
			return errors.E(op, err)
		}
		// The rescan point is the first block that does not have synced
		// transactions, so we are synced with the parent.
		log.Infof("Transactions synced through block %v height %d", &h.PrevBlock, h.Height-1)
	} else {
		log.Infof("Transactions synced through block %v height %d", &tipHash, tipHeight)
	}

	// TODO: handling of voting notifications should be done sequentially with
	// every other notification (voters must know the blocks they are voting
	// on).  Until then, a couple notification processing goroutines must be
	// started and errors merged.
	g, ctx := errgroup.WithContext(ctx)
	g.Go(func() error {
		if startupSync {
			err := s.startupSync(ctx)
			if err != nil {
				return err
			}
		}
		return s.handleNotifications(ctx)
	})
	g.Go(func() error {
		return s.handleVoteNotifications(ctx)
	})
	g.Go(func() error {
		err := s.rpcClient.NotifySpentAndMissedTickets()
		if err != nil {
			const op errors.Op = "dcrd.jsonrpc.notifyspentandmissedtickets"
			return errors.E(op, err)
		}

		if s.wallet.VotingEnabled() {
			// Request notifications for winning tickets.
			err := s.rpcClient.NotifyWinningTickets()
			if err != nil {
				const op errors.Op = "dcrd.jsonrpc.notifywinningtickets"
				return errors.E(op, err)
			}

			vb := s.wallet.VoteBits()
			log.Infof("Wallet voting enabled: vote bits = %#04x, "+
				"extended vote bits = %x", vb.Bits, vb.ExtendedBits)
			log.Infof("Please ensure your wallet remains unlocked so it may vote")
		}

		return nil
	})
	err = g.Wait()
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

func (s *RPCSyncer) handleNotifications(ctx context.Context) error {
	// connectingBlocks keeps track of whether any blocks have been successfully
	// attached to the main chain.  Once any blocks have attached, if a future
	// block fails to attach, the error is fatal.  Otherwise, errors are logged.
	connectingBlocks := false

	sidechains := new(wallet.SidechainForest)
	var relevantTxs map[chainhash.Hash][]*wire.MsgTx

	c := s.rpcClient.notifications()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()

		case n, ok := <-c:
			if !ok {
				return errors.E(errors.NoPeers, "RPC client disconnected")
			}

			var op errors.Op
			var err error
			nonFatal := false
			switch n := n.(type) {
			case blockConnected:
				op = "dcrd.jsonrpc.blockconnected"
				header := new(wire.BlockHeader)
				err = header.Deserialize(bytes.NewReader(n.blockHeader))
				if err != nil {
					break
				}
				blockHash := header.BlockHash()
				getCfilter := s.rpcClient.GetCFilterAsync(&blockHash, wire.GCSFilterRegular)
				var txs []*wire.MsgTx
				rpt, err := s.wallet.RescanPoint()
				if err != nil {
					break
				}
				if rpt == nil {
					txs = make([]*wire.MsgTx, 0, len(n.transactions))
					for _, tx := range n.transactions {
						msgTx := new(wire.MsgTx)
						err = msgTx.Deserialize(bytes.NewReader(tx))
						if err != nil {
							break
						}
						txs = append(txs, msgTx)
					}
					if relevantTxs == nil {
						relevantTxs = make(map[chainhash.Hash][]*wire.MsgTx)
					}
					relevantTxs[blockHash] = txs
				}

				f, err := getCfilter.Receive()
				if err != nil {
					break
				}

				blockNode := wallet.NewBlockNode(header, &blockHash, f)
				sidechains.AddBlockNode(blockNode)

				bestChain, err := s.wallet.EvaluateBestChain(sidechains)
				if err != nil {
					break
				}
				if len(bestChain) != 0 {
					var prevChain []*wallet.BlockNode
					prevChain, err = s.wallet.ChainSwitch(sidechains, bestChain, relevantTxs)
					if err == nil {
						connectingBlocks = true
					}
					nonFatal = !connectingBlocks
					relevantTxs = nil

					if len(prevChain) != 0 {
						log.Infof("Reorganize from %v to %v (total %d block(s) reorged)",
							prevChain[len(prevChain)-1].Hash, bestChain[len(bestChain)-1].Hash, len(prevChain))
						for _, n := range prevChain {
							sidechains.AddBlockNode(n)
						}
					}
					for _, n := range bestChain {
						log.Infof("Connected block %v, height %d, %d wallet transaction(s)",
							n.Hash, n.Header.Height, len(relevantTxs[*n.Hash]))
					}
				}

			case blockDisconnected, reorganization:
				continue // These notifications are ignored

			case relevantTxAccepted:
				op = "dcrd.jsonrpc.relevanttxaccepted"
				nonFatal = true
				var rpt *chainhash.Hash
				rpt, err = s.wallet.RescanPoint()
				if err != nil || rpt != nil {
					break
				}
				err = s.wallet.AcceptMempoolTx(n.transaction)

			case missedTickets:
				op = "dcrd.jsonrpc.spentandmissedtickets"
				err = s.wallet.RevokeOwnedTickets(n.tickets)
				nonFatal = true

			default:
				log.Warnf("Notification handler received unknown notification type %T", n)
				continue
			}

			if err == nil {
				continue
			}

			err = errors.E(op, err)
			if nonFatal {
				log.Errorf("Failed to process consensus server notification: %v", err)
				continue
			}

			return err
		}
	}
}

func (s *RPCSyncer) handleVoteNotifications(ctx context.Context) error {
	c := s.rpcClient.notificationsVoting()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()

		case n, ok := <-c:
			if !ok {
				return errors.E(errors.NoPeers, "RPC client disconnected")
			}

			var op errors.Op
			var err error
			switch n := n.(type) {
			case winningTickets:
				op = "dcrd.jsonrpc.winningtickets"
				err = s.wallet.VoteOnOwnedTickets(n.tickets, n.blockHash, int32(n.blockHeight))
			default:
				log.Warnf("Voting handler received unknown notification type %T", n)
			}
			if err != nil {
				err = errors.E(op, err)
				log.Errorf("Failed to process consensus server notification: %v", err)
			}
		}
	}
}

// ctxdo executes f, returning the earliest of f's returned error or a context
// error.  ctxdo adds early returns for operations which do not understand
// context, but the operation is not canceled and will continue executing in
// the background.  If f returns non-nil before the context errors, the error is
// wrapped with op.
func ctxdo(ctx context.Context, op errors.Op, f func() error) error {
	e := make(chan error, 1)
	go func() { e <- f() }()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case err := <-e:
		if err != nil {
			return errors.E(op, err)
		}
		return nil
	}
}

// hashStop is a zero value stop hash for fetching all possible data using
// locators.
var hashStop chainhash.Hash

// startupSync brings the wallet up to date with the current chain server
// connection.  It creates a rescan request and blocks until the rescan has
// finished.
func (s *RPCSyncer) startupSync(ctx context.Context) error {
	n := BackendFromRPCClient(s.rpcClient.Client)

	// Fetch any missing main chain compact filters.
	err := s.wallet.FetchMissingCFilters(ctx, n)
	if err != nil {
		return err
	}

	// Request notifications for connected and disconnected blocks.
	err = s.rpcClient.NotifyBlocks()
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.notifyblocks"
		return errors.E(op, err)
	}

	// Fetch new headers and cfilters from the server.
	var sidechains wallet.SidechainForest
	locators, err := s.wallet.BlockLocators(nil)
	if err != nil {
		return err
	}
	for {
		if err := ctx.Err(); err != nil {
			return err
		}

		var headers []*wire.BlockHeader
		err := ctxdo(ctx, "dcrd.jsonrpc.getheaders", func() error {
			headersMsg, err := s.rpcClient.GetHeaders(locators, &hashStop)
			if err != nil {
				return err
			}
			headers = make([]*wire.BlockHeader, 0, len(headersMsg.Headers))
			for _, h := range headersMsg.Headers {
				header := new(wire.BlockHeader)
				err := header.Deserialize(newHexReader(h))
				if err != nil {
					return err
				}
				headers = append(headers, header)
			}
			return nil
		})
		if err != nil {
			return err
		}
		if len(headers) == 0 {
			break
		}

		nodes := make([]*wallet.BlockNode, len(headers))
		var g errgroup.Group
		for i := range headers {
			i := i
			g.Go(func() error {
				header := headers[i]
				hash := header.BlockHash()
				var filter *gcs.Filter
				err := ctxdo(ctx, "dcrd.jsonrpc.getcfilter", func() error {
					var err error
					filter, err = s.rpcClient.GetCFilter(&hash, wire.GCSFilterRegular)
					return err
				})
				if err != nil {
					return err
				}
				nodes[i] = wallet.NewBlockNode(header, &hash, filter)
				return nil
			})
		}
		err = g.Wait()
		if err != nil {
			return err
		}

		var added int
		for _, n := range nodes {
			haveBlock, _, _ := s.wallet.BlockInMainChain(n.Hash)
			if haveBlock {
				continue
			}
			if sidechains.AddBlockNode(n) {
				added++
			}
		}

		log.Infof("Fetched %d new header(s) ending at height %d from %v",
			added, nodes[len(nodes)-1].Header.Height, s.rpcClient)
		bestChain, err := s.wallet.EvaluateBestChain(&sidechains)
		if err != nil {
			return err
		}
		if len(bestChain) == 0 {
			continue
		}

		_, err = s.wallet.ValidateHeaderChainDifficulties(bestChain, 0)
		if err != nil {
			return err
		}

		prevChain, err := s.wallet.ChainSwitch(&sidechains, bestChain, nil)
		if err != nil {
			return err
		}

		if len(prevChain) != 0 {
			log.Infof("Reorganize from %v to %v (total %d block(s) reorged)",
				prevChain[len(prevChain)-1].Hash, bestChain[len(bestChain)-1].Hash, len(prevChain))
			for _, n := range prevChain {
				sidechains.AddBlockNode(n)
			}
		}
		tip := bestChain[len(bestChain)-1]
		if len(bestChain) == 1 {
			log.Infof("Connected block %v, height %d", tip.Hash, tip.Header.Height)
		} else {
			log.Infof("Connected %d blocks, new tip block %v, height %d, date %v",
				len(bestChain), tip.Hash, tip.Header.Height, tip.Header.Timestamp)
		}

		locators, err = s.wallet.BlockLocators(nil)
		if err != nil {
			return err
		}
	}

	rescanPoint, err := s.wallet.RescanPoint()
	if err != nil {
		return err
	}
	if rescanPoint != nil {
		s.mu.Lock()
		discoverAccts := s.discoverAccts
		s.mu.Unlock()
		err = s.wallet.DiscoverActiveAddresses(ctx, n, rescanPoint, discoverAccts)
		if err != nil {
			return err
		}
		s.mu.Lock()
		s.discoverAccts = false
		s.mu.Unlock()
		err = s.wallet.LoadActiveDataFilters(ctx, n, true)
		if err != nil {
			return err
		}
		err = s.wallet.Rescan(ctx, n, rescanPoint)
		if err != nil {
			return err
		}
	} else {
		err = s.wallet.LoadActiveDataFilters(ctx, n, true)
		if err != nil {
			return err
		}
	}

	// Rebroadcast unmined transactions
	err = s.wallet.PublishUnminedTransactions(ctx, n)
	if err != nil {
		// Returning this error would end and (likely) restart sync in
		// an endless loop.  It's possible a transaction should be
		// removed, but this is difficult to reliably detect over RPC.
		log.Warnf("Could not publish one or more unmined transactions: %v", err)
	}

	_, err = s.rpcClient.RawRequest("rebroadcastwinners", nil)
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.rebroadcastwinners"
		return errors.E(op, err)
	}
	_, err = s.rpcClient.RawRequest("rebroadcastmissed", nil)
	if err != nil {
		const op errors.Op = "dcrd.jsonrpc.rebroadcastmissed"
		return errors.E(op, err)
	}

	log.Infof("Blockchain sync completed, wallet ready for general usage.")

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccAWSVolumeAttachment_basic(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSVolumeAttachment_skipDestroy(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfigSkipDestroy(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"skip_destroy", // attribute only used on resource deletion
				},
			},
		},
	})
}

func TestAccAWSVolumeAttachment_attachStopped(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	stopInstance := func() {
		conn := testAccProvider.Meta().(*AWSClient).ec2conn

		_, err := conn.StopInstances(&ec2.StopInstancesInput{
			InstanceIds: []*string{i.InstanceId},
		})
		if err != nil {
			t.Fatalf("error stopping instance (%s): %s", aws.StringValue(i.InstanceId), err)
		}

		stateConf := &resource.StateChangeConf{
			Pending:    []string{ec2.InstanceStateNamePending, ec2.InstanceStateNameRunning, ec2.InstanceStateNameStopping},
			Target:     []string{ec2.InstanceStateNameStopped},
			Refresh:    InstanceStateRefreshFunc(conn, *i.InstanceId, []string{}),
			Timeout:    10 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		_, err = stateConf.WaitForState()
		if err != nil {
			t.Fatalf("Error waiting for instance(%s) to stop: %s", *i.InstanceId, err)
		}
	}

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfigBase(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists("aws_instance.test", &i),
				),
			},
			{
				PreConfig: stopInstance,
				Config:    testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSVolumeAttachment_update(t *testing.T) {
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentUpdateConfig(rName, false),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "force_detach", "false"),
					resource.TestCheckResourceAttr(resourceName, "skip_destroy", "false"),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"force_detach", // attribute only used on resource deletion
					"skip_destroy", // attribute only used on resource deletion
				},
			},
			{
				Config: testAccVolumeAttachmentUpdateConfig(rName, true),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "force_detach", "true"),
					resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"force_detach", // attribute only used on resource deletion
					"skip_destroy", // attribute only used on resource deletion
				},
			},
		},
	})
}

func TestAccAWSVolumeAttachment_disappears(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
					testAccCheckVolumeAttachmentDisappears(resourceName, &i, &v),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckVolumeAttachmentExists(n string, i *ec2.Instance, v *ec2.Volume) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		for _, b := range i.BlockDeviceMappings {
			if rs.Primary.Attributes["device_name"] == *b.DeviceName {
				if b.Ebs.VolumeId != nil &&
					rs.Primary.Attributes["volume_id"] == *b.Ebs.VolumeId &&
					rs.Primary.Attributes["volume_id"] == *v.VolumeId {
					// pass
					return nil
				}
			}
		}

		return fmt.Errorf("Error finding instance/volume")
	}
}

func testAccCheckVolumeAttachmentDisappears(n string, i *ec2.Instance, v *ec2.Volume) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn

		opts := &ec2.DetachVolumeInput{
			Device:     aws.String(rs.Primary.Attributes["device_name"]),
			InstanceId: i.InstanceId,
			VolumeId:   v.VolumeId,
			Force:      aws.Bool(true),
		}

		_, err := conn.DetachVolume(opts)
		if err != nil {
			return err
		}

		vId := aws.StringValue(v.VolumeId)
		iId := aws.StringValue(i.InstanceId)

		stateConf := &resource.StateChangeConf{
			Pending:    []string{ec2.VolumeAttachmentStateDetaching},
			Target:     []string{ec2.VolumeAttachmentStateDetached},
			Refresh:    volumeAttachmentStateRefreshFunc(conn, rs.Primary.Attributes["device_name"], vId, iId),
			Timeout:    5 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vId, iId)
		_, err = stateConf.WaitForState()
		if err != nil {
			return fmt.Errorf(
				"Error waiting for Volume (%s) to detach from Instance (%s): %s",
				vId, iId, err)
		}

		return err
	}
}

func testAccCheckVolumeAttachmentDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).ec2conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_volume_attachment" {
			continue
		}

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(rs.Primary.Attributes["volume_id"])},
			Filters: []*ec2.Filter{
				{
					Name:   aws.String("attachment.device"),
					Values: []*string{aws.String(rs.Primary.Attributes["device_name"])},
				},
				{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(rs.Primary.Attributes["instance_id"])},
				},
			},
		}

		_, err := conn.DescribeVolumes(request)
		if err != nil {
			if isAWSErr(err, "InvalidVolume.NotFound", "") {
				return nil
			}
			return fmt.Errorf("error describing volumes (%s): %s", rs.Primary.ID, err)
		}
	}
	return nil
}

func testAccVolumeAttachmentInstanceOnlyConfigBase(rName string) string {
	return fmt.Sprintf(`
data "aws_availability_zones" "available" {
  state = "available"

  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

resource "aws_instance" "test" {
  ami               = "ami-21f78e11"
  availability_zone = "${data.aws_availability_zones.available.names[0]}"
  instance_type     = "t1.micro"

  tags = {
    Name = %[1]q
  }
}
`, rName)
}

func testAccVolumeAttachmentConfigBase(rName string) string {
	return testAccVolumeAttachmentInstanceOnlyConfigBase(rName) + fmt.Sprintf(`
resource "aws_ebs_volume" "test" {
  availability_zone = "${data.aws_availability_zones.available.names[0]}"
  size              = 1

  tags = {
    Name = %[1]q
  }
}
`, rName)
}

func testAccVolumeAttachmentConfig(rName string) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
resource "aws_volume_attachment" "test" {
  device_name = "/dev/sdh"
  volume_id   = "${aws_ebs_volume.test.id}"
  instance_id = "${aws_instance.test.id}"
}
`)
}

func testAccVolumeAttachmentConfigSkipDestroy(rName string) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
data "aws_ebs_volume" "test" {
  filter {
    name   = "size"
    values = ["${aws_ebs_volume.test.size}"]
  }
  filter {
    name   = "availability-zone"
    values = ["${aws_ebs_volume.test.availability_zone}"]
  }
  filter {
    name   = "tag:Name"
    values = ["tf-acc-test-volume-attachment"]
  }
}

resource "aws_volume_attachment" "test" {
  device_name  = "/dev/sdh"
  volume_id    = "${data.aws_ebs_volume.test.id}"
  instance_id  = "${aws_instance.test.id}"
  skip_destroy = true
}
`)
}

func testAccVolumeAttachmentUpdateConfig(rName string, detach bool) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
resource "aws_volume_attachment" "test" {
  device_name  = "/dev/sdh"
  volume_id    = "${aws_ebs_volume.test.id}"
  instance_id  = "${aws_instance.test.id}"
  force_detach = %[1]t
  skip_destroy = %[1]t
}
`, detach)
}

func testAccAWSVolumeAttachmentImportStateIDFunc(resourceName string) resource.ImportStateIdFunc {
	return func(s *terraform.State) (string, error) {
		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return "", fmt.Errorf("Not found: %s", resourceName)
		}
		return fmt.Sprintf("%s:%s:%s", rs.Primary.Attributes["device_name"], rs.Primary.Attributes["volume_id"], rs.Primary.Attributes["instance_id"]), nil
	}
}
<commit_msg>fix skip test data source filter<commit_after>package aws

import (
	"fmt"
	"log"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccAWSVolumeAttachment_basic(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSVolumeAttachment_skipDestroy(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfigSkipDestroy(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"skip_destroy", // attribute only used on resource deletion
				},
			},
		},
	})
}

func TestAccAWSVolumeAttachment_attachStopped(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	stopInstance := func() {
		conn := testAccProvider.Meta().(*AWSClient).ec2conn

		_, err := conn.StopInstances(&ec2.StopInstancesInput{
			InstanceIds: []*string{i.InstanceId},
		})
		if err != nil {
			t.Fatalf("error stopping instance (%s): %s", aws.StringValue(i.InstanceId), err)
		}

		stateConf := &resource.StateChangeConf{
			Pending:    []string{ec2.InstanceStateNamePending, ec2.InstanceStateNameRunning, ec2.InstanceStateNameStopping},
			Target:     []string{ec2.InstanceStateNameStopped},
			Refresh:    InstanceStateRefreshFunc(conn, *i.InstanceId, []string{}),
			Timeout:    10 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		_, err = stateConf.WaitForState()
		if err != nil {
			t.Fatalf("Error waiting for instance(%s) to stop: %s", *i.InstanceId, err)
		}
	}

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfigBase(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists("aws_instance.test", &i),
				),
			},
			{
				PreConfig: stopInstance,
				Config:    testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "device_name", "/dev/sdh"),
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSVolumeAttachment_update(t *testing.T) {
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentUpdateConfig(rName, false),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "force_detach", "false"),
					resource.TestCheckResourceAttr(resourceName, "skip_destroy", "false"),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"force_detach", // attribute only used on resource deletion
					"skip_destroy", // attribute only used on resource deletion
				},
			},
			{
				Config: testAccVolumeAttachmentUpdateConfig(rName, true),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "force_detach", "true"),
					resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"),
				),
			},
			{
				ResourceName:      "aws_volume_attachment.ebs_att",
				ImportState:       true,
				ImportStateIdFunc: testAccAWSVolumeAttachmentImportStateIDFunc("aws_volume_attachment.ebs_att"),
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"force_detach", // attribute only used on resource deletion
					"skip_destroy", // attribute only used on resource deletion
				},
			},
		},
	})
}

func TestAccAWSVolumeAttachment_disappears(t *testing.T) {
	var i ec2.Instance
	var v ec2.Volume
	resourceName := "aws_volume_attachment.test"
	rName := acctest.RandomWithPrefix("tf-acc-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckVolumeAttachmentDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccVolumeAttachmentConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckInstanceExists("aws_instance.test", &i),
					testAccCheckVolumeExists("aws_ebs_volume.test", &v),
					testAccCheckVolumeAttachmentExists(resourceName, &i, &v),
					testAccCheckVolumeAttachmentDisappears(resourceName, &i, &v),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckVolumeAttachmentExists(n string, i *ec2.Instance, v *ec2.Volume) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		for _, b := range i.BlockDeviceMappings {
			if rs.Primary.Attributes["device_name"] == *b.DeviceName {
				if b.Ebs.VolumeId != nil &&
					rs.Primary.Attributes["volume_id"] == *b.Ebs.VolumeId &&
					rs.Primary.Attributes["volume_id"] == *v.VolumeId {
					// pass
					return nil
				}
			}
		}

		return fmt.Errorf("Error finding instance/volume")
	}
}

func testAccCheckVolumeAttachmentDisappears(n string, i *ec2.Instance, v *ec2.Volume) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn

		opts := &ec2.DetachVolumeInput{
			Device:     aws.String(rs.Primary.Attributes["device_name"]),
			InstanceId: i.InstanceId,
			VolumeId:   v.VolumeId,
			Force:      aws.Bool(true),
		}

		_, err := conn.DetachVolume(opts)
		if err != nil {
			return err
		}

		vId := aws.StringValue(v.VolumeId)
		iId := aws.StringValue(i.InstanceId)

		stateConf := &resource.StateChangeConf{
			Pending:    []string{ec2.VolumeAttachmentStateDetaching},
			Target:     []string{ec2.VolumeAttachmentStateDetached},
			Refresh:    volumeAttachmentStateRefreshFunc(conn, rs.Primary.Attributes["device_name"], vId, iId),
			Timeout:    5 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vId, iId)
		_, err = stateConf.WaitForState()
		if err != nil {
			return fmt.Errorf(
				"Error waiting for Volume (%s) to detach from Instance (%s): %s",
				vId, iId, err)
		}

		return err
	}
}

func testAccCheckVolumeAttachmentDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).ec2conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_volume_attachment" {
			continue
		}

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(rs.Primary.Attributes["volume_id"])},
			Filters: []*ec2.Filter{
				{
					Name:   aws.String("attachment.device"),
					Values: []*string{aws.String(rs.Primary.Attributes["device_name"])},
				},
				{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(rs.Primary.Attributes["instance_id"])},
				},
			},
		}

		_, err := conn.DescribeVolumes(request)
		if err != nil {
			if isAWSErr(err, "InvalidVolume.NotFound", "") {
				return nil
			}
			return fmt.Errorf("error describing volumes (%s): %s", rs.Primary.ID, err)
		}
	}
	return nil
}

func testAccVolumeAttachmentInstanceOnlyConfigBase(rName string) string {
	return fmt.Sprintf(`
data "aws_availability_zones" "available" {
  state = "available"

  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

resource "aws_instance" "test" {
  ami               = "ami-21f78e11"
  availability_zone = "${data.aws_availability_zones.available.names[0]}"
  instance_type     = "t1.micro"

  tags = {
    Name = %[1]q
  }
}
`, rName)
}

func testAccVolumeAttachmentConfigBase(rName string) string {
	return testAccVolumeAttachmentInstanceOnlyConfigBase(rName) + fmt.Sprintf(`
resource "aws_ebs_volume" "test" {
  availability_zone = "${data.aws_availability_zones.available.names[0]}"
  size              = 1

  tags = {
    Name = %[1]q
  }
}
`, rName)
}

func testAccVolumeAttachmentConfig(rName string) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
resource "aws_volume_attachment" "test" {
  device_name = "/dev/sdh"
  volume_id   = "${aws_ebs_volume.test.id}"
  instance_id = "${aws_instance.test.id}"
}
`)
}

func testAccVolumeAttachmentConfigSkipDestroy(rName string) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
data "aws_ebs_volume" "test" {
  filter {
    name   = "size"
    values = ["${aws_ebs_volume.test.size}"]
  }
  filter {
    name   = "availability-zone"
    values = ["${aws_ebs_volume.test.availability_zone}"]
  }
  filter {
    name   = "tag:Name"
    values = ["%[1]s"]
  }
}

resource "aws_volume_attachment" "test" {
  device_name  = "/dev/sdh"
  volume_id    = "${data.aws_ebs_volume.test.id}"
  instance_id  = "${aws_instance.test.id}"
  skip_destroy = true
}
`, rName)
}

func testAccVolumeAttachmentUpdateConfig(rName string, detach bool) string {
	return testAccVolumeAttachmentConfigBase(rName) + fmt.Sprintf(`
resource "aws_volume_attachment" "test" {
  device_name  = "/dev/sdh"
  volume_id    = "${aws_ebs_volume.test.id}"
  instance_id  = "${aws_instance.test.id}"
  force_detach = %[1]t
  skip_destroy = %[1]t
}
`, detach)
}

func testAccAWSVolumeAttachmentImportStateIDFunc(resourceName string) resource.ImportStateIdFunc {
	return func(s *terraform.State) (string, error) {
		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return "", fmt.Errorf("Not found: %s", resourceName)
		}
		return fmt.Sprintf("%s:%s:%s", rs.Primary.Attributes["device_name"], rs.Primary.Attributes["volume_id"], rs.Primary.Attributes["instance_id"]), nil
	}
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"github.com/google/go-querystring/query"
	"github.com/jochasinga/requests"
)

type Opts struct {
	APICode string `url:"api_code,omitempty"`
}

type OptsEncoder interface {
	EncodeOpts() string
}

type API struct {
	BaseURL   string
	Endpoints map[string]string
}

func (api API) Get(endpoint string) (string, error) {
	url := api.BaseURL + api.Endpoints[endpoint]
	// fmt.Printf("Get %s\n", url)
	res, err := requests.Get(url)
	if res.Response.StatusCode != 200 {
		return "", errors.New(res.String())
	}
	return res.String(), err
}

func (api API) GetWithOpts(endpoint string, opts string) (string, error) {
	url := api.BaseURL + api.Endpoints[endpoint] + opts
	// fmt.Printf("GetWithOpts %s\n", url)
	res, err := requests.Get(url)
	if res.Response.StatusCode != 200 {
		return "", errors.New(res.String())
	}
	return res.String(), err
}

func (API) EncodeOpts(opts interface{}) string {
	v, err := query.Values(opts)
	if err != nil {
		panic(err)
	}
	encodedOpts := v.Encode()
	return encodedOpts
}
<commit_msg>Removes unrequired interface<commit_after>package api

import (
	"errors"
	"github.com/google/go-querystring/query"
	"github.com/jochasinga/requests"
)

type Opts struct {
	APICode string `url:"api_code,omitempty"`
}

type API struct {
	BaseURL   string
	Endpoints map[string]string
}

func (api API) Get(endpoint string) (string, error) {
	url := api.BaseURL + api.Endpoints[endpoint]
	// fmt.Printf("Get %s\n", url)
	res, err := requests.Get(url)
	if res.Response.StatusCode != 200 {
		return "", errors.New(res.String())
	}
	return res.String(), err
}

func (api API) GetWithOpts(endpoint string, opts string) (string, error) {
	url := api.BaseURL + api.Endpoints[endpoint] + opts
	// fmt.Printf("GetWithOpts %s\n", url)
	res, err := requests.Get(url)
	if res.Response.StatusCode != 200 {
		return "", errors.New(res.String())
	}
	return res.String(), err
}

func (API) EncodeOpts(opts interface{}) string {
	v, err := query.Values(opts)
	if err != nil {
		panic(err)
	}
	encodedOpts := v.Encode()
	return encodedOpts
}
<|endoftext|>