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

package userhtml

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"golang.org/x/sync/errgroup"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/templates"

	"go.chromium.org/luci/cv/internal/acls"
	"go.chromium.org/luci/cv/internal/common"
	"go.chromium.org/luci/cv/internal/rpc/admin"
	adminpb "go.chromium.org/luci/cv/internal/rpc/admin/api"
	"go.chromium.org/luci/cv/internal/run"
	"go.chromium.org/luci/cv/internal/run/eventpb"
	"go.chromium.org/luci/cv/internal/tryjob"
)

func runDetails(c *router.Context) {
	ctx := c.Context

	rID := fmt.Sprintf("%s/%s", c.Params.ByName("Project"), c.Params.ByName("Run"))

	// Load the Run, checking its existence and ACLs.
	r, err := acls.LoadRun(ctx, common.RunID(rID))
	if err != nil {
		errPage(c, err)
		return
	}

	// Reverse entries to get the most recent at the top of the list.
	logEntries, err := run.LoadRunLogEntries(ctx, r.ID)
	if err != nil {
		errPage(c, err)
		return
	}
	nEntries := len(logEntries)
	for i := 0; i < nEntries/2; i++ {
		logEntries[i], logEntries[nEntries-1-i] = logEntries[nEntries-1-i], logEntries[i]
	}

	cls, err := run.LoadRunCLs(ctx, common.RunID(r.ID), r.CLs)
	if err != nil {
		errPage(c, err)
		return
	}

	// Sort a stack of CLs by external ID.
	sort.Slice(cls, func(i, j int) bool { return cls[i].ExternalID < cls[j].ExternalID })

	// Compute next and previous runs for all cls in parallel.
	adminServer := &admin.AdminServer{}
	clsAndLinks, err := computeCLsAndLinks(ctx, adminServer, cls, r.ID)
	if err != nil {
		errPage(c, err)
		return
	}
	clidToURL := makeURLMap(cls)

	templates.MustRender(ctx, c.Writer, "pages/run_details.html", templates.Args{
		"Run":  r,
		"Logs": logEntries,
		"Cls":  clsAndLinks,
		"RelTime": func(ts time.Time) string {
			return humanize.RelTime(ts, startTime(ctx), "ago", "from now")
		},
		"LogMessage": func(rle *run.LogEntry) string {
			switch v := rle.GetKind().(type) {
			case *run.LogEntry_Info_:
				return v.Info.Message
			case *run.LogEntry_ClSubmitted:
				return StringifySubmissionSuccesses(clidToURL, v.ClSubmitted, int64(len(r.CLs)))
			case *run.LogEntry_SubmissionFailure_:
				return StringifySubmissionFailureReason(clidToURL, v.SubmissionFailure.Event)
			default:
				return ""
			}
		},
		"AllTryjobs": r.Tryjobs.GetTryjobs(),
	})
}

type clAndNeighborRuns struct {
	Prev, Next common.RunID
	CL         *run.RunCL
}

func (cl *clAndNeighborRuns) URLWithPatchset() string {
	return fmt.Sprintf("%s/%d", cl.CL.ExternalID.MustURL(), cl.CL.Detail.GetPatchset())
}

func (cl *clAndNeighborRuns) ShortWithPatchset() string {
	return fmt.Sprintf("%s/%d", displayCLExternalID(cl.CL.ExternalID), cl.CL.Detail.GetPatchset())
}

func computeCLsAndLinks(ctx context.Context, srv adminpb.AdminServer, cls []*run.RunCL, rid common.RunID) ([]*clAndNeighborRuns, error) {
	ret := make([]*clAndNeighborRuns, len(cls))
	eg, ctx := errgroup.WithContext(ctx)
	for i, cl := range cls {
		i, cl := i, cl
		eg.Go(func() error {
			newer, older, err := getNeighborsByCL(ctx, srv, cls[i], rid)
			if err != nil {
				return errors.Annotate(err, "unable to get previous and next runs for cl %s", cl.ExternalID).Err()
			}
			ret[i] = &clAndNeighborRuns{
				Prev: older,
				Next: newer,
				CL:   cl,
			}
			return nil
		})
	}
	return ret, eg.Wait()
}

func getNeighborsByCL(ctx context.Context, srv adminpb.AdminServer, cl *run.RunCL, rID common.RunID) (common.RunID, common.RunID, error) {
	var newer, older, last common.RunID
	req := &adminpb.SearchRunsRequest{
		Project: rID.LUCIProject(),
		Cl:      &adminpb.GetCLRequest{Id: int64(cl.ID)},
	}
	for {
		// Note that SearchRuns are returned in reverse chronological order
		// i.e. Most recent first.
		resp, err := srv.SearchRuns(ctx, req)
		if err != nil {
			return "", "", err
		}

		i := indexOf(resp.Runs, rID)
		switch {
		case i == -1:
			if resp.GetNextPageToken() == "" {
				return newer, older, nil
			}
			// There are more pages and we haven't found the run of interest.
			// Save the last run of this page in case the first run in the next
			// page is the one of interest.
			last = common.RunID(resp.Runs[len(resp.Runs)-1].Id)
			req.PageToken = resp.GetNextPageToken()
			continue
		case i > 0:
			newer = common.RunID(resp.Runs[i-1].Id)
		case last != common.RunID(""):
			// This is not the first page, but the run of interest
			// is the first in the page. Use the last run of the
			// previous page.
			newer = last
		}

		switch {
		case i < len(resp.Runs)-1:
			older = common.RunID(resp.Runs[i+1].Id)
		case resp.GetNextPageToken() != "":
			// The last run in the page is the one of interest,
			// then the first run in the next page will be the next.
			req.PageToken = resp.GetNextPageToken()
			resp, err = srv.SearchRuns(ctx, req)
			if err != nil {
				return "", "", err
			}
			if len(resp.Runs) > 0 {
				older = common.RunID(resp.Runs[0].Id)
			}
		}
		return newer, older, nil
	}
}

// uiTryjob is fed to the template to draw tryjob chips.
type uiTryjob struct {
	Link, Class, Display string
}

func makeUITryjob(t *run.Tryjob) *uiTryjob {
	// TODO(crbug/1233963):  Make sure we are not leaking any sensitive info
	// based on Read Perms. E.g. internal builder name.
	builder := t.Definition.GetBuildbucket().Builder
	return &uiTryjob{
		Link:    tryjob.ExternalID(t.ExternalId).MustURL(),
		Class:   toCSSClass(t),
		Display: fmt.Sprintf("%s/%s/%s", builder.Project, builder.Bucket, builder.Builder),
	}
}

// toCSSClass returns a css class for styling a tryjob chip based on its
// status and its result's status.
func toCSSClass(t *run.Tryjob) string {
	switch t.GetStatus() {
	case tryjob.Status_PENDING:
		return "not-started"
	case tryjob.Status_CANCELLED:
		return "cancelled"
	case tryjob.Status_TRIGGERED, tryjob.Status_ENDED:
		switch t.GetResult().GetStatus() {
		case tryjob.Result_FAILED_PERMANENTLY, tryjob.Result_FAILED_TRANSIENTLY:
			return "failed"
		case tryjob.Result_SUCCEEDED:
			return "passed"
		default:
			if t.GetStatus() == tryjob.Status_ENDED {
				panic("Tryjob status is ENDED but result status is not set")
			}
			return "running"
		}
	default:
		panic(fmt.Errorf("unknown tryjob status: %s", t.GetStatus()))
	}
}

func logTypeString(rle *run.LogEntry) string {
	switch v := rle.GetKind().(type) {
	case *run.LogEntry_TryjobsUpdated_:
		return "Tryjob Updated"
	case *run.LogEntry_TryjobsRequirementUpdated_:
		return "Tryjob Requirements Updated"
	case *run.LogEntry_ConfigChanged_:
		return "Config Changed"
	case *run.LogEntry_Started_:
		return "Started"
	case *run.LogEntry_Created_:
		return "Created"
	case *run.LogEntry_TreeChecked_:
		if v.TreeChecked.Open {
			return "Tree Found Open"
		}
		return "Tree Found Closed"
	case *run.LogEntry_Info_:
		return v.Info.Label
	case *run.LogEntry_AcquiredSubmitQueue_:
		return "Acquired Submit Queue"
	case *run.LogEntry_ReleasedSubmitQueue_:
		return "Released Submit Queue"
	case *run.LogEntry_Waitlisted_:
		return "Waitlisted for Submit Queue"
	case *run.LogEntry_SubmissionFailure_:
		if v.SubmissionFailure.Event.GetResult() == eventpb.SubmissionResult_FAILED_TRANSIENT {
			return "Transient Submission Failure"
		}
		return "Final Submission Failure"
	case *run.LogEntry_ClSubmitted:
		return "CL Submission"
	case *run.LogEntry_RunEnded_:
		return "CV Finished Work on this Run"
	default:
		return fmt.Sprintf("FIXME: Unknown Kind of LogEntry %T", v)
	}
}

// groupTryjobsByStatus puts tryjobs in the list into separate lists by status.
func groupTryjobsByStatus(tjs []*run.Tryjob) map[string][]*run.Tryjob {
	ret := map[string][]*run.Tryjob{}
	for _, t := range tjs {
		k := strings.Title(strings.ToLower(t.Status.String()))
		ret[k] = append(ret[k], t)
	}
	return ret
}

func indexOf(runs []*adminpb.GetRunResponse, runID common.RunID) int {
	for i := 0; i < len(runs); i++ {
		if runs[i].Id == string(runID) {
			return i
		}
	}
	return -1
}

func makeURLMap(cls []*run.RunCL) map[common.CLID]string {
	ret := make(map[common.CLID]string, len(cls))
	for _, cl := range cls {
		ret[cl.ID] = cl.ExternalID.MustURL()
	}
	return ret
}
<commit_msg>[cv][ui] stop using admin in run_details.<commit_after>// Copyright 2021 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package userhtml

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"golang.org/x/sync/errgroup"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/templates"

	"go.chromium.org/luci/cv/internal/acls"
	"go.chromium.org/luci/cv/internal/common"
	adminpb "go.chromium.org/luci/cv/internal/rpc/admin/api"
	"go.chromium.org/luci/cv/internal/run"
	"go.chromium.org/luci/cv/internal/run/eventpb"
	"go.chromium.org/luci/cv/internal/tryjob"
)

