<commit_before>package hpeg

import "strings"

var (
	AnimalNames             []string
	AnimalPhrases           []string
	AnimalComplementPhrases []string
	AnimalTitles            []string
)

func init() {
	AnimalNames = []string{
		"Potam l'hippopotame",
		"Sophie la girafe",
		"Hector le castor",
		"Mireille l'abeille",
		"Jordy le colibri",
		"Momo le caribou",
		"Babar l'éléphant",
		"Sonic le hérisson",
		"Denver le dernier dinosaure",
		"Victor le tyrannosaure",
	}
	AnimalPhrases = []string{
		"Il y avait :NAME:",
		":NAME: aimait bien se promener",
		":NAME: avait beaucoup d'amis",
		":NAME: était un aventurier",
		":NAME: n'a peur de rien",
		":NAME: a peur de tout",
		"Pour :NAME:, une bonne journée se passe avec des amis",
	}
	AnimalComplementPhrases = []string{
		"sa passion est de manger des pommes",
		"son repas préféré est le gouter",
		"son plus grand rêve est d'aller voir un film au cinéma",
		"ses parents sont les plus gentils du monde",
		"son repas favoris est la pizza",
		"son matelas est moelleux",
	}
	AnimalTitles = []string{
		"Les fabuleuses aventures de :NAME:",
		":NAME: à la plage",
		":NAME: à la piscine",
		":NAME: à la ferme",
	}
}

type Animal struct {
	Name string
}

func (a Animal) GetPhrase() string {
	phrase := PickAndDelete(&AnimalPhrases)
	complement := PickAndDelete(&AnimalComplementPhrases)
	phrase += ", " + complement

	phrase = strings.Replace(phrase, ":NAME:", a.Name, -1)
	return phrase
}

func (a Animal) GetTitle() string {
	title := PickAndDelete(&AnimalTitles)
	title = strings.Replace(title, ":NAME:", a.Name, -1)
	return title
}

func NewAnimal() Animal {
	return Animal{
		Name: PickAndDelete(&AnimalNames),
	}
}
<commit_msg>feat: add a way of resetting the generator<commit_after>package hpeg

import "strings"

var (
	AnimalNames             []string
	AnimalPhrases           []string
	AnimalComplementPhrases []string
	AnimalTitles            []string
)

func init() {
	Reset()
}

func Reset() {
	AnimalNames = []string{
		"Potam l'hippopotame",
		"Sophie la girafe",
		"Hector le castor",
		"Mireille l'abeille",
		"Jordy le colibri",
		"Momo le caribou",
		"Babar l'éléphant",
		"Sonic le hérisson",
		"Denver le dernier dinosaure",
		"Victor le tyrannosaure",
	}
	AnimalPhrases = []string{
		"Il y avait :NAME:",
		":NAME: aimait bien se promener",
		":NAME: avait beaucoup d'amis",
		":NAME: était un aventurier",
		":NAME: n'a peur de rien",
		":NAME: a peur de tout",
		"Pour :NAME:, une bonne journée se passe avec des amis",
	}
	AnimalComplementPhrases = []string{
		"sa passion est de manger des pommes",
		"son repas préféré est le gouter",
		"son plus grand rêve est d'aller voir un film au cinéma",
		"ses parents sont les plus gentils du monde",
		"son repas favoris est la pizza",
		"son matelas est moelleux",
	}
	AnimalTitles = []string{
		"Les fabuleuses aventures de :NAME:",
		":NAME: à la plage",
		":NAME: à la piscine",
		":NAME: à la ferme",
	}
}

type Animal struct {
	Name string
}

func (a Animal) GetPhrase() string {
	phrase := PickAndDelete(&AnimalPhrases)
	complement := PickAndDelete(&AnimalComplementPhrases)
	phrase += ", " + complement

	phrase = strings.Replace(phrase, ":NAME:", a.Name, -1)
	return phrase
}

func (a Animal) GetTitle() string {
	title := PickAndDelete(&AnimalTitles)
	title = strings.Replace(title, ":NAME:", a.Name, -1)
	return title
}

func NewAnimal() Animal {
	return Animal{
		Name: PickAndDelete(&AnimalNames),
	}
}
<|endoftext|>


<commit_before>package api

import (
	"sort"
	"time"
)

// CSIVolumes is used to query the top level csi volumes
type CSIVolumes struct {
	client *Client
}

// CSIVolumes returns a handle on the CSIVolumes endpoint
func (c *Client) CSIVolumes() *CSIVolumes {
	return &CSIVolumes{client: c}
}