func runDetails(c *router.Context) {
	ctx := c.Context

	rID := fmt.Sprintf("%s/%s", c.Params.ByName("Project"), c.Params.ByName("Run"))

	// Load the Run, checking its existence and ACLs.
	r, err := acls.LoadRun(ctx, common.RunID(rID))
	if err != nil {
		errPage(c, err)
		return
	}

	// Reverse entries to get the most recent at the top of the list.
	logEntries, err := run.LoadRunLogEntries(ctx, r.ID)
	if err != nil {
		errPage(c, err)
		return
	}
	nEntries := len(logEntries)
	for i := 0; i < nEntries/2; i++ {
		logEntries[i], logEntries[nEntries-1-i] = logEntries[nEntries-1-i], logEntries[i]
	}

	cls, err := run.LoadRunCLs(ctx, common.RunID(r.ID), r.CLs)
	if err != nil {
		errPage(c, err)
		return
	}

	// Sort a stack of CLs by external ID.
	sort.Slice(cls, func(i, j int) bool { return cls[i].ExternalID < cls[j].ExternalID })

	// Compute next and previous runs for all cls in parallel.
	clsAndLinks, err := computeCLsAndLinks(ctx, cls, r.ID)
	if err != nil {
		errPage(c, err)
		return
	}
	clidToURL := makeURLMap(cls)

	templates.MustRender(ctx, c.Writer, "pages/run_details.html", templates.Args{
		"Run":  r,
		"Logs": logEntries,
		"Cls":  clsAndLinks,
		"RelTime": func(ts time.Time) string {
			return humanize.RelTime(ts, startTime(ctx), "ago", "from now")
		},
		"LogMessage": func(rle *run.LogEntry) string {
			switch v := rle.GetKind().(type) {
			case *run.LogEntry_Info_:
				return v.Info.Message
			case *run.LogEntry_ClSubmitted:
				return StringifySubmissionSuccesses(clidToURL, v.ClSubmitted, int64(len(r.CLs)))
			case *run.LogEntry_SubmissionFailure_:
				return StringifySubmissionFailureReason(clidToURL, v.SubmissionFailure.Event)
			default:
				return ""
			}
		},
		"AllTryjobs": r.Tryjobs.GetTryjobs(),
	})
}

type clAndNeighborRuns struct {
	Prev, Next common.RunID
	CL         *run.RunCL
}

func (cl *clAndNeighborRuns) URLWithPatchset() string {
	return fmt.Sprintf("%s/%d", cl.CL.ExternalID.MustURL(), cl.CL.Detail.GetPatchset())
}

func (cl *clAndNeighborRuns) ShortWithPatchset() string {
	return fmt.Sprintf("%s/%d", displayCLExternalID(cl.CL.ExternalID), cl.CL.Detail.GetPatchset())
}

func computeCLsAndLinks(ctx context.Context, cls []*run.RunCL, rid common.RunID) ([]*clAndNeighborRuns, error) {
	ret := make([]*clAndNeighborRuns, len(cls))
	eg, ctx := errgroup.WithContext(ctx)
	for i, cl := range cls {
		i, cl := i, cl
		eg.Go(func() error {
			prev, next, err := getNeighborsByCL(ctx, cl, rid)
			if err != nil {
				return errors.Annotate(err, "unable to get previous and next runs for cl %s", cl.ExternalID).Err()
			}
			ret[i] = &clAndNeighborRuns{
				Prev: prev,
				Next: next,
				CL:   cl,
			}
			return nil
		})
	}
	return ret, eg.Wait()
}

func getNeighborsByCL(ctx context.Context, cl *run.RunCL, rID common.RunID) (common.RunID, common.RunID, error) {
	eg, ctx := errgroup.WithContext(ctx)

	prev := common.RunID("")
	eg.Go(func() error {
		qb := run.CLQueryBuilder{CLID: cl.ID, Limit: 1}.BeforeInProject(rID)
		switch keys, err := qb.GetAllRunKeys(ctx); {
		case err != nil:
			return err
		case len(keys) == 1:
			prev = common.RunID(keys[0].StringID())
			// It's OK to return the prev Run ID w/o checking ACLs because even if the
			// user can't see the prev Run, user is already served this Run's ID, which
			// contains the same LUCI project.
			if prev.LUCIProject() != rID.LUCIProject() {
				panic(fmt.Errorf("CLQueryBuilder.Before didn't limit project: %q vs %q", prev, rID))
			}
		}
		return nil
	})

	next := common.RunID("")
	eg.Go(func() error {
		qb := run.CLQueryBuilder{CLID: cl.ID, Limit: 1, Descending: true}.AfterInProject(rID)
		switch keys, err := qb.GetAllRunKeys(ctx); {
		case err != nil:
			return err
		case len(keys) == 1:
			next = common.RunID(keys[0].StringID())
			// It's OK to return the next Run ID w/o checking ACLs because even if the
			// user can't see the next Run, user is already served this Run's ID, which
			// contains the same LUCI project.
			if next.LUCIProject() != rID.LUCIProject() {
				panic(fmt.Errorf("CLQueryBuilder.After didn't limit project: %q vs %q", next, rID))
			}
		}
		return nil
	})

	err := eg.Wait()
	return prev, next, err
}

// uiTryjob is fed to the template to draw tryjob chips.
type uiTryjob struct {
	Link, Class, Display string
}

func makeUITryjob(t *run.Tryjob) *uiTryjob {
	// TODO(crbug/1233963):  Make sure we are not leaking any sensitive info
	// based on Read Perms. E.g. internal builder name.
	builder := t.Definition.GetBuildbucket().Builder
	return &uiTryjob{
		Link:    tryjob.ExternalID(t.ExternalId).MustURL(),
		Class:   toCSSClass(t),
		Display: fmt.Sprintf("%s/%s/%s", builder.Project, builder.Bucket, builder.Builder),
	}
}

// toCSSClass returns a css class for styling a tryjob chip based on its
// status and its result's status.
func toCSSClass(t *run.Tryjob) string {
	switch t.GetStatus() {
	case tryjob.Status_PENDING:
		return "not-started"
	case tryjob.Status_CANCELLED:
		return "cancelled"
	case tryjob.Status_TRIGGERED, tryjob.Status_ENDED:
		switch t.GetResult().GetStatus() {
		case tryjob.Result_FAILED_PERMANENTLY, tryjob.Result_FAILED_TRANSIENTLY:
			return "failed"
		case tryjob.Result_SUCCEEDED:
			return "passed"
		default:
			if t.GetStatus() == tryjob.Status_ENDED {
				panic("Tryjob status is ENDED but result status is not set")
			}
			return "running"
		}
	default:
		panic(fmt.Errorf("unknown tryjob status: %s", t.GetStatus()))
	}
}

func logTypeString(rle *run.LogEntry) string {
	switch v := rle.GetKind().(type) {
	case *run.LogEntry_TryjobsUpdated_:
		return "Tryjob Updated"
	case *run.LogEntry_TryjobsRequirementUpdated_:
		return "Tryjob Requirements Updated"
	case *run.LogEntry_ConfigChanged_:
		return "Config Changed"
	case *run.LogEntry_Started_:
		return "Started"
	case *run.LogEntry_Created_:
		return "Created"
	case *run.LogEntry_TreeChecked_:
		if v.TreeChecked.Open {
			return "Tree Found Open"
		}
		return "Tree Found Closed"
	case *run.LogEntry_Info_:
		return v.Info.Label
	case *run.LogEntry_AcquiredSubmitQueue_:
		return "Acquired Submit Queue"
	case *run.LogEntry_ReleasedSubmitQueue_:
		return "Released Submit Queue"
	case *run.LogEntry_Waitlisted_:
		return "Waitlisted for Submit Queue"
	case *run.LogEntry_SubmissionFailure_:
		if v.SubmissionFailure.Event.GetResult() == eventpb.SubmissionResult_FAILED_TRANSIENT {
			return "Transient Submission Failure"
		}
		return "Final Submission Failure"
	case *run.LogEntry_ClSubmitted:
		return "CL Submission"
	case *run.LogEntry_RunEnded_:
		return "CV Finished Work on this Run"
	default:
		return fmt.Sprintf("FIXME: Unknown Kind of LogEntry %T", v)
	}
}

// groupTryjobsByStatus puts tryjobs in the list into separate lists by status.
func groupTryjobsByStatus(tjs []*run.Tryjob) map[string][]*run.Tryjob {
	ret := map[string][]*run.Tryjob{}
	for _, t := range tjs {
		k := strings.Title(strings.ToLower(t.Status.String()))
		ret[k] = append(ret[k], t)
	}
	return ret
}

func indexOf(runs []*adminpb.GetRunResponse, runID common.RunID) int {
	for i := 0; i < len(runs); i++ {
		if runs[i].Id == string(runID) {
			return i
		}
	}
	return -1
}

func makeURLMap(cls []*run.RunCL) map[common.CLID]string {
	ret := make(map[common.CLID]string, len(cls))
	for _, cl := range cls {
		ret[cl.ID] = cl.ExternalID.MustURL()
	}
	return ret
}
<|endoftext|>


<commit_before>// +build windows

package lcow

import (
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	"github.com/Microsoft/hcsshim"
	"github.com/Microsoft/opengcs/client"
	"github.com/sirupsen/logrus"
)

// Code for all the service VM management for the LCOW graphdriver

var errVMisTerminating = errors.New("service VM is shutting down")
var errVMUnknown = errors.New("service vm id is unknown")
var errVMStillHasReference = errors.New("Attemping to delete a VM that is still being used")

// serviceVMMap is the struct representing the id -> service VM mapping.
type serviceVMMap struct {
	sync.Mutex
	svms map[string]*serviceVMMapItem
}

// serviceVMMapItem is our internal structure representing an item in our
// map of service VMs we are maintaining.
type serviceVMMapItem struct {
	svm      *serviceVM // actual service vm object
	refCount int        // refcount for VM
}

type serviceVM struct {
	sync.Mutex                     // Serialises operations being performed in this service VM.
	scratchAttached bool           // Has a scratch been attached?
	config          *client.Config // Represents the service VM item.

	// Indicates that the vm is started
	startStatus chan interface{}
	startError  error

	// Indicates that the vm is stopped
	stopStatus chan interface{}
	stopError  error

	attachedVHDs map[string]int // Map ref counting all the VHDS we've hot-added/hot-removed.
	unionMounts  map[string]int // Map ref counting all the union filesystems we mounted.
}

// add will add an id to the service vm map. There are three cases:
// 	- entry doesn't exist:
// 		- add id to map and return a new vm that the caller can manually configure+start
//	- entry does exist
//  	- return vm in map and increment ref count
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
func (svmMap *serviceVMMap) add(id string) (svm *serviceVM, alreadyExists bool, err error) {
	svmMap.Lock()
	defer svmMap.Unlock()
	if svm, ok := svmMap.svms[id]; ok {
		if svm.refCount == 0 {
			return svm.svm, true, errVMisTerminating
		}
		svm.refCount++
		return svm.svm, true, nil
	}

	// Doesn't exist, so create an empty svm to put into map and return
	newSVM := &serviceVM{
		startStatus:  make(chan interface{}),
		stopStatus:   make(chan interface{}),
		attachedVHDs: make(map[string]int),
		unionMounts:  make(map[string]int),
		config:       &client.Config{},
	}
	svmMap.svms[id] = &serviceVMMapItem{
		svm:      newSVM,
		refCount: 1,
	}
	return newSVM, false, nil
}

// get will get the service vm from the map. There are three cases:
// 	- entry doesn't exist:
// 		- return errVMUnknown
//	- entry does exist
//  	- return vm with no error
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
func (svmMap *serviceVMMap) get(id string) (*serviceVM, error) {
	svmMap.Lock()
	defer svmMap.Unlock()
	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, errVMisTerminating
	}
	return svm.svm, nil
}

// decrementRefCount decrements the ref count of the given ID from the map. There are four cases:
// 	- entry doesn't exist:
// 		- return errVMUnknown
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
//	- entry does exist but ref count is 1
//  	- return vm and set lastRef to true. The caller can then stop the vm, delete the id from this map
//      - and execute svm.signalStopFinished to signal the threads that the svm has been terminated.
//	- entry does exist and ref count > 1
//		- just reduce ref count and return svm
func (svmMap *serviceVMMap) decrementRefCount(id string) (_ *serviceVM, lastRef bool, _ error) {
	svmMap.Lock()
	defer svmMap.Unlock()

	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, false, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, false, errVMisTerminating
	}
	svm.refCount--
	return svm.svm, svm.refCount == 0, nil
}

// setRefCountZero works the same way as decrementRefCount, but sets ref count to 0 instead of decrementing it.
func (svmMap *serviceVMMap) setRefCountZero(id string) (*serviceVM, error) {
	svmMap.Lock()
	defer svmMap.Unlock()

	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, errVMisTerminating
	}
	svm.refCount = 0
	return svm.svm, nil
}

// deleteID deletes the given ID from the map. If the refcount is not 0 or the
// VM does not exist, then this function returns an error.
func (svmMap *serviceVMMap) deleteID(id string) error {
	svmMap.Lock()
	defer svmMap.Unlock()
	svm, ok := svmMap.svms[id]
	if !ok {
		return errVMUnknown
	}
	if svm.refCount != 0 {
		return errVMStillHasReference
	}
	delete(svmMap.svms, id)
	return nil
}

func (svm *serviceVM) signalStartFinished(err error) {
	svm.Lock()
	svm.startError = err
	svm.Unlock()
	close(svm.startStatus)
}

func (svm *serviceVM) getStartError() error {
	<-svm.startStatus
	svm.Lock()
	defer svm.Unlock()
	return svm.startError
}

func (svm *serviceVM) signalStopFinished(err error) {
	svm.Lock()
	svm.stopError = err
	svm.Unlock()
	close(svm.stopStatus)
}

func (svm *serviceVM) getStopError() error {
	<-svm.stopStatus
	svm.Lock()
	defer svm.Unlock()
	return svm.stopError
}

// hotAddVHDs waits for the service vm to start and then attaches the vhds.
func (svm *serviceVM) hotAddVHDs(mvds ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}
	return svm.hotAddVHDsAtStart(mvds...)
}

// hotAddVHDsAtStart works the same way as hotAddVHDs but does not wait for the VM to start.
func (svm *serviceVM) hotAddVHDsAtStart(mvds ...hcsshim.MappedVirtualDisk) error {
	svm.Lock()
	defer svm.Unlock()
	for i, mvd := range mvds {
		if _, ok := svm.attachedVHDs[mvd.HostPath]; ok {
			svm.attachedVHDs[mvd.HostPath]++
			continue
		}

		if err := svm.config.HotAddVhd(mvd.HostPath, mvd.ContainerPath, mvd.ReadOnly, !mvd.AttachOnly); err != nil {
			svm.hotRemoveVHDsAtStart(mvds[:i]...)
			return err
		}
		svm.attachedVHDs[mvd.HostPath] = 1
	}
	return nil
}

// hotRemoveVHDs waits for the service vm to start and then removes the vhds.
func (svm *serviceVM) hotRemoveVHDs(mvds ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}
	return svm.hotRemoveVHDsAtStart(mvds...)
}

// hotRemoveVHDsAtStart works the same way as hotRemoveVHDs but does not wait for the VM to start.
func (svm *serviceVM) hotRemoveVHDsAtStart(mvds ...hcsshim.MappedVirtualDisk) error {
	svm.Lock()
	defer svm.Unlock()
	var retErr error
	for _, mvd := range mvds {
		if _, ok := svm.attachedVHDs[mvd.HostPath]; !ok {
			// We continue instead of returning an error if we try to hot remove a non-existent VHD.
			// This is because one of the callers of the function is graphdriver.Put(). Since graphdriver.Get()
			// defers the VM start to the first operation, it's possible that nothing have been hot-added
			// when Put() is called. To avoid Put returning an error in that case, we simply continue if we
			// don't find the vhd attached.
			continue
		}

		if svm.attachedVHDs[mvd.HostPath] > 1 {
			svm.attachedVHDs[mvd.HostPath]--
			continue
		}

		// last VHD, so remove from VM and map
		if err := svm.config.HotRemoveVhd(mvd.HostPath); err == nil {
			delete(svm.attachedVHDs, mvd.HostPath)
		} else {
			// Take note of the error, but still continue to remove the other VHDs
			logrus.Warnf("Failed to hot remove %s: %s", mvd.HostPath, err)
			if retErr == nil {
				retErr = err
			}
		}
	}
	return retErr
}

func (svm *serviceVM) createExt4VHDX(destFile string, sizeGB uint32, cacheFile string) error {
	if err := svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	return svm.config.CreateExt4Vhdx(destFile, sizeGB, cacheFile)
}

func (svm *serviceVM) createUnionMount(mountName string, mvds ...hcsshim.MappedVirtualDisk) (err error) {
	if len(mvds) == 0 {
		return fmt.Errorf("createUnionMount: error must have at least 1 layer")
	}

	if err = svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	if _, ok := svm.unionMounts[mountName]; ok {
		svm.unionMounts[mountName]++
		return nil
	}

	var lowerLayers []string
	if mvds[0].ReadOnly {
		lowerLayers = append(lowerLayers, mvds[0].ContainerPath)
	}

	for i := 1; i < len(mvds); i++ {
		lowerLayers = append(lowerLayers, mvds[i].ContainerPath)
	}

	logrus.Debugf("Doing the overlay mount with union directory=%s", mountName)
	if err = svm.runProcess(fmt.Sprintf("mkdir -p %s", mountName), nil, nil, nil); err != nil {
		return err
	}

	var cmd string
	if mvds[0].ReadOnly {
		// Readonly overlay
		cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s %s",
			strings.Join(lowerLayers, ","),
			mountName)
	} else {
		upper := fmt.Sprintf("%s/upper", mvds[0].ContainerPath)
		work := fmt.Sprintf("%s/work", mvds[0].ContainerPath)

		if err = svm.runProcess(fmt.Sprintf("mkdir -p %s %s", upper, work), nil, nil, nil); err != nil {
			return err
		}

		cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s,upperdir=%s,workdir=%s %s",
			strings.Join(lowerLayers, ":"),
			upper,
			work,
			mountName)
	}

	logrus.Debugf("createUnionMount: Executing mount=%s", cmd)
	if err = svm.runProcess(cmd, nil, nil, nil); err != nil {
		return err
	}

	svm.unionMounts[mountName] = 1
	return nil
}

func (svm *serviceVM) deleteUnionMount(mountName string, disks ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	if _, ok := svm.unionMounts[mountName]; !ok {
		return nil
	}

	if svm.unionMounts[mountName] > 1 {
		svm.unionMounts[mountName]--
		return nil
	}

	logrus.Debugf("Removing union mount %s", mountName)
	if err := svm.runProcess(fmt.Sprintf("umount %s", mountName), nil, nil, nil); err != nil {
		return err
	}

	delete(svm.unionMounts, mountName)
	return nil
}

func (svm *serviceVM) runProcess(command string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
	process, err := svm.config.RunProcess(command, stdin, stdout, stderr)
	if err != nil {
		return err
	}
	defer process.Close()

	process.WaitTimeout(time.Duration(int(time.Second) * svm.config.UvmTimeoutSeconds))
	exitCode, err := process.ExitCode()
	if err != nil {
		return err
	}

	if exitCode != 0 {
		return fmt.Errorf("svm.runProcess: command %s failed with exit code %d", command, exitCode)
	}
	return nil
}
<commit_msg>LCOW: Graphdriver fix deadlock<commit_after>// +build windows