// List returns all CSI volumes
func (v *CSIVolumes) List(q *QueryOptions) ([]*CSIVolumeListStub, *QueryMeta, error) {
	var resp []*CSIVolumeListStub
	qm, err := v.client.query("/v1/volumes?type=csi", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(CSIVolumeIndexSort(resp))
	return resp, qm, nil
}

// PluginList returns all CSI volumes for the specified plugin id
func (v *CSIVolumes) PluginList(pluginID string) ([]*CSIVolumeListStub, *QueryMeta, error) {
	return v.List(&QueryOptions{Prefix: pluginID})
}

// Info is used to retrieve a single CSIVolume
func (v *CSIVolumes) Info(id string, q *QueryOptions) (*CSIVolume, *QueryMeta, error) {
	var resp CSIVolume
	qm, err := v.client.query("/v1/volume/csi/"+id, &resp, q)
	if err != nil {
		return nil, nil, err
	}

	// Cleanup allocation representation for the ui
	resp.allocs()

	return &resp, qm, nil
}

func (v *CSIVolumes) Register(vol *CSIVolume, w *WriteOptions) (*WriteMeta, error) {
	req := CSIVolumeRegisterRequest{
		Volumes: []*CSIVolume{vol},
	}
	meta, err := v.client.write("/v1/volume/csi/"+vol.ID, req, nil, w)
	return meta, err
}

func (v *CSIVolumes) Deregister(id string, w *WriteOptions) error {
	_, err := v.client.delete("/v1/volume/csi/"+id, nil, w)
	return err
}

// CSIVolumeAttachmentMode duplicated in nomad/structs/csi.go
type CSIVolumeAttachmentMode string

const (
	CSIVolumeAttachmentModeUnknown     CSIVolumeAttachmentMode = ""
	CSIVolumeAttachmentModeBlockDevice CSIVolumeAttachmentMode = "block-device"
	CSIVolumeAttachmentModeFilesystem  CSIVolumeAttachmentMode = "file-system"
)

// CSIVolumeAccessMode duplicated in nomad/structs/csi.go
type CSIVolumeAccessMode string

const (
	CSIVolumeAccessModeUnknown CSIVolumeAccessMode = ""

	CSIVolumeAccessModeSingleNodeReader CSIVolumeAccessMode = "single-node-reader-only"
	CSIVolumeAccessModeSingleNodeWriter CSIVolumeAccessMode = "single-node-writer"

	CSIVolumeAccessModeMultiNodeReader       CSIVolumeAccessMode = "multi-node-reader-only"
	CSIVolumeAccessModeMultiNodeSingleWriter CSIVolumeAccessMode = "multi-node-single-writer"
	CSIVolumeAccessModeMultiNodeMultiWriter  CSIVolumeAccessMode = "multi-node-multi-writer"
)

type CSIMountOptions struct {
	FSType       string   `hcl:"fs_type"`
	MountFlags   []string `hcl:"mount_flags"`
	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"` // report unexpected keys
}

// CSIVolume is used for serialization, see also nomad/structs/csi.go
type CSIVolume struct {
	ID             string
	Name           string
	ExternalID     string `hcl:"external_id"`
	Namespace      string
	Topologies     []*CSITopology
	AccessMode     CSIVolumeAccessMode     `hcl:"access_mode"`
	AttachmentMode CSIVolumeAttachmentMode `hcl:"attachment_mode"`
	MountOptions   *CSIMountOptions        `hcl:"mount_options"`

	// Allocations, tracking claim status
	ReadAllocs  map[string]*Allocation
	WriteAllocs map[string]*Allocation

	// Combine structs.{Read,Write}Allocs
	Allocations []*AllocationListStub

	// Schedulable is true if all the denormalized plugin health fields are true
	Schedulable         bool
	PluginID            string `hcl:"plugin_id"`
	Provider            string
	ProviderVersion     string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	ResourceExhausted   time.Time

	CreateIndex uint64
	ModifyIndex uint64

	// ExtraKeysHCL is used by the hcl parser to report unexpected keys
	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
}

// allocs is called after we query the volume (creating this CSIVolume struct) to collapse
// allocations for the UI
func (v *CSIVolume) allocs() {
	for _, a := range v.WriteAllocs {
		if a != nil {
			v.Allocations = append(v.Allocations, a.Stub())
		}
	}
	for _, a := range v.ReadAllocs {
		if a != nil {
			v.Allocations = append(v.Allocations, a.Stub())
		}
	}
}

type CSIVolumeIndexSort []*CSIVolumeListStub

func (v CSIVolumeIndexSort) Len() int {
	return len(v)
}

func (v CSIVolumeIndexSort) Less(i, j int) bool {
	return v[i].CreateIndex > v[j].CreateIndex
}

func (v CSIVolumeIndexSort) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// CSIVolumeListStub omits allocations. See also nomad/structs/csi.go
type CSIVolumeListStub struct {
	ID                  string
	Namespace           string
	Name                string
	ExternalID          string
	Topologies          []*CSITopology
	AccessMode          CSIVolumeAccessMode
	AttachmentMode      CSIVolumeAttachmentMode
	MountOptions        *CSIMountOptions
	Schedulable         bool
	PluginID            string
	Provider            string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	ResourceExhausted   time.Time

	CreateIndex uint64
	ModifyIndex uint64
}

type CSIVolumeRegisterRequest struct {
	Volumes []*CSIVolume
	WriteRequest
}

type CSIVolumeDeregisterRequest struct {
	VolumeIDs []string
	WriteRequest
}

// CSI Plugins are jobs with plugin specific data
type CSIPlugins struct {
	client *Client
}

type CSIPlugin struct {
	ID                 string
	Provider           string
	Version            string
	ControllerRequired bool
	// Map Node.ID to CSIInfo fingerprint results
	Controllers        map[string]*CSIInfo
	Nodes              map[string]*CSIInfo
	Allocations        []*AllocationListStub
	ControllersHealthy int
	NodesHealthy       int
	CreateIndex        uint64
	ModifyIndex        uint64
}

type CSIPluginListStub struct {
	ID                  string
	Provider            string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	CreateIndex         uint64
	ModifyIndex         uint64
}

type CSIPluginIndexSort []*CSIPluginListStub

func (v CSIPluginIndexSort) Len() int {
	return len(v)
}

func (v CSIPluginIndexSort) Less(i, j int) bool {
	return v[i].CreateIndex > v[j].CreateIndex
}

func (v CSIPluginIndexSort) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// CSIPlugins returns a handle on the CSIPlugins endpoint
func (c *Client) CSIPlugins() *CSIPlugins {
	return &CSIPlugins{client: c}
}

// List returns all CSI plugins
func (v *CSIPlugins) List(q *QueryOptions) ([]*CSIPluginListStub, *QueryMeta, error) {
	var resp []*CSIPluginListStub
	qm, err := v.client.query("/v1/plugins?type=csi", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(CSIPluginIndexSort(resp))
	return resp, qm, nil
}

// Info is used to retrieve a single CSI Plugin Job
func (v *CSIPlugins) Info(id string, q *QueryOptions) (*CSIPlugin, *QueryMeta, error) {
	var resp *CSIPlugin
	qm, err := v.client.query("/v1/plugin/csi/"+id, &resp, q)
	if err != nil {
		return nil, nil, err
	}
	return resp, qm, nil
}
<commit_msg>Add ControllersExpected to the PluginListStub<commit_after>package api

import (
	"sort"
	"time"
)

// CSIVolumes is used to query the top level csi volumes
type CSIVolumes struct {
	client *Client
}

// CSIVolumes returns a handle on the CSIVolumes endpoint
func (c *Client) CSIVolumes() *CSIVolumes {
	return &CSIVolumes{client: c}
}

// List returns all CSI volumes
func (v *CSIVolumes) List(q *QueryOptions) ([]*CSIVolumeListStub, *QueryMeta, error) {
	var resp []*CSIVolumeListStub
	qm, err := v.client.query("/v1/volumes?type=csi", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(CSIVolumeIndexSort(resp))
	return resp, qm, nil
}

// PluginList returns all CSI volumes for the specified plugin id
func (v *CSIVolumes) PluginList(pluginID string) ([]*CSIVolumeListStub, *QueryMeta, error) {
	return v.List(&QueryOptions{Prefix: pluginID})
}

// Info is used to retrieve a single CSIVolume
func (v *CSIVolumes) Info(id string, q *QueryOptions) (*CSIVolume, *QueryMeta, error) {
	var resp CSIVolume
	qm, err := v.client.query("/v1/volume/csi/"+id, &resp, q)
	if err != nil {
		return nil, nil, err
	}

	// Cleanup allocation representation for the ui
	resp.allocs()

	return &resp, qm, nil
}

func (v *CSIVolumes) Register(vol *CSIVolume, w *WriteOptions) (*WriteMeta, error) {
	req := CSIVolumeRegisterRequest{
		Volumes: []*CSIVolume{vol},
	}
	meta, err := v.client.write("/v1/volume/csi/"+vol.ID, req, nil, w)
	return meta, err
}

func (v *CSIVolumes) Deregister(id string, w *WriteOptions) error {
	_, err := v.client.delete("/v1/volume/csi/"+id, nil, w)
	return err
}

// CSIVolumeAttachmentMode duplicated in nomad/structs/csi.go
type CSIVolumeAttachmentMode string

const (
	CSIVolumeAttachmentModeUnknown     CSIVolumeAttachmentMode = ""
	CSIVolumeAttachmentModeBlockDevice CSIVolumeAttachmentMode = "block-device"
	CSIVolumeAttachmentModeFilesystem  CSIVolumeAttachmentMode = "file-system"
)

// CSIVolumeAccessMode duplicated in nomad/structs/csi.go
type CSIVolumeAccessMode string

const (
	CSIVolumeAccessModeUnknown CSIVolumeAccessMode = ""

	CSIVolumeAccessModeSingleNodeReader CSIVolumeAccessMode = "single-node-reader-only"
	CSIVolumeAccessModeSingleNodeWriter CSIVolumeAccessMode = "single-node-writer"

	CSIVolumeAccessModeMultiNodeReader       CSIVolumeAccessMode = "multi-node-reader-only"
	CSIVolumeAccessModeMultiNodeSingleWriter CSIVolumeAccessMode = "multi-node-single-writer"
	CSIVolumeAccessModeMultiNodeMultiWriter  CSIVolumeAccessMode = "multi-node-multi-writer"
)

type CSIMountOptions struct {
	FSType       string   `hcl:"fs_type"`
	MountFlags   []string `hcl:"mount_flags"`
	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"` // report unexpected keys
}

// CSIVolume is used for serialization, see also nomad/structs/csi.go
type CSIVolume struct {
	ID             string
	Name           string
	ExternalID     string `hcl:"external_id"`
	Namespace      string
	Topologies     []*CSITopology
	AccessMode     CSIVolumeAccessMode     `hcl:"access_mode"`
	AttachmentMode CSIVolumeAttachmentMode `hcl:"attachment_mode"`
	MountOptions   *CSIMountOptions        `hcl:"mount_options"`

	// Allocations, tracking claim status
	ReadAllocs  map[string]*Allocation
	WriteAllocs map[string]*Allocation

	// Combine structs.{Read,Write}Allocs
	Allocations []*AllocationListStub

	// Schedulable is true if all the denormalized plugin health fields are true
	Schedulable         bool
	PluginID            string `hcl:"plugin_id"`
	Provider            string
	ProviderVersion     string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	ResourceExhausted   time.Time

	CreateIndex uint64
	ModifyIndex uint64

	// ExtraKeysHCL is used by the hcl parser to report unexpected keys
	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
}

// allocs is called after we query the volume (creating this CSIVolume struct) to collapse
// allocations for the UI
func (v *CSIVolume) allocs() {
	for _, a := range v.WriteAllocs {
		if a != nil {
			v.Allocations = append(v.Allocations, a.Stub())
		}
	}
	for _, a := range v.ReadAllocs {
		if a != nil {
			v.Allocations = append(v.Allocations, a.Stub())
		}
	}
}

type CSIVolumeIndexSort []*CSIVolumeListStub

func (v CSIVolumeIndexSort) Len() int {
	return len(v)
}

func (v CSIVolumeIndexSort) Less(i, j int) bool {
	return v[i].CreateIndex > v[j].CreateIndex
}

func (v CSIVolumeIndexSort) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// CSIVolumeListStub omits allocations. See also nomad/structs/csi.go
type CSIVolumeListStub struct {
	ID                  string
	Namespace           string
	Name                string
	ExternalID          string
	Topologies          []*CSITopology
	AccessMode          CSIVolumeAccessMode
	AttachmentMode      CSIVolumeAttachmentMode
	MountOptions        *CSIMountOptions
	Schedulable         bool
	PluginID            string
	Provider            string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	ResourceExhausted   time.Time

	CreateIndex uint64
	ModifyIndex uint64
}

type CSIVolumeRegisterRequest struct {
	Volumes []*CSIVolume
	WriteRequest
}

type CSIVolumeDeregisterRequest struct {
	VolumeIDs []string
	WriteRequest
}

// CSI Plugins are jobs with plugin specific data
type CSIPlugins struct {
	client *Client
}

type CSIPlugin struct {
	ID                 string
	Provider           string
	Version            string
	ControllerRequired bool
	// Map Node.ID to CSIInfo fingerprint results
	Controllers         map[string]*CSIInfo
	Nodes               map[string]*CSIInfo
	Allocations         []*AllocationListStub
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	CreateIndex         uint64
	ModifyIndex         uint64
}

type CSIPluginListStub struct {
	ID                  string
	Provider            string
	ControllerRequired  bool
	ControllersHealthy  int
	ControllersExpected int
	NodesHealthy        int
	NodesExpected       int
	CreateIndex         uint64
	ModifyIndex         uint64
}

type CSIPluginIndexSort []*CSIPluginListStub

func (v CSIPluginIndexSort) Len() int {
	return len(v)
}

func (v CSIPluginIndexSort) Less(i, j int) bool {
	return v[i].CreateIndex > v[j].CreateIndex
}

func (v CSIPluginIndexSort) Swap(i, j int) {
	v[i], v[j] = v[j], v[i]
}

// CSIPlugins returns a handle on the CSIPlugins endpoint
func (c *Client) CSIPlugins() *CSIPlugins {
	return &CSIPlugins{client: c}
}

// List returns all CSI plugins
func (v *CSIPlugins) List(q *QueryOptions) ([]*CSIPluginListStub, *QueryMeta, error) {
	var resp []*CSIPluginListStub
	qm, err := v.client.query("/v1/plugins?type=csi", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(CSIPluginIndexSort(resp))
	return resp, qm, nil
}

// Info is used to retrieve a single CSI Plugin Job
func (v *CSIPlugins) Info(id string, q *QueryOptions) (*CSIPlugin, *QueryMeta, error) {
	var resp *CSIPlugin
	qm, err := v.client.query("/v1/plugin/csi/"+id, &resp, q)
	if err != nil {
		return nil, nil, err
	}
	return resp, qm, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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

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

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

package blobref

import (
	"crypto"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var _ = log.Printf

type Fetcher interface {
	// Fetch returns a blob.  If the blob is not found then
	// os.ENOENT should be returned for the error (not a wrapped
	// error with a ENOENT inside)
	Fetch(*BlobRef) (file ReadSeekCloser, size int64, err os.Error)
}

type StreamingFetcher interface {
	// Fetch returns a blob.  If the blob is not found then
	// os.ENOENT should be returned for the error (not a wrapped
	// error with a ENOENT inside)
	FetchStreaming(*BlobRef) (file io.ReadCloser, size int64, err os.Error)
}

func NewSerialFetcher(fetchers ...Fetcher) Fetcher {
	return &serialFetcher{fetchers}
}

func NewSerialStreamingFetcher(fetchers ...StreamingFetcher) StreamingFetcher {
	return &serialStreamingFetcher{fetchers}
}

func NewSimpleDirectoryFetcher(dir string) *DirFetcher {
	return &DirFetcher{dir, "camli"}
}

func NewConfigDirFetcher() *DirFetcher {
	configDir := filepath.Join(os.Getenv("HOME"), ".camli", "keyblobs")
	return NewSimpleDirectoryFetcher(configDir)
}

type serialFetcher struct {
	fetchers []Fetcher
}

func (sf *serialFetcher) Fetch(b *BlobRef) (file ReadSeekCloser, size int64, err os.Error) {
	for _, fetcher := range sf.fetchers {
		file, size, err = fetcher.Fetch(b)
		if err == nil {
			return
		}
	}
	return

}

type serialStreamingFetcher struct {
	fetchers []StreamingFetcher
}

func (sf *serialStreamingFetcher) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	for _, fetcher := range sf.fetchers {
		file, size, err = fetcher.FetchStreaming(b)
		if err == nil {
			return
		}
	}
	return
}

type DirFetcher struct {
	directory, extension string
}

func (df *DirFetcher) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	return df.Fetch(b)
}

func (df *DirFetcher) Fetch(b *BlobRef) (file ReadSeekCloser, size int64, err os.Error) {
	fileName := fmt.Sprintf("%s/%s.%s", df.directory, b.String(), df.extension)
	var stat *os.FileInfo
	stat, err = os.Stat(fileName)
	if err != nil {
		return
	}
	file, err = os.Open(fileName)
	if err != nil {
		return
	}
	size = stat.Size
	return
}

// MemoryStore stores blobs in memory and is a Fetcher and
// StreamingFetcher. Its zero value is usable.
type MemoryStore struct {
	lk sync.Mutex
	m  map[string]string
}

func (s *MemoryStore) AddBlob(hashtype crypto.Hash, data string) (*BlobRef, os.Error) {
	if hashtype != crypto.SHA1 {
		return nil, os.NewError("blobref: unsupported hash type")
	}
	hash := hashtype.New()
	hash.Write([]byte(data))
	bstr := fmt.Sprintf("sha1-%x", hash.Sum())
	s.lk.Lock()
	defer s.lk.Unlock()
	if s.m == nil {
		s.m = make(map[string]string)
	}
	s.m[bstr] = data
	return Parse(bstr), nil
}

func (s *MemoryStore) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	s.lk.Lock()
        defer s.lk.Unlock()
	if s.m == nil {
		return nil, 0, os.ENOENT
	}
	str, ok := s.m[b.String()]
	if !ok {
		return nil, 0, os.ENOENT
	}
	return ioutil.NopCloser(strings.NewReader(str)), int64(len(str)), nil
}
<commit_msg>Add TODO to rename StreamingFetcher to be Fetcher<commit_after>/*
Copyright 2011 Google Inc.

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

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

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

package blobref

import (
	"crypto"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var _ = log.Printf

// TODO: rename StreamingFetcher to be Fetch (the common case) and
// make a new interface for SeekingFetcher (the rare case)

type Fetcher interface {
	// Fetch returns a blob.  If the blob is not found then
	// os.ENOENT should be returned for the error (not a wrapped
	// error with a ENOENT inside)
	Fetch(*BlobRef) (file ReadSeekCloser, size int64, err os.Error)
}

type StreamingFetcher interface {
	// Fetch returns a blob.  If the blob is not found then
	// os.ENOENT should be returned for the error (not a wrapped
	// error with a ENOENT inside)
	FetchStreaming(*BlobRef) (file io.ReadCloser, size int64, err os.Error)
}

func NewSerialFetcher(fetchers ...Fetcher) Fetcher {
	return &serialFetcher{fetchers}
}

func NewSerialStreamingFetcher(fetchers ...StreamingFetcher) StreamingFetcher {
	return &serialStreamingFetcher{fetchers}
}

func NewSimpleDirectoryFetcher(dir string) *DirFetcher {
	return &DirFetcher{dir, "camli"}
}

func NewConfigDirFetcher() *DirFetcher {
	configDir := filepath.Join(os.Getenv("HOME"), ".camli", "keyblobs")
	return NewSimpleDirectoryFetcher(configDir)
}

type serialFetcher struct {
	fetchers []Fetcher
}

func (sf *serialFetcher) Fetch(b *BlobRef) (file ReadSeekCloser, size int64, err os.Error) {
	for _, fetcher := range sf.fetchers {
		file, size, err = fetcher.Fetch(b)
		if err == nil {
			return
		}
	}
	return

}

type serialStreamingFetcher struct {
	fetchers []StreamingFetcher
}

func (sf *serialStreamingFetcher) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	for _, fetcher := range sf.fetchers {
		file, size, err = fetcher.FetchStreaming(b)
		if err == nil {
			return
		}
	}
	return
}

type DirFetcher struct {
	directory, extension string
}

func (df *DirFetcher) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	return df.Fetch(b)
}

func (df *DirFetcher) Fetch(b *BlobRef) (file ReadSeekCloser, size int64, err os.Error) {
	fileName := fmt.Sprintf("%s/%s.%s", df.directory, b.String(), df.extension)
	var stat *os.FileInfo
	stat, err = os.Stat(fileName)
	if err != nil {
		return
	}
	file, err = os.Open(fileName)
	if err != nil {
		return
	}
	size = stat.Size
	return
}

// MemoryStore stores blobs in memory and is a Fetcher and
// StreamingFetcher. Its zero value is usable.
type MemoryStore struct {
	lk sync.Mutex
	m  map[string]string
}

func (s *MemoryStore) AddBlob(hashtype crypto.Hash, data string) (*BlobRef, os.Error) {
	if hashtype != crypto.SHA1 {
		return nil, os.NewError("blobref: unsupported hash type")
	}
	hash := hashtype.New()
	hash.Write([]byte(data))
	bstr := fmt.Sprintf("sha1-%x", hash.Sum())
	s.lk.Lock()
	defer s.lk.Unlock()
	if s.m == nil {
		s.m = make(map[string]string)
	}
	s.m[bstr] = data
	return Parse(bstr), nil
}

func (s *MemoryStore) FetchStreaming(b *BlobRef) (file io.ReadCloser, size int64, err os.Error) {
	s.lk.Lock()
        defer s.lk.Unlock()
	if s.m == nil {
		return nil, 0, os.ENOENT
	}
	str, ok := s.m[b.String()]
	if !ok {
		return nil, 0, os.ENOENT
	}
	return ioutil.NopCloser(strings.NewReader(str)), int64(len(str)), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 HenryLee. 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.

// API automation documentation.

package thinkgo

import (
	"encoding/json"
	"fmt"
	"path"
	"strings"

	"github.com/henrylee2cn/thinkgo/swagger"
)

// register the API doc router.
func (frame *Framework) regAPIdoc() {
	frame.config.APIdoc.Path = path.Join("/", frame.config.APIdoc.Path)
	// jsonPattern := strings.TrimRight(strings.Replace(frame.config.APIdoc.Path, "/*filepath", "", -1), "/") + ".json"
	jsonPattern := "/swagger.json"
	if frame.config.APIdoc.NoLimit {
		frame.MuxAPI.NamedStaticFS("APIdoc-Swagger", frame.config.APIdoc.Path, FS(swagger.AssetFS()))
		frame.MuxAPI.NamedGET("APIdoc-Swagger-JSON", jsonPattern, newAPIdocJSONHandler())
	} else {
		allowApidoc := NewIPFilter(frame.config.APIdoc.Whitelist, frame.config.APIdoc.RealIP)
		frame.MuxAPI.NamedStaticFS("APIdoc-Swagger", frame.config.APIdoc.Path, FS(swagger.AssetFS())).Use(allowApidoc)
		frame.MuxAPI.NamedGET("APIdoc-Swagger-JSON", jsonPattern, newAPIdocJSONHandler(), allowApidoc)
	}

	tip := `APIdoc's URL path is '` + frame.config.APIdoc.Path
	if frame.config.APIdoc.NoLimit {
		frame.syslog.Criticalf(tip + `' [free access]`)
	} else if len(frame.config.APIdoc.Whitelist) == 0 {
		frame.syslog.Criticalf(tip + `' [no access]`)
	} else if frame.config.APIdoc.RealIP {
		frame.syslog.Criticalf(tip + `' [check real ip for filter]`)
	} else {
		frame.syslog.Criticalf(tip + `' [check direct ip for filter]`)
	}
}

func newAPIdocJSONHandler() HandlerFunc {
	return func(ctx *Context) error {
		if ctx.frame.apidoc == nil {
			ctx.frame.initAPIdoc(ctx.R.Host)
		}
		ctx.frame.apidoc.Schemes = []string{ctx.Scheme()}
		ctx.frame.apidoc.Host = ctx.R.Host
		return ctx.JSON(200, ctx.frame.apidoc)
	}
}

var apiStaticParam = &swagger.Parameter{
	In:          "path",
	Name:        "filepath",
	Type:        swagger.ParamType("*"),
	Description: "any static path or file",
	Required:    true,
	Format:      fmt.Sprintf("%T", "*"),
	Default:     "",
}

func (frame *Framework) initAPIdoc(host string) {
	rootMuxAPI := frame.MuxAPI
	rootTag := &swagger.Tag{
		Name:        rootMuxAPI.Path(),
		Description: apiTagDesc(rootMuxAPI.Name()),
	}
	frame.apidoc = &swagger.Swagger{
		Version: swagger.Version,
		Info: &swagger.Info{
			Title:          strings.Title(frame.Name()) + " API",
			ApiVersion:     frame.Version(),
			Description:    frame.config.APIdoc.Desc,
			Contact:        &swagger.Contact{Email: frame.config.APIdoc.Email},
			TermsOfService: frame.config.APIdoc.TermsURL,
			License: &swagger.License{
				Name: frame.config.APIdoc.License,
				Url:  frame.config.APIdoc.LicenseURL,
			},
		},
		Host:     host,
		BasePath: "/",
		Tags:     []*swagger.Tag{rootTag},
		Schemes:  []string{"http", "https"},
		Paths:    map[string]map[string]*swagger.Opera{},
		// SecurityDefinitions: map[string]map[string]interface{}{},
		// Definitions:         map[string]Definition{},
		// ExternalDocs:        map[string]string{},
	}

	for _, child := range rootMuxAPI.Children() {
		if !child.IsGroup() {
			addpath(child, rootTag)
			continue
		}
		childTag := &swagger.Tag{
			Name:        child.Path(),
			Description: apiTagDesc(child.Name()),
		}
		frame.apidoc.Tags = append(frame.apidoc.Tags, childTag)
		for _, grandson := range child.Children() {
			if !grandson.IsGroup() {
				addpath(grandson, childTag)
				continue
			}
			grandsonTag := &swagger.Tag{
				Name:        grandson.Path(),
				Description: apiTagDesc(grandson.Name()),
			}
			frame.apidoc.Tags = append(frame.apidoc.Tags, grandsonTag)
			for _, progeny := range grandson.Progeny() {
				if !progeny.IsGroup() {
					addpath(progeny, grandsonTag)
					continue
				}
			}
		}
	}
}

// 添加API操作项
func addpath(mux *MuxAPI, tag *swagger.Tag) {
	operas := map[string]*swagger.Opera{}
	pid := apiCreatePath(mux.Path())
	summary := apiSummary(mux.Name())
	desc := apiDesc(mux.Notes())
	for _, method := range mux.Methods() {
		if method == "CONNECT" || method == "TRACE" {
			continue
		}
		// if method == "WS" {
		// 	method = "GET"
		// }
		o := &swagger.Opera{
			Tags:        []string{tag.Name},
			Summary:     summary,
			Description: desc,
			OperationId: pid + "-" + method,
			Consumes:    swagger.CommonMIMETypes,
			Produces:    swagger.CommonMIMETypes,
			Responses:   make(map[string]*swagger.Resp, 1),
			// Security: []map[string][]string{},
		}

		for _, param := range mux.ParamInfos() {
			p := &swagger.Parameter{
				In:          param.In,
				Name:        param.Name,
				Description: param.Desc,
				Required:    param.Required,
				// Items:       &Items{},
				// Schema:      &Schema{},
			}
			typ := swagger.ParamType(param.Model)
			switch p.In {
			case "cookie":
				continue
			default:
				switch typ {
				case "file":
					o.Consumes = []string{"multipart/form-data"}
					p.Type = typ

				case "array":
					subtyp, first, count := swagger.SliceInfo(param.Model)
					switch subtyp {
					case "object":
						ref := apiDefinitions(mux, p.Name, method, param.Model)
						p.Schema = &swagger.Schema{
							Type: typ,
							Items: &swagger.Items{
								Ref: "#/definitions/" + ref,
							},
						}

					default:
						p.Type = typ
						p.Items = &swagger.Items{
							Type:    subtyp,
							Default: first,
						}
						if count > 0 {
							p.Items.Enum = param.Model
						}
						p.CollectionFormat = "multi"
					}

				case "object":
					ref := apiDefinitions(mux, p.Name, method, param.Model)
					p.Schema = &swagger.Schema{
						Type: typ,
						Ref:  "#/definitions/" + ref,
					}

				default:
					p.Type = typ
					p.Format = fmt.Sprintf("%T", param.Model)
					p.Default = param.Model
				}
			}

			o.Parameters = append(o.Parameters, p)
		}

		// static file
		if strings.HasSuffix(pid, "/{filepath}") {
			o.Parameters = append(o.Parameters, apiStaticParam)
		}

		operas[strings.ToLower(method)] = o
	}
	if _operas, ok := mux.frame.apidoc.Paths[pid]; ok {
		for k, v := range operas {
			_operas[k] = v
		}
	} else {
		mux.frame.apidoc.Paths[pid] = operas
	}
}

func apiDefinitions(mux *MuxAPI, pname, method string, format interface{}) (ref string) {
	upath := mux.Path()
	ref = strings.Replace(path.Join(upath[1:], pname, method), "/", "@", -1)
	def := &swagger.Definition{
		Type: "object",
		Xml:  &swagger.Xml{Name: ref},
	}
	def.Properties = swagger.CreateProperties(format)
	if mux.frame.apidoc.Definitions == nil {
		mux.frame.apidoc.Definitions = map[string]*swagger.Definition{}
	}
	mux.frame.apidoc.Definitions[ref] = def
	return
}

func apiCreatePath(u string) string {
	a := strings.Split(u, "/*")
	if len(a) > 1 {
		u = path.Join(a[0], "{filepath}")
	}
	s := strings.Split(u, "/:")
	p := s[0]
	if len(s) == 1 {
		return p
	}
	for _, param := range s[1:] {
		p = path.Join(p, "{"+param+"}")
	}
	return p
}

func apiTagDesc(desc string) string {
	return strings.TrimSpace(desc)
}

func apiSummary(desc string) string {
	return strings.TrimSpace(strings.Split(strings.TrimSpace(desc), "\n")[0])
}

func apiDesc(notes []Notes) string {
	var desc string
	count := len(notes)
	for i, n := range notes {
		if count > 1 {
			desc += fmt.Sprintf("\n\n======================= Handler %d =======================", i)
		}
		if n.Note != "" {
			desc += fmt.Sprintf("\nNote: %s", strings.TrimSpace(n.Note))
		}
		if n.Return != nil {
			b, _ := json.MarshalIndent(n.Return, "", "  ")
			desc += fmt.Sprintf("\nReturn: %s", string(b))
		} else {
			desc += "\nReturn:"
		}
	}
	if desc != "" {
		return "
" + strings.TrimSpace(desc) + "
"
	}
	return ""
}
<commit_msg>Optimize api document notes<commit_after>// Copyright 2016 HenryLee. 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.

// API automation documentation.

package thinkgo

import (
	"encoding/json"
	"fmt"
	"path"
	"strings"

	"github.com/henrylee2cn/thinkgo/swagger"
)

// register the API doc router.
func (frame *Framework) regAPIdoc() {
	frame.config.APIdoc.Path = path.Join("/", frame.config.APIdoc.Path)
	// jsonPattern := strings.TrimRight(strings.Replace(frame.config.APIdoc.Path, "/*filepath", "", -1), "/") + ".json"
	jsonPattern := "/swagger.json"
	if frame.config.APIdoc.NoLimit {
		frame.MuxAPI.NamedStaticFS("APIdoc-Swagger", frame.config.APIdoc.Path, FS(swagger.AssetFS()))
		frame.MuxAPI.NamedGET("APIdoc-Swagger-JSON", jsonPattern, newAPIdocJSONHandler())
	} else {
		allowApidoc := NewIPFilter(frame.config.APIdoc.Whitelist, frame.config.APIdoc.RealIP)
		frame.MuxAPI.NamedStaticFS("APIdoc-Swagger", frame.config.APIdoc.Path, FS(swagger.AssetFS())).Use(allowApidoc)
		frame.MuxAPI.NamedGET("APIdoc-Swagger-JSON", jsonPattern, newAPIdocJSONHandler(), allowApidoc)
	}

	tip := `APIdoc's URL path is '` + frame.config.APIdoc.Path
	if frame.config.APIdoc.NoLimit {
		frame.syslog.Criticalf(tip + `' [free access]`)
	} else if len(frame.config.APIdoc.Whitelist) == 0 {
		frame.syslog.Criticalf(tip + `' [no access]`)
	} else if frame.config.APIdoc.RealIP {
		frame.syslog.Criticalf(tip + `' [check real ip for filter]`)
	} else {
		frame.syslog.Criticalf(tip + `' [check direct ip for filter]`)
	}
}

func newAPIdocJSONHandler() HandlerFunc {
	return func(ctx *Context) error {
		if ctx.frame.apidoc == nil {
			ctx.frame.initAPIdoc(ctx.R.Host)
		}
		ctx.frame.apidoc.Schemes = []string{ctx.Scheme()}
		ctx.frame.apidoc.Host = ctx.R.Host
		return ctx.JSON(200, ctx.frame.apidoc)
	}
}

var apiStaticParam = &swagger.Parameter{
	In:          "path",
	Name:        "filepath",
	Type:        swagger.ParamType("*"),
	Description: "any static path or file",
	Required:    true,
	Format:      fmt.Sprintf("%T", "*"),
	Default:     "",
}

func (frame *Framework) initAPIdoc(host string) {
	rootMuxAPI := frame.MuxAPI
	rootTag := &swagger.Tag{
		Name:        rootMuxAPI.Path(),
		Description: apiTagDesc(rootMuxAPI.Name()),
	}
	frame.apidoc = &swagger.Swagger{
		Version: swagger.Version,
		Info: &swagger.Info{
			Title:          strings.Title(frame.Name()) + " API",
			ApiVersion:     frame.Version(),
			Description:    frame.config.APIdoc.Desc,
			Contact:        &swagger.Contact{Email: frame.config.APIdoc.Email},
			TermsOfService: frame.config.APIdoc.TermsURL,
			License: &swagger.License{
				Name: frame.config.APIdoc.License,
				Url:  frame.config.APIdoc.LicenseURL,
			},
		},
		Host:     host,
		BasePath: "/",
		Tags:     []*swagger.Tag{rootTag},
		Schemes:  []string{"http", "https"},
		Paths:    map[string]map[string]*swagger.Opera{},
		// SecurityDefinitions: map[string]map[string]interface{}{},
		// Definitions:         map[string]Definition{},
		// ExternalDocs:        map[string]string{},
	}

	for _, child := range rootMuxAPI.Children() {
		if !child.IsGroup() {
			addpath(child, rootTag)
			continue
		}
		childTag := &swagger.Tag{
			Name:        child.Path(),
			Description: apiTagDesc(child.Name()),
		}
		frame.apidoc.Tags = append(frame.apidoc.Tags, childTag)
		for _, grandson := range child.Children() {
			if !grandson.IsGroup() {
				addpath(grandson, childTag)
				continue
			}
			grandsonTag := &swagger.Tag{
				Name:        grandson.Path(),
				Description: apiTagDesc(grandson.Name()),
			}
			frame.apidoc.Tags = append(frame.apidoc.Tags, grandsonTag)
			for _, progeny := range grandson.Progeny() {
				if !progeny.IsGroup() {
					addpath(progeny, grandsonTag)
					continue
				}
			}
		}
	}
}

// 添加API操作项
func addpath(mux *MuxAPI, tag *swagger.Tag) {
	operas := map[string]*swagger.Opera{}
	pid := apiCreatePath(mux.Path())
	summary := apiSummary(mux.Name())
	desc := apiDesc(mux.Notes())
	for _, method := range mux.Methods() {
		if method == "CONNECT" || method == "TRACE" {
			continue
		}
		// if method == "WS" {
		// 	method = "GET"
		// }
		o := &swagger.Opera{
			Tags:        []string{tag.Name},
			Summary:     summary,
			Description: desc,
			OperationId: pid + "-" + method,
			Consumes:    swagger.CommonMIMETypes,
			Produces:    swagger.CommonMIMETypes,
			Responses:   make(map[string]*swagger.Resp, 1),
			// Security: []map[string][]string{},
		}

		for _, param := range mux.ParamInfos() {
			p := &swagger.Parameter{
				In:          param.In,
				Name:        param.Name,
				Description: param.Desc,
				Required:    param.Required,
				// Items:       &Items{},
				// Schema:      &Schema{},
			}
			typ := swagger.ParamType(param.Model)
			switch p.In {
			case "cookie":
				continue
			default:
				switch typ {
				case "file":
					o.Consumes = []string{"multipart/form-data"}
					p.Type = typ

				case "array":
					subtyp, first, count := swagger.SliceInfo(param.Model)
					switch subtyp {
					case "object":
						ref := apiDefinitions(mux, p.Name, method, param.Model)
						p.Schema = &swagger.Schema{
							Type: typ,
							Items: &swagger.Items{
								Ref: "#/definitions/" + ref,
							},
						}

					default:
						p.Type = typ
						p.Items = &swagger.Items{
							Type:    subtyp,
							Default: first,
						}
						if count > 0 {
							p.Items.Enum = param.Model
						}
						p.CollectionFormat = "multi"
					}

				case "object":
					ref := apiDefinitions(mux, p.Name, method, param.Model)
					p.Schema = &swagger.Schema{
						Type: typ,
						Ref:  "#/definitions/" + ref,
					}

				default:
					p.Type = typ
					p.Format = fmt.Sprintf("%T", param.Model)
					p.Default = param.Model
				}
			}

			o.Parameters = append(o.Parameters, p)
		}

		// static file
		if strings.HasSuffix(pid, "/{filepath}") {
			o.Parameters = append(o.Parameters, apiStaticParam)
		}

		operas[strings.ToLower(method)] = o
	}
	if _operas, ok := mux.frame.apidoc.Paths[pid]; ok {
		for k, v := range operas {
			_operas[k] = v
		}
	} else {
		mux.frame.apidoc.Paths[pid] = operas
	}
}

func apiDefinitions(mux *MuxAPI, pname, method string, format interface{}) (ref string) {
	upath := mux.Path()
	ref = strings.Replace(path.Join(upath[1:], pname, method), "/", "@", -1)
	def := &swagger.Definition{
		Type: "object",
		Xml:  &swagger.Xml{Name: ref},
	}
	def.Properties = swagger.CreateProperties(format)
	if mux.frame.apidoc.Definitions == nil {
		mux.frame.apidoc.Definitions = map[string]*swagger.Definition{}
	}
	mux.frame.apidoc.Definitions[ref] = def
	return
}

func apiCreatePath(u string) string {
	a := strings.Split(u, "/*")
	if len(a) > 1 {
		u = path.Join(a[0], "{filepath}")
	}
	s := strings.Split(u, "/:")
	p := s[0]
	if len(s) == 1 {
		return p
	}
	for _, param := range s[1:] {
		p = path.Join(p, "{"+param+"}")
	}
	return p
}

func apiTagDesc(desc string) string {
	return strings.TrimSpace(desc)
}

func apiSummary(desc string) string {
	return strings.TrimSpace(strings.Split(strings.TrimSpace(desc), "\n")[0])
}

func apiDesc(notes []Notes) string {
	var desc string
	count := len(notes)
	for i, n := range notes {
		if count > 1 {
			desc += fmt.Sprintf("\n\n======================= Handler %d =======================", i)
		}
		if n.Note != "" {
			desc += fmt.Sprintf("\nNote: %s", strings.TrimSpace(n.Note))
		} else {
			desc += "\nNote:"
		}
		if n.Return != nil {
			b, _ := json.MarshalIndent(n.Return, "", "  ")
			desc += fmt.Sprintf("\nReturn: %s", string(b))
		} else {
			desc += "\nReturn:"
		}
	}
	if desc != "" {
		return "
" + strings.TrimSpace(desc) + "
"
	}
	return ""
}
<|endoftext|>


<commit_before>package awsbase

import (
	"strings"

	"github.com/aws/aws-sdk-go/aws/awserr"
)

// IsAWSErr returns true if the error matches all these conditions:
//  * err is of type awserr.Error
//  * Error.Code() matches code
//  * Error.Message() contains message
func IsAWSErr(err error, code string, message string) bool {
	awsErr, ok := err.(awserr.Error)

	if !ok {
		return false
	}

	if awsErr.Code() != code {
		return false
	}

	return strings.Contains(awsErr.Message(), message)
}

// IsAWSErrExtended returns true if the error matches all these conditions:
//  * err is of type awserr.Error
//  * Error.Code() matches code
//  * Error.Message() contains message
//  * Error.OrigErr() contains origErrMessage
func IsAWSErrExtended(err error, code string, message string, origErrMessage string) bool {
	if !IsAWSErr(err, code, message) {
		return false
	}

	// Ensure OrigErr() is non-nil, to prevent panics
	if origErr := err.(awserr.Error).OrigErr(); origErr != nil {
		return strings.Contains(origErr.Error(), origErrMessage)
	}

	// Allow missing OrigErr() with missing origErrMessage
	return origErrMessage == ""
}
<commit_msg>Clarify conditional logic in IsAWSErrExtended()<commit_after>package awsbase

import (
	"strings"

	"github.com/aws/aws-sdk-go/aws/awserr"
)

// IsAWSErr returns true if the error matches all these conditions:
//  * err is of type awserr.Error
//  * Error.Code() matches code
//  * Error.Message() contains message
func IsAWSErr(err error, code string, message string) bool {
	awsErr, ok := err.(awserr.Error)

	if !ok {
		return false
	}

	if awsErr.Code() != code {
		return false
	}

	return strings.Contains(awsErr.Message(), message)
}

// IsAWSErrExtended returns true if the error matches all these conditions:
//  * err is of type awserr.Error
//  * Error.Code() matches code
//  * Error.Message() contains message
//  * Error.OrigErr() contains origErrMessage
func IsAWSErrExtended(err error, code string, message string, origErrMessage string) bool {
	if !IsAWSErr(err, code, message) {
		return false
	}

	if origErrMessage == "" {
		return true
	}

	// Ensure OrigErr() is non-nil, to prevent panics
	if origErr := err.(awserr.Error).OrigErr(); origErr != nil {
		return strings.Contains(origErr.Error(), origErrMessage)
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-hashset"
	"github.com/dustin/gomemcached"

	"encoding/hex"
	"github.com/couchbaselabs/cbfs/config"
	"sync"
)

const backupKey = "/@backup"

type backupItem struct {
	Fn   string                `json:"filename"`
	Oid  string                `json:"oid"`
	When time.Time             `json:"when"`
	Conf cbfsconfig.CBFSConfig `json:"conf"`
}

type backups struct {
	Latest  backupItem   `json:"latest"`
	Backups []backupItem `json:"backups"`
}

func logDuration(m string, startTime time.Time) {
	log.Printf("Completed %v in %v", m, time.Since(startTime))
}

func streamFileMeta(w io.Writer,
	fch chan *namedFile,
	ech chan error) error {

	enc := json.NewEncoder(w)
	for {
		select {
		case f, ok := <-fch:
			if !ok {
				return nil
			}
			err := enc.Encode(map[string]interface{}{
				"path": f.name,
				"meta": f.meta,
			})
			if err != nil {
				return err
			}
		case e, ok := <-ech:
			if ok {
				return e
			}
			ech = nil
		}
	}
}

func backupTo(w io.Writer) (err error) {
	fch := make(chan *namedFile)
	ech := make(chan error)
	qch := make(chan bool)

	defer close(qch)

	defer logDuration("backup", time.Now())

	go pathGenerator("", fch, ech, qch)

	gz := gzip.NewWriter(w)
	defer func() {
		e := gz.Close()
		if err != nil {
			err = e
		}
	}()

	return streamFileMeta(gz, fch, ech)
}

func recordBackupObject() error {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(*root, ".backup.json"))
	if err != nil {
		return err
	}
	defer f.Close()
	e := json.NewEncoder(f)
	return e.Encode(&b)
}

func recordRemoteBackupObjects() {
	rn, err := findRemoteNodes()
	if err != nil {
		log.Printf("Error getting remote nodes for recording backup: %v",
			err)
		return
	}
	for _, n := range rn {
		u := fmt.Sprintf("http://%s%s",
			n.Address(), markBackupPrefix)
		c := n.Client()
		res, err := c.Post(u, "application/octet-stream", nil)
		if err != nil {
			log.Printf("Error posting to %v: %v", u, err)
			continue
		}
		res.Body.Close()
		if res.StatusCode != 204 {
			log.Printf("HTTP Error posting to %v: %v", u, res.Status)
		}
	}
}

func storeBackupObject(fn, h string) error {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil && !gomemcached.IsNotFound(err) {
		log.Printf("Weird: %v", err)
		// return err
	}

	ob := backupItem{fn, h, time.Now().UTC(), *globalConfig}

	// Keep only backups we're pretty sure still exist.
	obn := b.Backups
	b.Backups = nil
	for _, bi := range obn {
		fm := fileMeta{}
		err := couchbase.Get(shortName(bi.Fn), &fm)
		if gomemcached.IsNotFound(err) {
			log.Printf("Dropping previous (deleted) backup: %v",
				bi.Fn)
		} else {
			b.Backups = append(b.Backups, bi)
		}
	}

	b.Latest = ob
	b.Backups = append(b.Backups, ob)

	return couchbase.Set(backupKey, 0, &b)
}

func backupToCBFS(fn string) error {
	f, err := NewHashRecord(*root, "")
	if err != nil {
		return err
	}
	defer f.Close()

	pr, pw := io.Pipe()

	go func() { pw.CloseWithError(backupTo(pw)) }()

	h, length, err := f.Process(pr)
	if err != nil {
		return err
	}

	err = recordBlobOwnership(h, length, true)
	if err != nil {
		return err
	}

	fm := fileMeta{
		OID:      h,
		Length:   length,
		Modified: time.Now().UTC(),
	}

	err = storeMeta(fn, fm, 1, nil)
	if err != nil {
		return err
	}

	err = storeBackupObject(fn, h)
	if err != nil {
		return err
	}

	err = recordBackupObject()
	if err != nil {
		log.Printf("Failed to record backup OID: %v", err)
	}

	go recordRemoteBackupObjects()

	log.Printf("Replicating backup %v.", h)
	go increaseReplicaCount(h, length, globalConfig.MinReplicas-1)

	return nil
}

func doMarkBackup(w http.ResponseWriter, req *http.Request) {
	if req.FormValue("all") == "true" {
		go recordRemoteBackupObjects()
	}
	err := recordBackupObject()
	if err != nil {
		http.Error(w, fmt.Sprintf("Error marking backup; %v", err), 500)
		return
	}
	w.WriteHeader(204)
}

func doBackupDocs(w http.ResponseWriter, req *http.Request) {
	fn := req.FormValue("fn")
	if fn == "" {
		http.Error(w, "Missing fn parameter", 400)
		return
	}

	if bg, _ := strconv.ParseBool(req.FormValue("bg")); bg {
		go func() {
			err := backupToCBFS(fn)
			if err != nil {
				log.Printf("Error performing bg backup: %v", err)
			}
		}()
		w.WriteHeader(202)
		return
	}

	err := backupToCBFS(fn)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error performing backup: %v", err), 500)
		return
	}

	w.WriteHeader(201)
}

func doGetBackupInfo(w http.ResponseWriter, req *http.Request) {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil {
		code := 500
		if gomemcached.IsNotFound(err) {
			code = 404
		}
		http.Error(w, err.Error(), code)
		return
	}

	w.Write(mustEncode(&b))
}

var errExists = errors.New("item exists")

func maybeStoreMeta(k string, fm fileMeta, force bool) error {
	if force {
		return couchbase.Set(k, 0, fm)
	}
	added, err := couchbase.Add(k, 0, fm)
	if err == nil && !added {
		err = errExists
	}
	return err
}

func doRestoreDocument(w http.ResponseWriter, req *http.Request, fn string) {
	d := json.NewDecoder(req.Body)
	fm := fileMeta{}
	err := d.Decode(&fm)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	for len(fn) > 0 && fn[0] == '/' {
		fn = fn[1:]
	}

	if fn == "" {
		http.Error(w, "No filename", 400)
		return
	}

	if strings.Contains(fn, "//") {
		http.Error(w,
			fmt.Sprintf("Too many slashes in the path name: %v", fn), 400)
		return
	}

	force := false
	err = maybeStoreMeta(fn, fm, force)
	switch err {
	case errExists:
		http.Error(w, err.Error(), 409)
		return
	case nil:
	default:
		log.Printf("Error storing file meta of %v -> %v: %v",
			fn, fm.OID, err)
		http.Error(w,
			fmt.Sprintf("Error recording file meta: %v", err), 500)
		return
	}

	log.Printf("Restored %v -> %v", fn, fm.OID)

	w.WriteHeader(201)
}

func loadBackupHashes(oid string) (*hashset.Hashset, int, error) {
	rv := &hashset.Hashset{}

	r := blobReader(oid)
	defer r.Close()
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, 0, err
	}
	defer gz.Close()

	d := json.NewDecoder(gz)

	visited := 0
	for {
		ob := struct {
			Meta struct {
				OID   string
				Older []struct {
					OID string
				}
			}
		}{}

		err := d.Decode(&ob)
		switch err {
		case nil:
			oid, err := hex.DecodeString(ob.Meta.OID)
			if err != nil {
				return nil, visited, err
			}
			rv.Add(oid)
			visited++
			for _, obs := range ob.Meta.Older {
				oid, err = hex.DecodeString(obs.OID)
				if err != nil {
					return nil, visited, err
				}
				rv.Add(oid)
				visited++
			}
		case io.EOF:
			return rv, visited, nil
		default:
			return nil, visited, err
		}
	}
}

func loadExistingHashes() (*hashset.Hashset, error) {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil && !gomemcached.IsNotFound(err) {
		return nil, err
	}

	oids := make(chan string)
	hsch := make(chan *hashset.Hashset)
	visitch := make(chan int)
	errch := make(chan error)

	wg := sync.WaitGroup{}

	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for o := range oids {
				h, v, e := loadBackupHashes(o)
				if e == nil {
					hsch <- h
				} else {
					errch <- e
				}
				visitch <- v
			}
		}()
	}
	go func() {
		wg.Wait()
		close(hsch)
		close(visitch)
		close(errch)
	}()

	go func() {
		for _, i := range b.Backups {
			log.Printf("Loading backups from %v / %v", i.Oid, i.Fn)
			oids <- i.Oid
		}
		close(oids)
	}()

	visited := 0
	hs := &hashset.Hashset{}
	for {
		// Done getting all the things
		if hsch == nil && visitch == nil && errch == nil {
			break
		}
		select {
		case v, ok := <-visitch:
			visited += v
			if !ok {
				visitch = nil
			}
		case e, ok := <-errch:
			err = e
			if !ok {
				errch = nil
			}
		case h, ok := <-hsch:
			if ok {
				log.Printf("Got %v hashes from a backup",
					h.Len())
				hs.AddAll(h)
			} else {
				hsch = nil
			}
		}
	}

	log.Printf("Visited %v obs, kept %v", visited, hs.Len())

	return hs, err
}
<commit_msg>Don't show dead backups.<commit_after>package main

import (
	"compress/gzip"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-hashset"
	"github.com/dustin/gomemcached"

	"encoding/hex"
	"github.com/couchbaselabs/cbfs/config"
	"sync"
)

const backupKey = "/@backup"

type backupItem struct {
	Fn   string                `json:"filename"`
	Oid  string                `json:"oid"`
	When time.Time             `json:"when"`
	Conf cbfsconfig.CBFSConfig `json:"conf"`
}

type backups struct {
	Latest  backupItem   `json:"latest"`
	Backups []backupItem `json:"backups"`
}

func logDuration(m string, startTime time.Time) {
	log.Printf("Completed %v in %v", m, time.Since(startTime))
}

func streamFileMeta(w io.Writer,
	fch chan *namedFile,
	ech chan error) error {

	enc := json.NewEncoder(w)
	for {
		select {
		case f, ok := <-fch:
			if !ok {
				return nil
			}
			err := enc.Encode(map[string]interface{}{
				"path": f.name,
				"meta": f.meta,
			})
			if err != nil {
				return err
			}
		case e, ok := <-ech:
			if ok {
				return e
			}
			ech = nil
		}
	}
}

func backupTo(w io.Writer) (err error) {
	fch := make(chan *namedFile)
	ech := make(chan error)
	qch := make(chan bool)

	defer close(qch)

	defer logDuration("backup", time.Now())

	go pathGenerator("", fch, ech, qch)

	gz := gzip.NewWriter(w)
	defer func() {
		e := gz.Close()
		if err != nil {
			err = e
		}
	}()

	return streamFileMeta(gz, fch, ech)
}

func recordBackupObject() error {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil {
		return err
	}

	removeDeadBackups(&b)

	f, err := os.Create(filepath.Join(*root, ".backup.json"))
	if err != nil {
		return err
	}
	defer f.Close()
	e := json.NewEncoder(f)
	return e.Encode(&b)
}

func recordRemoteBackupObjects() {
	rn, err := findRemoteNodes()
	if err != nil {
		log.Printf("Error getting remote nodes for recording backup: %v",
			err)
		return
	}
	for _, n := range rn {
		u := fmt.Sprintf("http://%s%s",
			n.Address(), markBackupPrefix)
		c := n.Client()
		res, err := c.Post(u, "application/octet-stream", nil)
		if err != nil {
			log.Printf("Error posting to %v: %v", u, err)
			continue
		}
		res.Body.Close()
		if res.StatusCode != 204 {
			log.Printf("HTTP Error posting to %v: %v", u, res.Status)
		}
	}
}

func removeDeadBackups(b *backups) {
	// Keep only backups we're pretty sure still exist.
	obn := b.Backups
	b.Backups = nil
	for _, bi := range obn {
		fm := fileMeta{}
		err := couchbase.Get(shortName(bi.Fn), &fm)
		if gomemcached.IsNotFound(err) {
			log.Printf("Dropping previous (deleted) backup: %v",
				bi.Fn)
		} else {
			b.Backups = append(b.Backups, bi)
		}
	}

}

func storeBackupObject(fn, h string) error {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil && !gomemcached.IsNotFound(err) {
		log.Printf("Weird: %v", err)
		// return err
	}

	removeDeadBackups(&b)

	ob := backupItem{fn, h, time.Now().UTC(), *globalConfig}

	b.Latest = ob
	b.Backups = append(b.Backups, ob)

	return couchbase.Set(backupKey, 0, &b)
}

func backupToCBFS(fn string) error {
	f, err := NewHashRecord(*root, "")
	if err != nil {
		return err
	}
	defer f.Close()

	pr, pw := io.Pipe()

	go func() { pw.CloseWithError(backupTo(pw)) }()

	h, length, err := f.Process(pr)
	if err != nil {
		return err
	}

	err = recordBlobOwnership(h, length, true)
	if err != nil {
		return err
	}

	fm := fileMeta{
		OID:      h,
		Length:   length,
		Modified: time.Now().UTC(),
	}

	err = storeMeta(fn, fm, 1, nil)
	if err != nil {
		return err
	}

	err = storeBackupObject(fn, h)
	if err != nil {
		return err
	}

	err = recordBackupObject()
	if err != nil {
		log.Printf("Failed to record backup OID: %v", err)
	}

	go recordRemoteBackupObjects()

	log.Printf("Replicating backup %v.", h)
	go increaseReplicaCount(h, length, globalConfig.MinReplicas-1)

	return nil
}

func doMarkBackup(w http.ResponseWriter, req *http.Request) {
	if req.FormValue("all") == "true" {
		go recordRemoteBackupObjects()
	}
	err := recordBackupObject()
	if err != nil {
		http.Error(w, fmt.Sprintf("Error marking backup; %v", err), 500)
		return
	}
	w.WriteHeader(204)
}

func doBackupDocs(w http.ResponseWriter, req *http.Request) {
	fn := req.FormValue("fn")
	if fn == "" {
		http.Error(w, "Missing fn parameter", 400)
		return
	}

	if bg, _ := strconv.ParseBool(req.FormValue("bg")); bg {
		go func() {
			err := backupToCBFS(fn)
			if err != nil {
				log.Printf("Error performing bg backup: %v", err)
			}
		}()
		w.WriteHeader(202)
		return
	}

	err := backupToCBFS(fn)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error performing backup: %v", err), 500)
		return
	}

	w.WriteHeader(201)
}

func doGetBackupInfo(w http.ResponseWriter, req *http.Request) {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil {
		code := 500
		if gomemcached.IsNotFound(err) {
			code = 404
		}
		http.Error(w, err.Error(), code)
		return
	}

	removeDeadBackups(&b)

	w.Write(mustEncode(&b))
}

var errExists = errors.New("item exists")

func maybeStoreMeta(k string, fm fileMeta, force bool) error {
	if force {
		return couchbase.Set(k, 0, fm)
	}
	added, err := couchbase.Add(k, 0, fm)
	if err == nil && !added {
		err = errExists
	}
	return err
}

func doRestoreDocument(w http.ResponseWriter, req *http.Request, fn string) {
	d := json.NewDecoder(req.Body)
	fm := fileMeta{}
	err := d.Decode(&fm)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	for len(fn) > 0 && fn[0] == '/' {
		fn = fn[1:]
	}

	if fn == "" {
		http.Error(w, "No filename", 400)
		return
	}

	if strings.Contains(fn, "//") {
		http.Error(w,
			fmt.Sprintf("Too many slashes in the path name: %v", fn), 400)
		return
	}

	force := false
	err = maybeStoreMeta(fn, fm, force)
	switch err {
	case errExists:
		http.Error(w, err.Error(), 409)
		return
	case nil:
	default:
		log.Printf("Error storing file meta of %v -> %v: %v",
			fn, fm.OID, err)
		http.Error(w,
			fmt.Sprintf("Error recording file meta: %v", err), 500)
		return
	}

	log.Printf("Restored %v -> %v", fn, fm.OID)

	w.WriteHeader(201)
}

func loadBackupHashes(oid string) (*hashset.Hashset, int, error) {
	rv := &hashset.Hashset{}

	r := blobReader(oid)
	defer r.Close()
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, 0, err
	}
	defer gz.Close()

	d := json.NewDecoder(gz)

	visited := 0
	for {
		ob := struct {
			Meta struct {
				OID   string
				Older []struct {
					OID string
				}
			}
		}{}

		err := d.Decode(&ob)
		switch err {
		case nil:
			oid, err := hex.DecodeString(ob.Meta.OID)
			if err != nil {
				return nil, visited, err
			}
			rv.Add(oid)
			visited++
			for _, obs := range ob.Meta.Older {
				oid, err = hex.DecodeString(obs.OID)
				if err != nil {
					return nil, visited, err
				}
				rv.Add(oid)
				visited++
			}
		case io.EOF:
			return rv, visited, nil
		default:
			return nil, visited, err
		}
	}
}

func loadExistingHashes() (*hashset.Hashset, error) {
	b := backups{}
	err := couchbase.Get(backupKey, &b)
	if err != nil && !gomemcached.IsNotFound(err) {
		return nil, err
	}

	oids := make(chan string)
	hsch := make(chan *hashset.Hashset)
	visitch := make(chan int)
	errch := make(chan error)

	wg := sync.WaitGroup{}

	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for o := range oids {
				h, v, e := loadBackupHashes(o)
				if e == nil {
					hsch <- h
				} else {
					errch <- e
				}
				visitch <- v
			}
		}()
	}
	go func() {
		wg.Wait()
		close(hsch)
		close(visitch)
		close(errch)
	}()

	go func() {
		for _, i := range b.Backups {
			log.Printf("Loading backups from %v / %v", i.Oid, i.Fn)
			oids <- i.Oid
		}
		close(oids)
	}()

	visited := 0
	hs := &hashset.Hashset{}
	for {
		// Done getting all the things
		if hsch == nil && visitch == nil && errch == nil {
			break
		}
		select {
		case v, ok := <-visitch:
			visited += v
			if !ok {
				visitch = nil
			}
		case e, ok := <-errch:
			err = e
			if !ok {
				errch = nil
			}
		case h, ok := <-hsch:
			if ok {
				log.Printf("Got %v hashes from a backup",
					h.Len())
				hs.AddAll(h)
			} else {
				hsch = nil
			}
		}
	}

	log.Printf("Visited %v obs, kept %v", visited, hs.Len())

	return hs, err
}
<|endoftext|>


<commit_before>package baudio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"
)

const (
	FuncValueTypeFloat    = 0
	FuncValueTypeNotFloat = 1
)

type BChannel struct {
	funcValueType int
	funcs         []func(float64) float64
}

//func newBChannel(fvt int, fn func(float64) float64) *BChannel {
func newBChannel(fvt int) *BChannel {
	//bc := newBChannel(fvt)
	bc := &BChannel{
		funcValueType: fvt,
		funcs:         make([]func(float64) float64, 0),
	}
	//bc.funcs = append(bc.funcs, fn)
	return bc
}

func (bc *BChannel) push(fn func(float64) float64) {
	bc.funcs = append(bc.funcs, fn)
}

/*
func newBChannel(fvt int) {
	bc := &BChanenl{
		funcValueType: fvt,
		funcs:         make([]func(float64) float64),
	}
	b := NewBaudio(opts)
	b.Push(fn)
	return b
}
*/

type B struct {
	readable   bool
	size       int
	rate       int
	t          int
	i          int
	paused     bool
	ended      bool
	destroyed  bool
	channels   []*BChannel
	chEnd      chan bool
	chResume   chan func()
	chData     chan *bytes.Buffer
	chNextTick chan bool
}

//func NewBaudio(opts map[string]string) *B {
func New( /*opts map[string]string*/ fn func(float64) float64) *B {
	b := &B{
		readable:   true,
		size:       2048,
		rate:       44000,
		t:          0,
		i:          0,
		paused:     false,
		ended:      false,
		destroyed:  false,
		chEnd:      make(chan bool),
		chResume:   make(chan func()),
		chData:     make(chan *bytes.Buffer),
		chNextTick: make(chan bool),
	}
	//TODO
	/*
		if val, ok := opts["size"]; ok {
			b.size = val
		}
		if val, ok := opts["rate"]; ok {
			b.rate = val
		}
	*/

	go func() {
		if b.paused {
			b.chResume <- func() {
				b.main()
			}
		} else {
			b.main()
		}
	}()
	b.Push(0, fn)
	return b
}

func (b *B) main() {
	for {

		select {
		case <-b.chNextTick:
			b.loop()
		case <-b.chEnd:
			break
		case fn := <-b.chResume:
			fn()
		case buf := <-b.chData:
			// send buf to sox-play process. pipe?
			//DUMMY
			fmt.Printf("%0x\n", buf)
		}
	}
}

func (b *B) end() {
	b.ended = true
}

func (b *B) destroy() {
	b.destroyed = true
	b.chEnd <- true
}

func (b *B) pause() {
	b.paused = true
}

func (b *B) resume() {
	if !b.paused {
		return
	}
	b.paused = false
	b.chResume <- func() {}
}

func (b *B) AddChannel(funcValueType int, fn func(float64) float64) {
	// DEFAULT, TODO: support ohter value type?
	//bc := newBChannel(funcValueType, fn)
	bc := newBChannel(FuncValueTypeFloat)
	bc.push(fn)
	b.channels = append(b.channels, bc)
}

/*
func (b *B) AddChannel(funcValueType int) {
	funcValueType = FuncValueTypeFloat
	bc := newBChannel(funcValueType)
	b.channels = append(b.channels, bc)
}
*/

func (b *B) Push(index int, fn func(float64) float64) {
	if len(b.channels) <= index {
		//b.AddChannel(FuncValueTypeFloat)
		bc := newBChannel(FuncValueTypeFloat)
		b.channels = append(b.channels, bc)
	}
	b.channels[index].funcs = append(b.channels[index].funcs, fn)
}

/*
func (b *B) Push(fn func(float64) float64) {
	b.Push(0, fn)
}
*/

func (b *B) loop() {
	buf := b.tick()
	if b.destroyed {
		// no more events
	} else if b.paused {
		b.chResume <- func() {
			b.chData <- buf
			b.chNextTick <- true
		}
	} else {
		b.chData <- buf
		if b.ended {
			b.chEnd <- true
		} else {
			b.chNextTick <- true
		}
	}
}

func (b *B) tick() *bytes.Buffer {
	byteBuffer := make([]byte, b.size*len(b.channels))
	buf := bytes.NewBuffer(byteBuffer)
	for i := 0; i < buf.Len(); i++ {
		lenCh := len(b.channels)
		ch := b.channels[(int(i/2))%lenCh]
		t := float64(b.t) + math.Floor(float64(i/2))/float64(b.rate)/float64(lenCh)
		//counter := b.i + int(math.Floor(float64(i/2)/float64(lenCh)))

		value := float64(0)
		n := float64(0)
		for j := 0; j < len(ch.funcs); j++ {
			//x := ch.funcs[j](float64(t), counter)
			x := ch.funcs[j](float64(t))
			n += x
		}
		n /= float64(len(ch.funcs))

		if ch.funcValueType == FuncValueTypeFloat {
			value = signed(n)
		} else {
			/*
				b := math.Pow(2, ch.funcValueType)
				x := (math.Floor(n) % b) / b * math.Pow(2, 15)
				value = x
			*/
		}
		err := binary.Write(buf, binary.LittleEndian, clamp(value))
		if err != nil {
			panic(err)
		}
	}
	b.i += b.size / 2
	b.t += b.size / 2 / b.rate
	return buf
}

func clamp(x float64) float64 {
	return math.Max(math.Min(x, math.Pow(2, 15)-1), -math.Pow(2, 15))
}

func mergeArgs(opts, args []string) {
}

func (b *B) Play( /*opts []string*/) {
}

func (b *B) Record(file string, opts []string) {
}

func signed(n float64) float64 {
	b := math.Pow(2, 15)
	if n > 0 {
		return math.Min(b-1, math.Floor(b*n-1))
	}
	return math.Max(-b, math.Ceil(b*n-1))
}
<commit_msg>audible.<commit_after>package baudio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os/exec"
)

const (
	FuncValueTypeFloat    = 0
	FuncValueTypeNotFloat = 1
)

type BChannel struct {
	funcValueType int
	funcs         []func(float64) float64
}

//func newBChannel(fvt int, fn func(float64) float64) *BChannel {
func newBChannel(fvt int) *BChannel {
	//bc := newBChannel(fvt)
	bc := &BChannel{
		funcValueType: fvt,
		funcs:         make([]func(float64) float64, 0),
	}
	//bc.funcs = append(bc.funcs, fn)
	return bc
}

func (bc *BChannel) push(fn func(float64) float64) {
	bc.funcs = append(bc.funcs, fn)
}

/*
func newBChannel(fvt int) {
	bc := &BChanenl{
		funcValueType: fvt,
		funcs:         make([]func(float64) float64),
	}
	b := NewBaudio(opts)
	b.Push(fn)
	return b
}
*/

type B struct {
	readable   bool
	size       int
	rate       int
	t          int
	i          int
	paused     bool
	ended      bool
	destroyed  bool
	channels   []*BChannel
	chEnd      chan bool
	chResume   chan func()
	chData     chan *bytes.Buffer
	chNextTick chan bool
	pipeReader *io.PipeReader
	pipeWriter *io.PipeWriter
}

func New( /*opts map[string]string*/ fn func(float64) float64) *B {
	b := &B{
		readable:   true,
		size:       2048,
		rate:       44000,
		t:          0,
		i:          0,
		paused:     false,
		ended:      false,
		destroyed:  false,
		chEnd:      make(chan bool),
		chResume:   make(chan func()),
		chData:     make(chan *bytes.Buffer),
		chNextTick: make(chan bool),
	}
	b.pipeReader, b.pipeWriter = io.Pipe()
	//TODO
	/*
		if val, ok := opts["size"]; ok {
			b.size = val
		}
		if val, ok := opts["rate"]; ok {
			b.rate = val
		}
	*/

	go func() {
		if b.paused {
			b.chResume <- func() {
				go b.main()
				b.loop()
			}
		} else {
			go b.main()
			b.loop()
		}
	}()
	b.Push(0, fn)
	return b
}

func (b *B) main() {
	for {
		fmt.Println("main loop header")
		select {
		case <-b.chEnd:
			fmt.Println("main chEnd")
			break
		case fn := <-b.chResume:
			fmt.Println("main chResume")
			fn()
		case buf := <-b.chData:
			fmt.Println("main chData")
			b.pipeWriter.Write(buf.Bytes())
		case <-b.chNextTick:
			fmt.Println("main chNextTick")
			//b.loop()
		default:
			fmt.Println("main default")
			go b.loop()
		}
	}
}

func (b *B) end() {
	b.ended = true
}

func (b *B) destroy() {
	b.destroyed = true
	b.chEnd <- true
}

func (b *B) pause() {
	b.paused = true
}

func (b *B) resume() {
	if !b.paused {
		return
	}
	b.paused = false
	b.chResume <- func() {}
}

func (b *B) AddChannel(funcValueType int, fn func(float64) float64) {
	// DEFAULT, TODO: support ohter value type?
	//bc := newBChannel(funcValueType, fn)
	bc := newBChannel(FuncValueTypeFloat)
	bc.push(fn)
	b.channels = append(b.channels, bc)
}

/*
func (b *B) AddChannel(funcValueType int) {
	funcValueType = FuncValueTypeFloat
	bc := newBChannel(funcValueType)
	b.channels = append(b.channels, bc)
}
*/

func (b *B) Push(index int, fn func(float64) float64) {
	if len(b.channels) <= index {
		//b.AddChannel(FuncValueTypeFloat)
		bc := newBChannel(FuncValueTypeFloat)
		b.channels = append(b.channels, bc)
	}
	b.channels[index].funcs = append(b.channels[index].funcs, fn)
}

/*
func (b *B) Push(fn func(float64) float64) {
	b.Push(0, fn)
}
*/

func (b *B) loop() {
	buf := b.tick()
	if b.destroyed {
		// no more events
		fmt.Println("loop destroyed")
	} else if b.paused {
		fmt.Println("loop paused")
		b.chResume <- func() {
			b.chData <- buf
			b.chNextTick <- true
		}
	} else {
		fmt.Println("loop !(destroyed || paused)")
		b.chData <- buf
		if b.ended {
			fmt.Println("loop ended")
			b.chEnd <- true
		} else {
			fmt.Println("loop !ended")
			b.chNextTick <- true
		}
	}
}

func (b *B) tick() *bytes.Buffer {
	byteBuffer := make([]byte, b.size*len(b.channels))
	buf := bytes.NewBuffer(byteBuffer)
	bufLen := buf.Len()
	for i := 0; i < bufLen; i++ {
		lenCh := len(b.channels)
		ch := b.channels[(int(i/2))%lenCh]
		t := float64(b.t) + math.Floor(float64(i/2))/float64(b.rate)/float64(lenCh)
		//counter := b.i + int(math.Floor(float64(i/2)/float64(lenCh)))

		value := float64(0)
		n := float64(0)
		for j := 0; j < len(ch.funcs); j++ {
			//x := ch.funcs[j](float64(t), counter)
			x := ch.funcs[j](float64(t))
			n += x
		}
		n /= float64(len(ch.funcs))

		if ch.funcValueType == FuncValueTypeFloat {
			value = signed(n)
		} else {
			/*
				b := math.Pow(2, ch.funcValueType)
				x := (math.Floor(n) % b) / b * math.Pow(2, 15)
				value = x
			*/
		}
		err := binary.Write(buf, binary.LittleEndian, clamp(value))
		if err != nil {
			panic(err)
		}
	}
	b.i += b.size / 2
	b.t += b.size / 2 / b.rate
	return buf
}

func clamp(x float64) float64 {
	return math.Max(math.Min(x, math.Pow(2, 15)-1), -math.Pow(2, 15))
}

func mergeArgs(opts, args []string) {
}

func (b *B) Play( /*opts []string*/) {
	fmt.Println("Play!")
	cmd := exec.Command("play", "-c", "1", "-r", "8k", "-t", "s16", "-")
	stdin, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}
	defer stdin.Close()
	var out bytes.Buffer
	cmd.Stdout = &out
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	defer func() {
		if p := cmd.Process; p != nil {
			p.Kill()
		}
	}()

	readBuf := make([]byte, b.size)
	for {
		fmt.Println("play loop header")
		n, err := b.pipeReader.Read(readBuf)
		if err != nil {
			panic(err)
		}
		fmt.Printf("read n = %d\n", n)
		_, err = stdin.Write(readBuf)
		if err != nil {
			panic(err)
		}
	}
}

func (b *B) Record(file string, opts []string) {
}

func signed(n float64) float64 {
	b := math.Pow(2, 15)
	if n > 0 {
		return math.Min(b-1, math.Floor(b*n-1))
	}
	return math.Max(-b, math.Ceil(b*n-1))
}
<|endoftext|>


<commit_before>package baymax

import (
	"gopkg.in/gin-gonic/gin.v1"
	"io/ioutil"
	"strings"
	"net/http"
	"github.com/pkg/errors"
	"fmt"
	"sort"
	"strconv"
	"os"
	"github.com/rogpeppe/rog-go/reverse"
	"github.com/brunetto/goutils/file"
	"time"
)

//go:generate rice embed-go

var Version string

type Baymax struct {
	*http.Client
	Targets []Target
	LogLines int
}

type Conf struct {
	Targets []Target `json:"targets"`
	LogLines int `json:"log_lines"`
	LogFile string `json:"log_file"`
}

type errorSlice []error
func (es errorSlice) Error() string {
	switch len(es) {
	case 0:
		return ""
	case 1:
		return es[0].Error()
	default:
		err := errors.Errorf("[0] %v", es[0].Error())
		for i, e := range es[1:] {
			err = errors.Wrap(err, "[" + strconv.Itoa(i+1) + "] " + e.Error()+"\n")
		}
		return err.Error()
	}
}

type Targets []Target

type Target struct {
	Name string `json:"name"`
	URL string `json:"url"`
	AlternativeURL string `json:"alternative_url"`
	LogLocation string `json:"log_location"`
	Status int `json:"status"`
	Message string `json:"message"`
	Logs string `json:"logs"`
}
// Sort targets on name
func (t Targets) Len() int           { return len(t) }
func (t Targets) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Targets) Less(i, j int) bool { return t[i].Name < t[j].Name }