package lcow

import (
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	"github.com/Microsoft/hcsshim"
	"github.com/Microsoft/opengcs/client"
	"github.com/sirupsen/logrus"
)

// Code for all the service VM management for the LCOW graphdriver

var errVMisTerminating = errors.New("service VM is shutting down")
var errVMUnknown = errors.New("service vm id is unknown")
var errVMStillHasReference = errors.New("Attemping to delete a VM that is still being used")

// serviceVMMap is the struct representing the id -> service VM mapping.
type serviceVMMap struct {
	sync.Mutex
	svms map[string]*serviceVMMapItem
}

// serviceVMMapItem is our internal structure representing an item in our
// map of service VMs we are maintaining.
type serviceVMMapItem struct {
	svm      *serviceVM // actual service vm object
	refCount int        // refcount for VM
}

type serviceVM struct {
	sync.Mutex                     // Serialises operations being performed in this service VM.
	scratchAttached bool           // Has a scratch been attached?
	config          *client.Config // Represents the service VM item.

	// Indicates that the vm is started
	startStatus chan interface{}
	startError  error

	// Indicates that the vm is stopped
	stopStatus chan interface{}
	stopError  error

	attachedVHDs map[string]int // Map ref counting all the VHDS we've hot-added/hot-removed.
	unionMounts  map[string]int // Map ref counting all the union filesystems we mounted.
}

// add will add an id to the service vm map. There are three cases:
// 	- entry doesn't exist:
// 		- add id to map and return a new vm that the caller can manually configure+start
//	- entry does exist
//  	- return vm in map and increment ref count
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
func (svmMap *serviceVMMap) add(id string) (svm *serviceVM, alreadyExists bool, err error) {
	svmMap.Lock()
	defer svmMap.Unlock()
	if svm, ok := svmMap.svms[id]; ok {
		if svm.refCount == 0 {
			return svm.svm, true, errVMisTerminating
		}
		svm.refCount++
		return svm.svm, true, nil
	}

	// Doesn't exist, so create an empty svm to put into map and return
	newSVM := &serviceVM{
		startStatus:  make(chan interface{}),
		stopStatus:   make(chan interface{}),
		attachedVHDs: make(map[string]int),
		unionMounts:  make(map[string]int),
		config:       &client.Config{},
	}
	svmMap.svms[id] = &serviceVMMapItem{
		svm:      newSVM,
		refCount: 1,
	}
	return newSVM, false, nil
}

// get will get the service vm from the map. There are three cases:
// 	- entry doesn't exist:
// 		- return errVMUnknown
//	- entry does exist
//  	- return vm with no error
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
func (svmMap *serviceVMMap) get(id string) (*serviceVM, error) {
	svmMap.Lock()
	defer svmMap.Unlock()
	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, errVMisTerminating
	}
	return svm.svm, nil
}

// decrementRefCount decrements the ref count of the given ID from the map. There are four cases:
// 	- entry doesn't exist:
// 		- return errVMUnknown
//  - entry does exist but the ref count is 0
//		- return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop
//	- entry does exist but ref count is 1
//  	- return vm and set lastRef to true. The caller can then stop the vm, delete the id from this map
//      - and execute svm.signalStopFinished to signal the threads that the svm has been terminated.
//	- entry does exist and ref count > 1
//		- just reduce ref count and return svm
func (svmMap *serviceVMMap) decrementRefCount(id string) (_ *serviceVM, lastRef bool, _ error) {
	svmMap.Lock()
	defer svmMap.Unlock()

	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, false, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, false, errVMisTerminating
	}
	svm.refCount--
	return svm.svm, svm.refCount == 0, nil
}

// setRefCountZero works the same way as decrementRefCount, but sets ref count to 0 instead of decrementing it.
func (svmMap *serviceVMMap) setRefCountZero(id string) (*serviceVM, error) {
	svmMap.Lock()
	defer svmMap.Unlock()

	svm, ok := svmMap.svms[id]
	if !ok {
		return nil, errVMUnknown
	}
	if svm.refCount == 0 {
		return svm.svm, errVMisTerminating
	}
	svm.refCount = 0
	return svm.svm, nil
}

// deleteID deletes the given ID from the map. If the refcount is not 0 or the
// VM does not exist, then this function returns an error.
func (svmMap *serviceVMMap) deleteID(id string) error {
	svmMap.Lock()
	defer svmMap.Unlock()
	svm, ok := svmMap.svms[id]
	if !ok {
		return errVMUnknown
	}
	if svm.refCount != 0 {
		return errVMStillHasReference
	}
	delete(svmMap.svms, id)
	return nil
}

func (svm *serviceVM) signalStartFinished(err error) {
	svm.Lock()
	svm.startError = err
	svm.Unlock()
	close(svm.startStatus)
}

func (svm *serviceVM) getStartError() error {
	<-svm.startStatus
	svm.Lock()
	defer svm.Unlock()
	return svm.startError
}

func (svm *serviceVM) signalStopFinished(err error) {
	svm.Lock()
	svm.stopError = err
	svm.Unlock()
	close(svm.stopStatus)
}

func (svm *serviceVM) getStopError() error {
	<-svm.stopStatus
	svm.Lock()
	defer svm.Unlock()
	return svm.stopError
}

// hotAddVHDs waits for the service vm to start and then attaches the vhds.
func (svm *serviceVM) hotAddVHDs(mvds ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}
	return svm.hotAddVHDsAtStart(mvds...)
}

// hotAddVHDsAtStart works the same way as hotAddVHDs but does not wait for the VM to start.
func (svm *serviceVM) hotAddVHDsAtStart(mvds ...hcsshim.MappedVirtualDisk) error {
	svm.Lock()
	defer svm.Unlock()
	for i, mvd := range mvds {
		if _, ok := svm.attachedVHDs[mvd.HostPath]; ok {
			svm.attachedVHDs[mvd.HostPath]++
			continue
		}

		if err := svm.config.HotAddVhd(mvd.HostPath, mvd.ContainerPath, mvd.ReadOnly, !mvd.AttachOnly); err != nil {
			svm.hotRemoveVHDsNoLock(mvds[:i]...)
			return err
		}
		svm.attachedVHDs[mvd.HostPath] = 1
	}
	return nil
}

// hotRemoveVHDs waits for the service vm to start and then removes the vhds.
// The service VM must not be locked when calling this function.
func (svm *serviceVM) hotRemoveVHDs(mvds ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}
	svm.Lock()
	defer svm.Unlock()
	return svm.hotRemoveVHDsNoLock(mvds...)
}

// hotRemoveVHDsNoLock removes VHDs from a service VM. When calling this function,
// the contract is the service VM lock must be held.
func (svm *serviceVM) hotRemoveVHDsNoLock(mvds ...hcsshim.MappedVirtualDisk) error {
	var retErr error
	for _, mvd := range mvds {
		if _, ok := svm.attachedVHDs[mvd.HostPath]; !ok {
			// We continue instead of returning an error if we try to hot remove a non-existent VHD.
			// This is because one of the callers of the function is graphdriver.Put(). Since graphdriver.Get()
			// defers the VM start to the first operation, it's possible that nothing have been hot-added
			// when Put() is called. To avoid Put returning an error in that case, we simply continue if we
			// don't find the vhd attached.
			continue
		}

		if svm.attachedVHDs[mvd.HostPath] > 1 {
			svm.attachedVHDs[mvd.HostPath]--
			continue
		}

		// last VHD, so remove from VM and map
		if err := svm.config.HotRemoveVhd(mvd.HostPath); err == nil {
			delete(svm.attachedVHDs, mvd.HostPath)
		} else {
			// Take note of the error, but still continue to remove the other VHDs
			logrus.Warnf("Failed to hot remove %s: %s", mvd.HostPath, err)
			if retErr == nil {
				retErr = err
			}
		}
	}
	return retErr
}

func (svm *serviceVM) createExt4VHDX(destFile string, sizeGB uint32, cacheFile string) error {
	if err := svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	return svm.config.CreateExt4Vhdx(destFile, sizeGB, cacheFile)
}

func (svm *serviceVM) createUnionMount(mountName string, mvds ...hcsshim.MappedVirtualDisk) (err error) {
	if len(mvds) == 0 {
		return fmt.Errorf("createUnionMount: error must have at least 1 layer")
	}

	if err = svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	if _, ok := svm.unionMounts[mountName]; ok {
		svm.unionMounts[mountName]++
		return nil
	}

	var lowerLayers []string
	if mvds[0].ReadOnly {
		lowerLayers = append(lowerLayers, mvds[0].ContainerPath)
	}

	for i := 1; i < len(mvds); i++ {
		lowerLayers = append(lowerLayers, mvds[i].ContainerPath)
	}

	logrus.Debugf("Doing the overlay mount with union directory=%s", mountName)
	if err = svm.runProcess(fmt.Sprintf("mkdir -p %s", mountName), nil, nil, nil); err != nil {
		return err
	}

	var cmd string
	if mvds[0].ReadOnly {
		// Readonly overlay
		cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s %s",
			strings.Join(lowerLayers, ","),
			mountName)
	} else {
		upper := fmt.Sprintf("%s/upper", mvds[0].ContainerPath)
		work := fmt.Sprintf("%s/work", mvds[0].ContainerPath)

		if err = svm.runProcess(fmt.Sprintf("mkdir -p %s %s", upper, work), nil, nil, nil); err != nil {
			return err
		}

		cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s,upperdir=%s,workdir=%s %s",
			strings.Join(lowerLayers, ":"),
			upper,
			work,
			mountName)
	}

	logrus.Debugf("createUnionMount: Executing mount=%s", cmd)
	if err = svm.runProcess(cmd, nil, nil, nil); err != nil {
		return err
	}

	svm.unionMounts[mountName] = 1
	return nil
}