func (b *Baymax) MonitorJSON (c *gin.Context) {
	b.CollectStatus()
	b.CollectLogs()
	c.JSON(http.StatusOK, b.Targets)
	return
}

func (b *Baymax) MonitorGUI (c *gin.Context) {
	ok, warns, errs := b.CollectStatus()
	b.CollectLogs()
	c.HTML(http.StatusOK, "message", gin.H{
		"Title": "Status Monitor",
		"Targets": b.Targets,
		"Ok": ok, "Warnings": warns, "Errors": errs,
	})
	return
}

func (b *Baymax) CollectStatus () (ok, warns, errs int) {
	var (
		targets Targets
	)
	for _, t := range b.Targets {
		t = b.ProbeTarget(t)
		targets = append(targets, t)
		switch t.Status {
		case 0:
			errs += 1
		case 1:
			ok +=1
		case 2:
			warns +=1
		}
	}
	sort.Sort(targets)
	b.Targets = targets
	return ok, warns, errs
}

func (b *Baymax) ProbeTarget(t Target) Target {
	var (
		resp *http.Response
		err error
		errs errorSlice
	)
	resp, err = b.Get(t.URL)
	if err != nil {
		errs = append(errs, errors.Wrap(err, "can't reach service on main URL"))
		if t.AlternativeURL != "" {
			errs = append(errs, errors.Errorf("probing alternative URL: %v", t.AlternativeURL))
			resp, err = b.Get(t.AlternativeURL)
			if err != nil {
				errs = append(errs, errors.Wrap(err, "can't reach service on alternative URL"))
				t.Status = 0
				t.Message = errs.Error()
				return t
			}
		} else {
			errs = append(errs, errors.New("no alternative URL provided"))
			t.Status = 0
			t.Message = errs.Error()
			return t
		}
	}

	if resp.StatusCode != 200 {
		t.Status = 2
		errs =  append(errs, errors.Errorf("got %v (%v)", resp.StatusCode, http.StatusText(resp.StatusCode)))
		t.Message = errs.Error()
		return t
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Status = 2
		errs = append(errs, errors.Wrap(errs, "unreadable response"))
		t.Message = errs.Error()
		return t
	}

	if strings.ToLower(string(body)) != "ok" {
		t.Status = 2
		errs = append(errs, errors.Wrap(err, fmt.Sprintf("service not ok: %v", string(body))))
		t.Message = errs.Error()
		return t
	}

	if len(errs) != 0 {
		t.Status = 2
		t.Message = errs.Error()
	} else {
		t.Status = 1
		t.Message = "OK"
	}
	return t
}