func (svm *serviceVM) deleteUnionMount(mountName string, disks ...hcsshim.MappedVirtualDisk) error {
	if err := svm.getStartError(); err != nil {
		return err
	}

	svm.Lock()
	defer svm.Unlock()
	if _, ok := svm.unionMounts[mountName]; !ok {
		return nil
	}

	if svm.unionMounts[mountName] > 1 {
		svm.unionMounts[mountName]--
		return nil
	}

	logrus.Debugf("Removing union mount %s", mountName)
	if err := svm.runProcess(fmt.Sprintf("umount %s", mountName), nil, nil, nil); err != nil {
		return err
	}

	delete(svm.unionMounts, mountName)
	return nil
}

func (svm *serviceVM) runProcess(command string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
	process, err := svm.config.RunProcess(command, stdin, stdout, stderr)
	if err != nil {
		return err
	}
	defer process.Close()

	process.WaitTimeout(time.Duration(int(time.Second) * svm.config.UvmTimeoutSeconds))
	exitCode, err := process.ExitCode()
	if err != nil {
		return err
	}

	if exitCode != 0 {
		return fmt.Errorf("svm.runProcess: command %s failed with exit code %d", command, exitCode)
	}
	return nil
}
<|endoftext|>


<commit_before>package azurerm

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmNetworkInterface() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmNetworkInterfaceCreate,
		Read:   resourceArmNetworkInterfaceRead,
		Update: resourceArmNetworkInterfaceCreate,
		Delete: resourceArmNetworkInterfaceDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": &schema.Schema{
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"resource_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"network_security_group_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"mac_address": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"private_ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"virtual_machine_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"ip_configuration": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"subnet_id": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"private_ip_address": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address_allocation": &schema.Schema{
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateNetworkInterfacePrivateIpAddressAllocation,
						},

						"public_ip_address_id": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"load_balancer_backend_address_pools_ids": &schema.Schema{
							Type:     schema.TypeSet,
							Optional: true,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},

						"load_balancer_inbound_nat_rules_ids": &schema.Schema{
							Type:     schema.TypeSet,
							Optional: true,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
				Set: resourceArmNetworkInterfaceIpConfigurationHash,
			},

			"dns_servers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"internal_dns_name_label": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"applied_dns_servers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"internal_fqdn": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	ifaceClient := client.ifaceClient

	log.Printf("[INFO] preparing arguments for Azure ARM Network Interface creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})

	properties := network.InterfacePropertiesFormat{}

	if v, ok := d.GetOk("network_security_group_id"); ok {
		nsgId := v.(string)
		properties.NetworkSecurityGroup = &network.SecurityGroup{
			ID: &nsgId,
		}
	}

	dns, hasDns := d.GetOk("dns_servers")
	nameLabel, hasNameLabel := d.GetOk("internal_dns_name_label")
	if hasDns || hasNameLabel {
		ifaceDnsSettings := network.InterfaceDNSSettings{}

		if hasDns {
			var dnsServers []string
			dns := dns.(*schema.Set).List()
			for _, v := range dns {
				str := v.(string)
				dnsServers = append(dnsServers, str)
			}
			ifaceDnsSettings.DNSServers = &dnsServers
		}

		if hasNameLabel {
			name_label := nameLabel.(string)
			ifaceDnsSettings.InternalDNSNameLabel = &name_label

		}

		properties.DNSSettings = &ifaceDnsSettings
	}

	ipConfigs, sgErr := expandAzureRmNetworkInterfaceIpConfigurations(d)
	if sgErr != nil {
		return fmt.Errorf("Error Building list of Network Interface IP Configurations: %s", sgErr)
	}
	if len(ipConfigs) > 0 {
		properties.IPConfigurations = &ipConfigs
	}

	iface := network.Interface{
		Name:       &name,
		Location:   &location,
		Properties: &properties,
		Tags:       expandTags(tags),
	}

	resp, err := ifaceClient.CreateOrUpdate(resGroup, name, iface)
	if err != nil {
		return err
	}

	d.SetId(*resp.ID)

	log.Printf("[DEBUG] Waiting for Network Interface (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: networkInterfaceStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for Network Interface (%s) to become available: %s", name, err)
	}

	return resourceArmNetworkInterfaceRead(d, meta)
}

func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
	ifaceClient := meta.(*ArmClient).ifaceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["networkInterfaces"]

	resp, err := ifaceClient.Get(resGroup, name, "")
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error making Read request on Azure Network Interface %s: %s", name, err)
	}

	iface := *resp.Properties

	if iface.MacAddress != nil {
		if *iface.MacAddress != "" {
			d.Set("mac_address", iface.MacAddress)
		}
	}

	if iface.IPConfigurations != nil && len(*iface.IPConfigurations) > 0 {
		var privateIPAddress *string
		///TODO: Change this to a loop when https://github.com/Azure/azure-sdk-for-go/issues/259 is fixed
		if (*iface.IPConfigurations)[0].Properties != nil {
			privateIPAddress = (*iface.IPConfigurations)[0].Properties.PrivateIPAddress
		}

		if *privateIPAddress != "" {
			d.Set("private_ip_address", *privateIPAddress)
		}
	}

	if iface.VirtualMachine != nil {
		if *iface.VirtualMachine.ID != "" {
			d.Set("virtual_machine_id", *iface.VirtualMachine.ID)
		}
	}

	if iface.DNSSettings != nil {
		if iface.DNSSettings.AppliedDNSServers != nil && len(*iface.DNSSettings.AppliedDNSServers) > 0 {
			dnsServers := make([]string, 0, len(*iface.DNSSettings.AppliedDNSServers))
			for _, dns := range *iface.DNSSettings.AppliedDNSServers {
				dnsServers = append(dnsServers, dns)
			}

			if err := d.Set("applied_dns_servers", dnsServers); err != nil {
				return err
			}
		}

		if iface.DNSSettings.InternalFqdn != nil && *iface.DNSSettings.InternalFqdn != "" {
			d.Set("internal_fqdn", iface.DNSSettings.InternalFqdn)
		}
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
	ifaceClient := meta.(*ArmClient).ifaceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["networkInterfaces"]

	_, err = ifaceClient.Delete(resGroup, name)

	return err
}

func networkInterfaceStateRefreshFunc(client *ArmClient, resourceGroupName string, ifaceName string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		res, err := client.ifaceClient.Get(resourceGroupName, ifaceName, "")
		if err != nil {
			return nil, "", fmt.Errorf("Error issuing read request in networkInterfaceStateRefreshFunc to Azure ARM for network interace '%s' (RG: '%s'): %s", ifaceName, resourceGroupName, err)
		}

		return res, *res.Properties.ProvisioningState, nil
	}
}

func resourceArmNetworkInterfaceIpConfigurationHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["subnet_id"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["private_ip_address_allocation"].(string)))

	return hashcode.String(buf.String())
}

func validateNetworkInterfacePrivateIpAddressAllocation(v interface{}, k string) (ws []string, errors []error) {
	value := strings.ToLower(v.(string))
	allocations := map[string]bool{
		"static":  true,
		"dynamic": true,
	}

	if !allocations[value] {
		errors = append(errors, fmt.Errorf("Network Interface Allocations can only be Static or Dynamic"))
	}
	return
}

func expandAzureRmNetworkInterfaceIpConfigurations(d *schema.ResourceData) ([]network.InterfaceIPConfiguration, error) {
	configs := d.Get("ip_configuration").(*schema.Set).List()
	ipConfigs := make([]network.InterfaceIPConfiguration, 0, len(configs))

	for _, configRaw := range configs {
		data := configRaw.(map[string]interface{})

		subnet_id := data["subnet_id"].(string)
		private_ip_allocation_method := data["private_ip_address_allocation"].(string)

		properties := network.InterfaceIPConfigurationPropertiesFormat{
			Subnet: &network.Subnet{
				ID: &subnet_id,
			},
			PrivateIPAllocationMethod: &private_ip_allocation_method,
		}

		if v := data["private_ip_address"].(string); v != "" {
			properties.PrivateIPAddress = &v
		}

		if v := data["public_ip_address_id"].(string); v != "" {
			properties.PublicIPAddress = &network.PublicIPAddress{
				ID: &v,
			}
		}

		if v, ok := data["load_balancer_backend_address_pools_ids"]; ok {
			var ids []network.BackendAddressPool
			pools := v.(*schema.Set).List()
			for _, p := range pools {
				pool_id := p.(string)
				id := network.BackendAddressPool{
					ID: &pool_id,
				}

				ids = append(ids, id)
			}

			properties.LoadBalancerBackendAddressPools = &ids
		}

		if v, ok := data["load_balancer_inbound_nat_rules_ids"]; ok {
			var natRules []network.InboundNatRule
			rules := v.(*schema.Set).List()
			for _, r := range rules {
				rule_id := r.(string)
				rule := network.InboundNatRule{
					ID: &rule_id,
				}

				natRules = append(natRules, rule)
			}

			properties.LoadBalancerInboundNatRules = &natRules
		}

		name := data["name"].(string)
		ipConfig := network.InterfaceIPConfiguration{
			Name:       &name,
			Properties: &properties,
		}

		ipConfigs = append(ipConfigs, ipConfig)
	}

	return ipConfigs, nil
}
<commit_msg>provider/azurerm: `azurerm_network_interface` diffs didn't match during apply<commit_after>package azurerm

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmNetworkInterface() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmNetworkInterfaceCreate,
		Read:   resourceArmNetworkInterfaceRead,
		Update: resourceArmNetworkInterfaceCreate,
		Delete: resourceArmNetworkInterfaceDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": &schema.Schema{
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"resource_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"network_security_group_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"mac_address": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"private_ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"virtual_machine_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"ip_configuration": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"subnet_id": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"private_ip_address": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"private_ip_address_allocation": &schema.Schema{
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateNetworkInterfacePrivateIpAddressAllocation,
						},

						"public_ip_address_id": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"load_balancer_backend_address_pools_ids": &schema.Schema{
							Type:     schema.TypeSet,
							Optional: true,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},

						"load_balancer_inbound_nat_rules_ids": &schema.Schema{
							Type:     schema.TypeSet,
							Optional: true,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Set:      schema.HashString,
						},
					},
				},
				Set: resourceArmNetworkInterfaceIpConfigurationHash,
			},

			"dns_servers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"internal_dns_name_label": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"applied_dns_servers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},

			"internal_fqdn": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	ifaceClient := client.ifaceClient

	log.Printf("[INFO] preparing arguments for Azure ARM Network Interface creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})

	properties := network.InterfacePropertiesFormat{}

	if v, ok := d.GetOk("network_security_group_id"); ok {
		nsgId := v.(string)
		properties.NetworkSecurityGroup = &network.SecurityGroup{
			ID: &nsgId,
		}
	}

	dns, hasDns := d.GetOk("dns_servers")
	nameLabel, hasNameLabel := d.GetOk("internal_dns_name_label")
	if hasDns || hasNameLabel {
		ifaceDnsSettings := network.InterfaceDNSSettings{}

		if hasDns {
			var dnsServers []string
			dns := dns.(*schema.Set).List()
			for _, v := range dns {
				str := v.(string)
				dnsServers = append(dnsServers, str)
			}
			ifaceDnsSettings.DNSServers = &dnsServers
		}

		if hasNameLabel {
			name_label := nameLabel.(string)
			ifaceDnsSettings.InternalDNSNameLabel = &name_label

		}

		properties.DNSSettings = &ifaceDnsSettings
	}

	ipConfigs, sgErr := expandAzureRmNetworkInterfaceIpConfigurations(d)
	if sgErr != nil {
		return fmt.Errorf("Error Building list of Network Interface IP Configurations: %s", sgErr)
	}
	if len(ipConfigs) > 0 {
		properties.IPConfigurations = &ipConfigs
	}

	iface := network.Interface{
		Name:       &name,
		Location:   &location,
		Properties: &properties,
		Tags:       expandTags(tags),
	}

	resp, err := ifaceClient.CreateOrUpdate(resGroup, name, iface)
	if err != nil {
		return err
	}

	d.SetId(*resp.ID)

	log.Printf("[DEBUG] Waiting for Network Interface (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: networkInterfaceStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for Network Interface (%s) to become available: %s", name, err)
	}

	return resourceArmNetworkInterfaceRead(d, meta)
}