func (b *Baymax) CollectLogs () {
	var (
		targets Targets
	)
	for _, t := range b.Targets {
		t = b.GetTargetLog(t)
		targets = append(targets, t)
	}
	sort.Sort(targets)
	b.Targets = targets
	return
}

func (b *Baymax) GetTargetLog (t Target) Target {
	if t.LogLocation == "" {
		t.Logs = errors.New("no log specified").Error()
		return t
	}
	logFile := strings.Replace(t.LogLocation, "{{date}}", time.Now().Format("2006-01-02"), 1)

	if !file.Exists(logFile) {
		t.Logs = "log file doesn't exists"
		return t
	}

	logFileObj, err := os.Open(logFile)
	if err != nil {
		t.Logs = errors.Wrap(err, "can't open log file").Error()
		return t
	}
	defer logFileObj.Close()

	logs := []string{}
	count := 0
	scanner := reverse.NewScanner(logFileObj)
	fmt.Println(logFile, b.LogLines)
	for scanner.Scan() {
		if count >= b.LogLines {
			break
		}
		line := scanner.Text()
		fmt.Println("line ", line)
		logs = append(logs, line)
		count++
	}
	// Reverse the order
	for i:=1; i<=len(logs); i++ {
		t.Logs += logs[len(logs)-i]
	}

	return t
}
<commit_msg>fix error collection<commit_after>package baymax