func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
	ifaceClient := meta.(*ArmClient).ifaceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["networkInterfaces"]

	resp, err := ifaceClient.Get(resGroup, name, "")
	if resp.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error making Read request on Azure Network Interface %s: %s", name, err)
	}

	iface := *resp.Properties

	if iface.MacAddress != nil {
		if *iface.MacAddress != "" {
			d.Set("mac_address", iface.MacAddress)
		}
	}

	if iface.IPConfigurations != nil && len(*iface.IPConfigurations) > 0 {
		var privateIPAddress *string
		///TODO: Change this to a loop when https://github.com/Azure/azure-sdk-for-go/issues/259 is fixed
		if (*iface.IPConfigurations)[0].Properties != nil {
			privateIPAddress = (*iface.IPConfigurations)[0].Properties.PrivateIPAddress
		}

		if *privateIPAddress != "" {
			d.Set("private_ip_address", *privateIPAddress)
		}
	}

	if iface.VirtualMachine != nil {
		if *iface.VirtualMachine.ID != "" {
			d.Set("virtual_machine_id", *iface.VirtualMachine.ID)
		}
	}

	if iface.DNSSettings != nil {
		if iface.DNSSettings.AppliedDNSServers != nil && len(*iface.DNSSettings.AppliedDNSServers) > 0 {
			dnsServers := make([]string, 0, len(*iface.DNSSettings.AppliedDNSServers))
			for _, dns := range *iface.DNSSettings.AppliedDNSServers {
				dnsServers = append(dnsServers, dns)
			}

			if err := d.Set("applied_dns_servers", dnsServers); err != nil {
				return err
			}
		}

		if iface.DNSSettings.InternalFqdn != nil && *iface.DNSSettings.InternalFqdn != "" {
			d.Set("internal_fqdn", iface.DNSSettings.InternalFqdn)
		}
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error {
	ifaceClient := meta.(*ArmClient).ifaceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["networkInterfaces"]

	_, err = ifaceClient.Delete(resGroup, name)

	return err
}

func networkInterfaceStateRefreshFunc(client *ArmClient, resourceGroupName string, ifaceName string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		res, err := client.ifaceClient.Get(resourceGroupName, ifaceName, "")
		if err != nil {
			return nil, "", fmt.Errorf("Error issuing read request in networkInterfaceStateRefreshFunc to Azure ARM for network interace '%s' (RG: '%s'): %s", ifaceName, resourceGroupName, err)
		}

		return res, *res.Properties.ProvisioningState, nil
	}
}

func resourceArmNetworkInterfaceIpConfigurationHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["subnet_id"].(string)))
	if m["private_ip_address"] != nil {
		buf.WriteString(fmt.Sprintf("%s-", m["private_ip_address"].(string)))
	}
	buf.WriteString(fmt.Sprintf("%s-", m["private_ip_address_allocation"].(string)))
	if m["public_ip_address_id"] != nil {
		buf.WriteString(fmt.Sprintf("%s-", m["public_ip_address_id"].(string)))
	}

	return hashcode.String(buf.String())
}

func validateNetworkInterfacePrivateIpAddressAllocation(v interface{}, k string) (ws []string, errors []error) {
	value := strings.ToLower(v.(string))
	allocations := map[string]bool{
		"static":  true,
		"dynamic": true,
	}

	if !allocations[value] {
		errors = append(errors, fmt.Errorf("Network Interface Allocations can only be Static or Dynamic"))
	}
	return
}

func expandAzureRmNetworkInterfaceIpConfigurations(d *schema.ResourceData) ([]network.InterfaceIPConfiguration, error) {
	configs := d.Get("ip_configuration").(*schema.Set).List()
	ipConfigs := make([]network.InterfaceIPConfiguration, 0, len(configs))

	for _, configRaw := range configs {
		data := configRaw.(map[string]interface{})

		subnet_id := data["subnet_id"].(string)
		private_ip_allocation_method := data["private_ip_address_allocation"].(string)

		properties := network.InterfaceIPConfigurationPropertiesFormat{
			Subnet: &network.Subnet{
				ID: &subnet_id,
			},
			PrivateIPAllocationMethod: &private_ip_allocation_method,
		}

		if v := data["private_ip_address"].(string); v != "" {
			properties.PrivateIPAddress = &v
		}

		if v := data["public_ip_address_id"].(string); v != "" {
			properties.PublicIPAddress = &network.PublicIPAddress{
				ID: &v,
			}
		}

		if v, ok := data["load_balancer_backend_address_pools_ids"]; ok {
			var ids []network.BackendAddressPool
			pools := v.(*schema.Set).List()
			for _, p := range pools {
				pool_id := p.(string)
				id := network.BackendAddressPool{
					ID: &pool_id,
				}

				ids = append(ids, id)
			}

			properties.LoadBalancerBackendAddressPools = &ids
		}

		if v, ok := data["load_balancer_inbound_nat_rules_ids"]; ok {
			var natRules []network.InboundNatRule
			rules := v.(*schema.Set).List()
			for _, r := range rules {
				rule_id := r.(string)
				rule := network.InboundNatRule{
					ID: &rule_id,
				}

				natRules = append(natRules, rule)
			}

			properties.LoadBalancerInboundNatRules = &natRules
		}

		name := data["name"].(string)
		ipConfig := network.InterfaceIPConfiguration{
			Name:       &name,
			Properties: &properties,
		}

		ipConfigs = append(ipConfigs, ipConfig)
	}

	return ipConfigs, nil
}
<|endoftext|>


<commit_before>//   Copyright (c) 2016 Ivan A Kostko (github.com/ivan-kostko)

//   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 Repository

<commit_msg>Filtering types<commit_after>//   Copyright (c) 2016 Ivan A Kostko (github.com/ivan-kostko)

//   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 Repository

// Represents comparrison operator ENUM
type Operator string

const(
    EQ Operator = "EQ"  // Equal
    NE Operator = "NE"  // Not equal
    IN Operator = "IN"  // In range
    GE Operator = "GE"  // Greater or equal
    // TODO(me): Extend with other operators
)

// Represents ingle predicate as operator + values
type Predicate struct {
    Operator
    Values    []interface{}
}

// Represents composition of fiels as key and predicates
type Criteria map[string][]Predicate
<|endoftext|>


<commit_before>package pg_test

import (
	"bytes"
	"fmt"
	"strings"

	"gopkg.in/pg.v3"
)

var db *pg.DB

func init() {
	db = pg.Connect(&pg.Options{
		User: "postgres",
	})
}

func ExampleConnect() {
	db := pg.Connect(&pg.Options{
		User: "postgres",
	})
	err := db.Close()
	fmt.Println(err)
	// Output: <nil>
}

func ExampleDB_QueryOne() {
	var user struct {
		Name string
	}

	res, err := db.QueryOne(&user, `
        WITH users (name) AS (VALUES (?))
        SELECT * FROM users
    `, "admin")
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Affected())
	fmt.Println(user)
	// Output: 1
	// {admin}
}

func ExampleDB_QueryOne_2() {
	var user struct {
		Id   int32
		Name string
	}

	_, err := db.Exec(`CREATE TEMP TABLE users(id serial, name varchar(500))`)
	if err != nil {
		panic(err)
	}

	user.Name = "admin"
	_, err = db.QueryOne(&user, `
        INSERT INTO users (name) VALUES (?name) RETURNING id
    `, user)
	if err != nil {
		panic(err)
	}
	fmt.Println(user)
	// Output: {1 admin}
}

func ExampleDB_Exec() {
	res, err := db.Exec("CREATE TEMP TABLE test()")
	fmt.Println(res.Affected(), err)
	// Output: 0 <nil>
}

func ExampleLoadInto() {
	var s1, s2 string
	_, err := db.QueryOne(pg.LoadInto(&s1, &s2), "SELECT ?, ?", "foo", "bar")
	fmt.Println(s1, s2, err)
	// Output: foo bar <nil>
}

func ExampleListener() {
	ln, _ := db.Listen("mychan")

	done := make(chan struct{})
	go func() {
		channel, payload, err := ln.Receive()
		fmt.Printf("%s %q %v", channel, payload, err)
		done <- struct{}{}
	}()

	_, _ = db.Exec("NOTIFY mychan, ?", "hello world")
	<-done

	// Output: mychan "hello world" <nil>
}

func ExampleDB_Begin() {
	tx, err := db.Begin()
	if err != nil {
		panic(err)
	}

	_, err = tx.Exec("CREATE TEMP TABLE test1()")
	if err != nil {
		panic(err)
	}

	err = tx.Rollback()
	if err != nil {
		panic(err)
	}

	_, err = db.Exec("SELECT * FROM test1")
	fmt.Println(err)
	// Output: ERROR #42P01 relation "test1" does not exist:
}

func ExampleDB_Prepare() {
	stmt, err := db.Prepare("SELECT $1::text, $2::text")
	if err != nil {
		panic(err)
	}

	var s1, s2 string
	_, err = stmt.QueryOne(pg.LoadInto(&s1, &s2), "foo", "bar")
	fmt.Println(s1, s2, err)
	// Output: foo bar <nil>
}

func ExampleInts() {
	var nums pg.Ints
	_, err := db.Query(&nums, "SELECT generate_series(0, 10)")
	fmt.Println(nums, err)
	// Output: [0 1 2 3 4 5 6 7 8 9 10] <nil>
}

func ExampleStrings() {
	var strs pg.Strings
	_, err := db.Query(
		&strs, "WITH users AS (VALUES ('foo'), ('bar')) SELECT * FROM users")
	fmt.Println(strs, err)
	// Output: [foo bar] <nil>
}

func ExampleDB_CopyFrom() {
	_, err := db.Exec("CREATE TEMP TABLE words(word text, len int)")
	if err != nil {
		panic(err)
	}

	r := strings.NewReader("hello,5\nfoo,3\n")
	_, err = db.CopyFrom(r, "COPY words FROM STDIN WITH CSV")
	if err != nil {
		panic(err)
	}

	buf := &bytes.Buffer{}
	_, err = db.CopyTo(&NopWriteCloser{buf}, "COPY words TO STDOUT WITH CSV")
	if err != nil {
		panic(err)
	}
	fmt.Println(buf.String())
	// Output: hello,5
	// foo,3
}
<commit_msg>Tweak examples.<commit_after>package pg_test

import (
	"bytes"
	"fmt"
	"strings"

	"gopkg.in/pg.v3"
)

var db *pg.DB

func init() {
	db = pg.Connect(&pg.Options{
		User: "postgres",
	})
}

func ExampleConnect() {
	db := pg.Connect(&pg.Options{
		User: "postgres",
	})
	err := db.Close()
	fmt.Println(err)
	// Output: <nil>
}

func ExampleDB_QueryOne() {
	var user struct {
		Name string
	}

	res, err := db.QueryOne(&user, `
        WITH users (name) AS (VALUES (?))
        SELECT * FROM users
    `, "admin")
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Affected())
	fmt.Println(user)
	// Output: 1
	// {admin}
}

func ExampleDB_QueryOne_2() {
	_, err := db.Exec(`CREATE TEMP TABLE users(id serial, name varchar(500))`)
	if err != nil {
		panic(err)
	}

	var user struct {
		Id   int32
		Name string
	}
	user.Name = "admin"

	_, err = db.QueryOne(&user, `
        INSERT INTO users (name) VALUES (?name) RETURNING id
    `, user)
	if err != nil {
		panic(err)
	}
	fmt.Println(user)
	// Output: {1 admin}
}

func ExampleDB_Exec() {
	res, err := db.Exec(`CREATE TEMP TABLE test()`)
	fmt.Println(res.Affected(), err)
	// Output: 0 <nil>
}

func ExampleLoadInto() {
	var s1, s2 string
	_, err := db.QueryOne(pg.LoadInto(&s1, &s2), `SELECT ?, ?`, "foo", "bar")
	fmt.Println(s1, s2, err)
	// Output: foo bar <nil>
}

func ExampleListener() {
	ln, err := db.Listen("mychan")
	if err != nil {
		panic(err)
	}

	wait := make(chan struct{})
	go func() {
		channel, payload, err := ln.Receive()
		fmt.Printf("%s %q %v", channel, payload, err)
		wait <- struct{}{}
	}()

	db.Exec("NOTIFY mychan, ?", "hello world")
	<-wait

	// Output: mychan "hello world" <nil>
}

func ExampleDB_Begin() {
	tx, err := db.Begin()
	if err != nil {
		panic(err)
	}

	_, err = tx.Exec(`CREATE TABLE tx_test()`)
	if err != nil {
		panic(err)
	}

	err = tx.Rollback()
	if err != nil {
		panic(err)
	}

	_, err = db.Exec(`SELECT * FROM tx_test`)
	fmt.Println(err)
	// Output: ERROR #42P01 relation "tx_test" does not exist:
}

func ExampleDB_Prepare() {
	stmt, err := db.Prepare(`SELECT $1::text, $2::text`)
	if err != nil {
		panic(err)
	}

	var s1, s2 string
	_, err = stmt.QueryOne(pg.LoadInto(&s1, &s2), "foo", "bar")
	fmt.Println(s1, s2, err)
	// Output: foo bar <nil>
}

func ExampleInts() {
	var nums pg.Ints
	_, err := db.Query(&nums, `SELECT generate_series(0, 10)`)
	fmt.Println(nums, err)
	// Output: [0 1 2 3 4 5 6 7 8 9 10] <nil>
}

func ExampleInts_2() {
	ids := pg.Ints{1, 2, 3}
	q := pg.MustFormatQ(`SELECT * FROM table WHERE id IN (?)`, ids)
	fmt.Println(q)
	// Output: SELECT * FROM table WHERE id IN (1,2,3)
}

func ExampleStrings() {
	var strs pg.Strings
	_, err := db.Query(
		&strs, `WITH users AS (VALUES ('foo'), ('bar')) SELECT * FROM users`)
	fmt.Println(strs, err)
	// Output: [foo bar] <nil>
}

func ExampleDB_CopyFrom() {
	_, err := db.Exec(`CREATE TEMP TABLE words(word text, len int)`)
	if err != nil {
		panic(err)
	}

	r := strings.NewReader("hello,5\nfoo,3\n")
	_, err = db.CopyFrom(r, `COPY words FROM STDIN WITH CSV`)
	if err != nil {
		panic(err)
	}

	buf := &bytes.Buffer{}
	_, err = db.CopyTo(&NopWriteCloser{buf}, `COPY words TO STDOUT WITH CSV`)
	if err != nil {
		panic(err)
	}
	fmt.Println(buf.String())
	// Output: hello,5
	// foo,3
}
<|endoftext|>


<commit_before>package shttp_test

import (
	"log"
	"net/http"

	"github.com/apriendeau/shttp"
)

func ExampleWrite() {
	handler := func(w http.ResponseWriter, r *http.Request) {

		message := struct {
			Message string `json:"message"`
		}{"I received your request."}

		if err := shttp.Write(w, r, message, 200); err != nil {
			log.Panic(err)
		}
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/post", handler)
	if err := http.ListenAndServe(":8000", mux); err != nil {
		log.Panic(err)
	}
}
<commit_msg>adds read example<commit_after>package shttp_test

import (
	"log"
	"net/http"

	"github.com/apriendeau/shttp"
)

func ExampleWrite() {
	handler := func(w http.ResponseWriter, r *http.Request) {
		message := struct {
			Message string `json:"message"`
		}{"I received your request."}

		if err := shttp.Write(w, r, message, 200); err != nil {
			log.Panic(err)
		}
	}

	http.HandleFunc("/post", handler)
	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Panic(err)
	}
}

func ExampleRead() {
	// Simple mimic server
	handler := func(w http.ResponseWriter, r *http.Request) {
		// parsing for example sake
		body := make(map[string]interface{})
		if err := shttp.Read(r, &body); err != nil {
			log.Panic(err)
		}
		// send body back to mimic
		if err := shttp.Write(w, r, body, 200); err != nil {
			log.Panic(err)
		}
	}

	http.HandleFunc("/post", handler)
	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Panic(err)
	}
}
<|endoftext|>


<commit_before>package numeric_test

import (
	"github.com/reiver/go-numeric"

	"fmt"
)

func ExampleNotInRangeComplainer() {

	r := '½'

	i64, err := numeric.Int64(r)
	if nil != err {
		switch err.(type) {
		case numeric.NotInRangeComplainer:
			fmt.Printf("Although %s is numeric, it is not in the range of an int64.\n", r)
		default:
			fmt.Println("Some other error.")
		}
	}

	fmt.Printf("The int64 number is: %d.\n", i64)
}

func ExampleNotNumericComplainer() {

	r := '&'

	i64, err := numeric.Int64(r)
	if nil != err {
		switch err.(type) {
		case numeric.NotNumericComplainer:
			fmt.Printf("The rune %s is NOT numeric.\n", r)
		default:
			fmt.Println("Some other error.")
		}
	}

	fmt.Printf("The int64 number is: %d.\n", i64)
}
<commit_msg>updated docs<commit_after>package numeric_test

import (
	"github.com/reiver/go-numeric"

	"fmt"
)

func ExampleNotInRangeComplainer() {

	r := '½'

	i64, err := numeric.Int64(r)
	if nil != err {
		switch err.(type) {
		case numeric.NotInRangeComplainer:
			fmt.Printf("Although %q is numeric, it is not in the range of an int64.\n", r)
			return
		default:
			fmt.Println("Some other error.")
			return
		}
	}

	fmt.Printf("The int64 number is: %d.\n", i64)

	// Output: Although '½' is numeric, it is not in the range of an int64.
}