import (
	"gopkg.in/gin-gonic/gin.v1"
	"io/ioutil"
	"strings"
	"net/http"
	"github.com/pkg/errors"
	"fmt"
	"sort"
	"strconv"
	"os"
	"github.com/rogpeppe/rog-go/reverse"
	"github.com/brunetto/goutils/file"
	"time"
)

//go:generate rice embed-go

var Version string

type Baymax struct {
	*http.Client
	Targets []Target
	LogLines int
}

type Conf struct {
	Targets []Target `json:"targets"`
	LogLines int `json:"log_lines"`
	LogFile string `json:"log_file"`
}

type errorSlice []error
func (es errorSlice) Error() string {
	switch len(es) {
	case 0:
		return ""
	case 1:
		return es[0].Error()
	default:
		err := errors.Errorf("[0] %v", es[0].Error())
		for i, e := range es[1:] {
			err = errors.Wrap(err, "[" + strconv.Itoa(i+1) + "] " + e.Error()+"\n")
		}
		return err.Error()
	}
}

type Targets []Target

type Target struct {
	Name string `json:"name"`
	URL string `json:"url"`
	AlternativeURL string `json:"alternative_url"`
	LogLocation string `json:"log_location"`
	Status int `json:"status"`
	Message string `json:"message"`
	Logs string `json:"logs"`
}
// Sort targets on name
func (t Targets) Len() int           { return len(t) }
func (t Targets) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Targets) Less(i, j int) bool { return t[i].Name < t[j].Name }