func ExampleNotNumericComplainer() {

	r := '&'

	i64, err := numeric.Int64(r)
	if nil != err {
		switch err.(type) {
		case numeric.NotNumericComplainer:
			fmt.Printf("The rune %q is NOT numeric.\n", r)
			return
		default:
			fmt.Println("Some other error.")
			return
		}
	}

	fmt.Printf("The int64 number is: %d.\n", i64)

	// Output: The rune '&' is NOT numeric.
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 config_test

import (
	"testing"

	"errors"

	"sync"

	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/config/path"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/stretchr/testify/assert"
)

var _ config.MessageReceiver = (*testSubscriber)(nil)

type testSubscriber struct {
	f func(path string, sg scope.Scope, id int64) error
}

func (ts *testSubscriber) MessageConfig(path string, sg scope.Scope, id int64) error {
	//ts.t.Logf("Message: %s Group %d Scope %d", path, sg, s.scope.ID())
	return ts.f(path, sg, id)
}

func TestPubSubBubbling(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "aa/bb/cc"

	s := config.NewService()

	_, err := s.Subscribe("", nil)
	assert.EqualError(t, err, path.ErrIncorrectPath.Error())

	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, testPath, path)
			if sg == scope.DefaultID {
				assert.Equal(t, int64(0), id)
			} else {
				assert.Equal(t, int64(123), id)
			}
			return nil
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")

	assert.NoError(t, s.Write(config.Value(1), config.Path(testPath), config.Scope(scope.WebsiteID, 123)))
	assert.NoError(t, s.Close())

	// send on closed channel
	assert.NoError(t, s.Write(config.Value(1), config.Path(testPath+"Doh"), config.Scope(scope.WebsiteID, 3)))
	assert.EqualError(t, s.Close(), config.ErrPublisherClosed.Error())
}

func TestPubSubPanicSimple(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "x/y/z"

	s := config.NewService()
	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic("Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Write(config.Value(321), config.Path(testPath), config.ScopeStore(123)))
	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Don't panic!"`)
}

func TestPubSubPanicError(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "™/ö/º"

	var pErr = errors.New("OMG! Panic!")
	s := config.NewService()
	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Write(config.Value(321), config.Path(testPath), config.ScopeStore(123)))

	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.err err: OMG! Panic!`)
}

func TestPubSubPanicMultiple(t *testing.T) {
	defer debugLogBuf.Reset()
	s := config.NewService()

	subID, err := s.Subscribe("x", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "x/y/z", path)
			panic("One: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	subID, err = s.Subscribe("x/y", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "x/y/z", path)
			panic("Two: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	subID, err = s.Subscribe("x/y/z", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "x/y/z", path)
			panic("Three: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	assert.NoError(t, s.Write(config.Value(789), config.Path("x/y/z"), config.ScopeStore(987)))
	assert.NoError(t, s.Close())

	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "One: Don't panic!`)
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Two: Don't panic!"`)
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Three: Don't panic!"`)
}

func TestPubSubUnsubscribe(t *testing.T) {
	defer debugLogBuf.Reset()

	var pErr = errors.New("WTF? Panic!")
	s := config.NewService()
	subID, err := s.Subscribe("x/y/z", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Unsubscribe(subID))
	assert.NoError(t, s.Write(config.Value(321), config.Path("x/y/z"), config.ScopeStore(123)))
	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.Service.Write path: "stores/123/x/y/z" val: 321`)

}

type levelCalls struct {
	sync.Mutex
	level2Calls int
	level3Calls int
}

func TestPubSubEvict(t *testing.T) {
	defer debugLogBuf.Reset()

	levelCall := new(levelCalls)

	var pErr = errors.New("WTF Eviction? Panic!")
	s := config.NewService()
	subID, err := s.Subscribe("x/y", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Contains(t, path, "x/y")
			// this function gets called 3 times
			levelCall.Lock()
			levelCall.level2Calls++
			levelCall.Unlock()
			return nil
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID)

	subID, err = s.Subscribe("x/y/z", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			levelCall.Lock()
			levelCall.level3Calls++
			levelCall.Unlock()
			// this function gets called 1 times and then gets removed
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 2, subID)

	assert.NoError(t, s.Write(config.Value(321), config.Path("x/y/z"), config.ScopeStore(123)))
	assert.NoError(t, s.Write(config.Value(321), config.Path("x/y/a"), config.ScopeStore(123)))
	assert.NoError(t, s.Write(config.Value(321), config.Path("x/y/z"), config.ScopeStore(123)))

	assert.NoError(t, s.Close())

	assert.Contains(t, debugLogBuf.String(), "config.pubSub.publish.recover.err err: WTF Eviction? Panic!")

	levelCall.Lock()
	assert.Equal(t, 3, levelCall.level2Calls)
	assert.Equal(t, 1, levelCall.level3Calls)
	levelCall.Unlock()
	assert.EqualError(t, s.Close(), config.ErrPublisherClosed.Error())
}
<commit_msg>config: Fix PubSub Test to adjust to pkg config/path<commit_after>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 config_test

import (
	"testing"

	"errors"

	"sync"

	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/config/path"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/stretchr/testify/assert"
)

var _ config.MessageReceiver = (*testSubscriber)(nil)

type testSubscriber struct {
	f func(path string, sg scope.Scope, id int64) error
}

func (ts *testSubscriber) MessageConfig(path string, sg scope.Scope, id int64) error {
	//ts.t.Logf("Message: %s Group %d Scope %d", path, sg, s.scope.ID())
	return ts.f(path, sg, id)
}

func TestPubSubBubbling(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "aa/bb/cc"

	s := config.NewService()

	_, err := s.Subscribe("", nil)
	assert.EqualError(t, err, path.ErrIncorrectPath.Error())

	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, testPath, path)
			if sg == scope.DefaultID {
				assert.Equal(t, int64(0), id)
			} else {
				assert.Equal(t, int64(123), id)
			}
			return nil
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")

	assert.NoError(t, s.Write(config.Value(1), config.Path(testPath), config.Scope(scope.WebsiteID, 123)))
	assert.NoError(t, s.Close())

	// send on closed channel
	assert.NoError(t, s.Write(config.Value(1), config.Path(testPath+"Doh"), config.Scope(scope.WebsiteID, 3)))
	assert.EqualError(t, s.Close(), config.ErrPublisherClosed.Error())
}

func TestPubSubPanicSimple(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "xx/yy/zz"

	s := config.NewService()
	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic("Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Write(config.Value(321), config.Path(testPath), config.ScopeStore(123)))
	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Don't panic!"`)
}

func TestPubSubPanicError(t *testing.T) {
	defer debugLogBuf.Reset()
	testPath := "aa/bb/cc"

	var pErr = errors.New("OMG! Panic!")
	s := config.NewService()
	subID, err := s.Subscribe(testPath, &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Write(config.Value(321), config.Path(testPath), config.ScopeStore(123)))

	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.err err: OMG! Panic!`)
}

func TestPubSubPanicMultiple(t *testing.T) {
	defer debugLogBuf.Reset()
	s := config.NewService()

	subID, err := s.Subscribe("xx", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "xx/yy/zz", path)
			panic("One: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	subID, err = s.Subscribe("xx/yy", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "xx/yy/zz", path)
			panic("Two: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	subID, err = s.Subscribe("xx/yy/zz", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Equal(t, "xx/yy/zz", path)
			panic("Three: Don't panic!")
		},
	})
	assert.NoError(t, err)
	assert.True(t, subID > 0)

	assert.NoError(t, s.Write(config.Value(789), config.Path("xx/yy/zz"), config.ScopeStore(987)))
	assert.NoError(t, s.Close())

	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "One: Don't panic!`)
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Two: Don't panic!"`)
	assert.Contains(t, debugLogBuf.String(), `config.pubSub.publish.recover.r recover: "Three: Don't panic!"`)
}

func TestPubSubUnsubscribe(t *testing.T) {
	defer debugLogBuf.Reset()

	var pErr = errors.New("WTF? Panic!")
	s := config.NewService()
	subID, err := s.Subscribe("xx/yy/zz", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID, "The very first subscription ID should be 1")
	assert.NoError(t, s.Unsubscribe(subID))
	assert.NoError(t, s.Write(config.Value(321), config.Path("xx/yy/zz"), config.ScopeStore(123)))
	assert.NoError(t, s.Close())
	assert.Contains(t, debugLogBuf.String(), `config.Service.Write path: "stores/123/xx/yy/zz" val: 321`)

}

type levelCalls struct {
	sync.Mutex
	level2Calls int
	level3Calls int
}

func TestPubSubEvict(t *testing.T) {
	defer debugLogBuf.Reset()

	levelCall := new(levelCalls)

	var pErr = errors.New("WTF Eviction? Panic!")
	s := config.NewService()
	subID, err := s.Subscribe("xx/yy", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			assert.Contains(t, path, "xx/yy")
			// this function gets called 3 times
			levelCall.Lock()
			levelCall.level2Calls++
			levelCall.Unlock()
			return nil
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 1, subID)

	subID, err = s.Subscribe("xx/yy/zz", &testSubscriber{
		f: func(path string, sg scope.Scope, id int64) error {
			levelCall.Lock()
			levelCall.level3Calls++
			levelCall.Unlock()
			// this function gets called 1 times and then gets removed
			panic(pErr)
		},
	})
	assert.NoError(t, err)
	assert.Equal(t, 2, subID)

	assert.NoError(t, s.Write(config.Value(321), config.Path("xx/yy/zz"), config.ScopeStore(123)))
	assert.NoError(t, s.Write(config.Value(321), config.Path("xx/yy/aa"), config.ScopeStore(123)))
	assert.NoError(t, s.Write(config.Value(321), config.Path("xx/yy/zz"), config.ScopeStore(123)))

	assert.NoError(t, s.Close())

	assert.Contains(t, debugLogBuf.String(), "config.pubSub.publish.recover.err err: WTF Eviction? Panic!")

	levelCall.Lock()
	assert.Equal(t, 3, levelCall.level2Calls)
	assert.Equal(t, 1, levelCall.level3Calls)
	levelCall.Unlock()
	assert.EqualError(t, s.Close(), config.ErrPublisherClosed.Error())
}
<|endoftext|>