func (b *Baymax) MonitorJSON (c *gin.Context) {
	b.CollectStatus()
	b.CollectLogs()
	c.JSON(http.StatusOK, b.Targets)
	return
}

func (b *Baymax) MonitorGUI (c *gin.Context) {
	ok, warns, errs := b.CollectStatus()
	b.CollectLogs()
	c.HTML(http.StatusOK, "message", gin.H{
		"Title": "Status Monitor",
		"Targets": b.Targets,
		"Ok": ok, "Warnings": warns, "Errors": errs,
	})
	return
}

func (b *Baymax) CollectStatus () (ok, warns, errs int) {
	var (
		targets Targets
	)
	for _, t := range b.Targets {
		t = b.ProbeTarget(t)
		targets = append(targets, t)
		switch t.Status {
		case 0:
			errs += 1
		case 1:
			ok +=1
		case 2:
			warns +=1
		}
	}
	sort.Sort(targets)
	b.Targets = targets
	return ok, warns, errs
}

func (b *Baymax) ProbeTarget(t Target) Target {
	var (
		resp *http.Response
		err error
		errs errorSlice
	)
	resp, err = b.Get(t.URL)
	if err != nil {
		errs = append(errs, errors.Wrap(err, "can't reach service on main URL"))
		if t.AlternativeURL != "" {
			errs = append(errs, errors.Errorf("probing alternative URL: %v", t.AlternativeURL))
			resp, err = b.Get(t.AlternativeURL)
			if err != nil {
				errs = append(errs, errors.Wrap(err, "can't reach service on alternative URL"))
				t.Status = 0
				t.Message = errs.Error()
				return t
			}
		} else {
			errs = append(errs, errors.New("no alternative URL provided"))
			t.Status = 0
			t.Message = errs.Error()
			return t
		}
	}

	if resp.StatusCode != 200 {
		t.Status = 2
		errs =  append(errs, errors.Errorf("got %v (%v)", resp.StatusCode, http.StatusText(resp.StatusCode)))
		t.Message = errs.Error()
		return t
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Status = 2
		errs = append(errs, errors.Wrap(errs, "unreadable response"))
		t.Message = errs.Error()
		return t
	}

	if strings.ToLower(string(body)) != "ok" {
		t.Status = 2
		errs = append(errs, fmt.Errorf("service not ok: %v", string(body)))
		t.Message = errs.Error()
		return t
	}

	if len(errs) != 0 {
		t.Status = 2
		t.Message = errs.Error()
	} else {
		t.Status = 1
		t.Message = "OK"
	}
	return t
}

func (b *Baymax) CollectLogs () {
	var (
		targets Targets
	)
	for _, t := range b.Targets {
		t = b.GetTargetLog(t)
		targets = append(targets, t)
	}
	sort.Sort(targets)
	b.Targets = targets
	return
}

func (b *Baymax) GetTargetLog (t Target) Target {
	if t.LogLocation == "" {
		t.Logs = errors.New("no log specified").Error()
		return t
	}
	logFile := strings.Replace(t.LogLocation, "{{date}}", time.Now().Format("2006-01-02"), 1)

	if !file.Exists(logFile) {
		t.Logs = "log file doesn't exists"
		return t
	}

	logFileObj, err := os.Open(logFile)
	if err != nil {
		t.Logs = errors.Wrap(err, "can't open log file").Error()
		return t
	}
	defer logFileObj.Close()

	logs := []string{}
	count := 0
	scanner := reverse.NewScanner(logFileObj)
	fmt.Println(logFile, b.LogLines)
	for scanner.Scan() {
		if count >= b.LogLines {
			break
		}
		line := scanner.Text()
		fmt.Println("line ", line)
		logs = append(logs, line)
		count++
	}
	// Reverse the order
	for i:=1; i<=len(logs); i++ {
		t.Logs += logs[len(logs)-i]
	}

	return t
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

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

func TestAccAWSElasticacheSecurityGroup_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSElasticacheSecurityGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSElasticacheSecurityGroupConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"),
					resource.TestCheckResourceAttr(
						"aws_elasticache_security_group.bar", "description", "Managed by Terraform"),
				),
			},
		},
	})
}

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

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_elasticache_security_group" {
			continue
		}
		res, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
			CacheSecurityGroupName: aws.String(rs.Primary.ID),
		})
		if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "CacheSecurityGroupNotFound" {
			continue
		}

		if len(res.CacheSecurityGroups) > 0 {
			return fmt.Errorf("cache security group still exists")
		}
		return err
	}
	return nil
}

func testAccCheckAWSElasticacheSecurityGroupExists(n string) 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 cache security group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
		_, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
			CacheSecurityGroupName: aws.String(rs.Primary.ID),
		})
		if err != nil {
			return fmt.Errorf("CacheSecurityGroup error: %v", err)
		}
		return nil
	}
}

var testAccAWSElasticacheSecurityGroupConfig = fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}
resource "aws_security_group" "bar" {
    name = "tf-test-security-group-%03d"
    ingress {
        from_port = -1
        to_port = -1
        protocol = "icmp"
        cidr_blocks = ["0.0.0.0/0"]
    }
}

resource "aws_elasticache_security_group" "bar" {
    name = "tf-test-security-group-%03d"
    description = "tf-test-security-group-descr"
    security_group_names = ["${aws_security_group.bar.name}"]
}
`, acctest.RandInt(), acctest.RandInt())
<commit_msg>provider/aws: Fix TestAccAWSElasticacheSecurityGroup_basic test<commit_after>package aws

import (
	"fmt"
	"testing"

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

func TestAccAWSElasticacheSecurityGroup_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSElasticacheSecurityGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSElasticacheSecurityGroupConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"),
					resource.TestCheckResourceAttr(
						"aws_elasticache_security_group.bar", "description", "Managed by Terraform"),
				),
			},
		},
	})
}

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

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_elasticache_security_group" {
			continue
		}
		res, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
			CacheSecurityGroupName: aws.String(rs.Primary.ID),
		})
		if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "CacheSecurityGroupNotFound" {
			continue
		}

		if len(res.CacheSecurityGroups) > 0 {
			return fmt.Errorf("cache security group still exists")
		}
		return err
	}
	return nil
}

func testAccCheckAWSElasticacheSecurityGroupExists(n string) 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 cache security group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
		_, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
			CacheSecurityGroupName: aws.String(rs.Primary.ID),
		})
		if err != nil {
			return fmt.Errorf("CacheSecurityGroup error: %v", err)
		}
		return nil
	}
}

var testAccAWSElasticacheSecurityGroupConfig = fmt.Sprintf(`
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "bar" {
  name = "tf-test-security-group-%03d"

  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_elasticache_security_group" "bar" {
  name                 = "tf-test-security-group-%03d"
  security_group_names = ["${aws_security_group.bar.name}"]
}
`, acctest.RandInt(), acctest.RandInt())
<|endoftext|>