<commit_before>/*
Copyright 2022 The Kubernetes Authors.

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

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

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

package tide

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"time"

	"k8s.io/apimachinery/pkg/util/sets"
	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	v1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/gerrit/client"
	"k8s.io/test-infra/prow/git/types"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/tide/blockers"
	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/andygrunwald/go-gerrit"
	githubql "github.com/shurcooL/githubv4"
	"github.com/sirupsen/logrus"
)

const (
	// ref:
	// https://gerrit-review.googlesource.com/Documentation/user-search.html#_search_operators.
	// Also good to know: `(repo:repo-A OR repo:repo-B)`
	gerritDefaultQueryParam = "status:open -is:wip is:submittable is:mergeable"
)

type gerritClient interface {
	QueryChangesForProject(instance, project string, lastUpdate time.Time, rateLimit int, addtionalFilters []string) ([]gerrit.ChangeInfo, error)
	GetBranchRevision(instance, project, branch string) (string, error)
}

// Enforcing interface implementation check at compile time
var _ provider = (*GerritProvider)(nil)

// GerritProvider implements provider, used by tide Controller for
// interacting directly with Gerrit.
//
// Tide Controller should only use GerritProvider for communicating with Gerrit.
type GerritProvider struct {
	cfg config.Getter
	gc  gerritClient

	pjclientset ctrlruntimeclient.Client

	cookiefilePath    string
	tokenPathOverride string

	*mergeChecker
	logger *logrus.Entry
}

func newGerritProvider(
	logger *logrus.Entry,
	cfg config.Getter,
	pjclientset ctrlruntimeclient.Client,
	mergeChecker *mergeChecker,
	cookiefilePath string,
	tokenPathOverride string,
) *GerritProvider {
	gerritClient, err := client.NewClient(nil)
	if err != nil {
		logrus.WithError(err).Fatal("Error creating gerrit client.")
	}

	gp := &GerritProvider{
		logger:            logger,
		cfg:               cfg,
		pjclientset:       pjclientset,
		gc:                gerritClient,
		cookiefilePath:    cookiefilePath,
		tokenPathOverride: tokenPathOverride,
		mergeChecker:      mergeChecker,
	}

	gp.applyGlobalConfig(cfg, gerritClient, cookiefilePath, tokenPathOverride)

	return gp
}

func (p *GerritProvider) applyGlobalConfig(cfg config.Getter, gerritClient *client.Client, cookiefilePath, tokenPathOverride string) {
	p.applyGlobalConfigOnce(cfg, gerritClient, cookiefilePath, tokenPathOverride)

	go func() {
		for {
			p.applyGlobalConfigOnce(cfg, gerritClient, cookiefilePath, tokenPathOverride)
			// No need to spin constantly, give it a break. It's ok that config change has one second delay.
			time.Sleep(time.Second)
		}
	}()
}

func (p *GerritProvider) applyGlobalConfigOnce(cfg config.Getter, gerritClient *client.Client, cookiefilePath, tokenPathOverride string) {
	gerritCfg := cfg().Tide.Gerrit
	if gerritCfg == nil {
		return
	}

	if err := gerritClient.UpdateClients(gerritCfg.Queries.AllRepos()); err != nil {
		logrus.WithError(err).Error("Updating clients.")
	}
	// Authenticate creates a goroutine for rotating token secrets when called the first
	// time, afterwards it only authenticate once.
	gerritClient.Authenticate(cookiefilePath, tokenPathOverride)
}

// Query returns all PRs from configured gerrit org/repos.
func (p *GerritProvider) Query() (map[string]CodeReviewCommon, error) {
	// lastUpdate is used by gerrit adapter for achieving incremental query. In
	// tide case we want to get everything so use default time.Time, which
	// should be 1970,1,1.
	var lastUpdate time.Time

	res := make(map[string]CodeReviewCommon)
	// This is querying serially, which would safely guard against quota issues.
	// TODO(chaodai): parallize this to boot the performance.
	for instance, projs := range p.cfg().Tide.Gerrit.Queries.AllRepos() {
		for projName := range projs {
			changes, err := p.gc.QueryChangesForProject(instance, projName, lastUpdate, p.cfg().Gerrit.RateLimit, []string{gerritDefaultQueryParam})
			if err != nil {
				p.logger.WithFields(logrus.Fields{"instance": instance, "project": projName}).WithError(err).Error("Querying gerrit project for changes.")
				continue
			}
			for _, pr := range changes {
				crc := CodeReviewCommonFromGerrit(&pr, instance)
				res[prKey(crc)] = *crc
			}
		}
	}

	return res, nil
}

func (p *GerritProvider) blockers() (blockers.Blockers, error) {
	// This is not supported yet, so return an empty blocker for now.
	return blockers.Blockers{}, nil
}

func (p *GerritProvider) isAllowedToMerge(crc *CodeReviewCommon) (string, error) {
	if crc.Mergeable == string(githubql.MergeableStateConflicting) {
		return "PR has a merge conflict.", nil
	}
	return "", nil
}

// GetRef gets the latest revision from org/repo/branch.
func (p *GerritProvider) GetRef(org, repo, ref string) (string, error) {
	return p.gc.GetBranchRevision(org, repo, ref)
}

// headContexts gets the status contexts for the commit with OID ==
// pr.HeadRefOID
//
// Assuming all submission requirements are already met as the PRs queried are
// already submittable. So the focus here is to ensure that all prowjobs were
// tested against latest baseSHA.
// Prow parses baseSHA from the `Description` field of a context, will make sure
// that all Prow jobs that vote to required labels are represented here.
func (p *GerritProvider) headContexts(crc *CodeReviewCommon) ([]Context, error) {
	var res []Context

	selector := map[string]string{
		kube.GerritRevision:   crc.HeadRefOID,
		kube.ProwJobTypeLabel: string(prowapi.PresubmitJob),
		kube.OrgLabel:         crc.Org,
		kube.RepoLabel:        crc.Repo,
		kube.PullLabel:        strconv.Itoa(crc.Number),
	}
	var pjs v1.ProwJobList
	if err := p.pjclientset.List(context.Background(), &pjs, ctrlruntimeclient.MatchingLabels(selector)); err != nil {
		return nil, fmt.Errorf("Cannot list prowjob with selector %v", selector)
	}

	// keep track of latest prowjobs only
	latestPjs := make(map[string]*prowapi.ProwJob)
	for _, pj := range pjs.Items {
		pj := pj
		if exist, ok := latestPjs[pj.Spec.Context]; ok && exist.CreationTimestamp.After(pj.CreationTimestamp.Time) {
			continue
		}
		latestPjs[pj.Spec.Context] = &pj
	}

	for _, pj := range latestPjs {
		res = append(res, Context{
			Context:     githubql.String(pj.Spec.Context),
			Description: githubql.String(config.ContextDescriptionWithBaseSha(pj.Status.Description, pj.Spec.Refs.BaseSHA)),
			State:       githubql.StatusState(pj.Status.State),
		})
	}

	return res, nil
}

func (p *GerritProvider) mergePRs(sp subpool, prs []CodeReviewCommon, dontUpdateStatus *threadSafePRSet) error {
	p.logger.Info("The merge function hasn't been implemented yet, just logging for now.")
	return nil
}

// GetTideContextPolicy gets context policy defined by users + requirements from
// prow jobs.
func (p *GerritProvider) GetTideContextPolicy(gitClient git.ClientFactory, org, repo, branch string, baseSHAGetter config.RefGetter, crc *CodeReviewCommon) (contextChecker, error) {
	pr := crc.Gerrit
	if pr == nil {
		return nil, errors.New("programmer error: crc.Gerrit cannot be nil for GerritProvider")
	}

	required := sets.NewString()
	requiredIfPresent := sets.NewString()
	optional := sets.NewString()

	headSHAGetter := func() (string, error) {
		return crc.HeadRefOID, nil
	}
	presubmits, err := p.cfg().GetPresubmits(gitClient, org+"/"+repo, baseSHAGetter, headSHAGetter)
	if err != nil {
		return nil, fmt.Errorf("failed to get presubmits: %w", err)
	}

	requireLabels := sets.NewString()
	for l, info := range pr.Labels {
		if !info.Optional {
			requireLabels.Insert(l)
		}
	}

	// generate required and optional entries for Prow Jobs
	for _, pj := range presubmits {
		if !pj.CouldRun(branch) {
			continue
		}

		var isJobRequired bool
		if val, ok := pj.Labels[kube.GerritReportLabel]; ok && requireLabels.Has(val) {
			isJobRequired = true
		}

		if isJobRequired {
			if pj.TriggersConditionally() {
				// jobs that trigger conditionally are required if present.
				requiredIfPresent.Insert(pj.Context)
			} else {
				// jobs that produce required contexts and will
				// always run should be required at all times
				required.Insert(pj.Context)
			}
		} else {
			optional.Insert(pj.Context)
		}
	}

	t := &config.TideContextPolicy{
		RequiredContexts:          required.List(),
		RequiredIfPresentContexts: requiredIfPresent.List(),
		OptionalContexts:          optional.List(),
	}
	if err := t.Validate(); err != nil {
		return t, err
	}
	return t, nil
}

func (p *GerritProvider) prMergeMethod(crc *CodeReviewCommon) (types.PullRequestMergeType, error) {
	var res types.PullRequestMergeType
	pr := crc.Gerrit
	if pr == nil {
		return res, errors.New("programmer error: crc.Gerrit cannot be nil for GerritProvider")
	}

	// Translate merge methods to types that git could understand. The merge
	// methods for gerrit are documented at
	// https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#repository.
	// Git can only understand MergeIfNecessary, MergeMerge, MergeRebase, MergeSquash.
	switch pr.SubmitType {
	case "MERGE_IF_NECESSARY":
		res = types.MergeIfNecessary
	case "FAST_FORWARD_ONLY":
		res = types.MergeMerge
	case "REBASE_IF_NECESSARY":
		res = types.MergeRebase
	case "REBASE_ALWAYS":
		res = types.MergeRebase
	case "MERGE_ALWAYS":
		res = types.MergeMerge
	default:
		res = types.MergeMerge
	}

	return res, nil
}
<commit_msg>Tide: Gerrit controller uses common auth refresher<commit_after>/*
Copyright 2022 The Kubernetes Authors.

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

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

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

package tide

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"time"

	"k8s.io/apimachinery/pkg/util/sets"
	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	v1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/gerrit/client"
	"k8s.io/test-infra/prow/git/types"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/tide/blockers"
	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/andygrunwald/go-gerrit"
	githubql "github.com/shurcooL/githubv4"
	"github.com/sirupsen/logrus"
)

const (
	// ref:
	// https://gerrit-review.googlesource.com/Documentation/user-search.html#_search_operators.
	// Also good to know: `(repo:repo-A OR repo:repo-B)`
	gerritDefaultQueryParam = "status:open -is:wip is:submittable is:mergeable"
)

type gerritClient interface {
	QueryChangesForProject(instance, project string, lastUpdate time.Time, rateLimit int, addtionalFilters []string) ([]gerrit.ChangeInfo, error)
	GetBranchRevision(instance, project, branch string) (string, error)
}

// Enforcing interface implementation check at compile time
var _ provider = (*GerritProvider)(nil)

// GerritProvider implements provider, used by tide Controller for
// interacting directly with Gerrit.
//
// Tide Controller should only use GerritProvider for communicating with Gerrit.
type GerritProvider struct {
	cfg config.Getter
	gc  gerritClient

	pjclientset ctrlruntimeclient.Client

	cookiefilePath    string
	tokenPathOverride string

	*mergeChecker
	logger *logrus.Entry
}

func newGerritProvider(
	logger *logrus.Entry,
	cfg config.Getter,
	pjclientset ctrlruntimeclient.Client,
	mergeChecker *mergeChecker,
	cookiefilePath string,
	tokenPathOverride string,
) *GerritProvider {
	gerritClient, err := client.NewClient(nil)
	if err != nil {
		logrus.WithError(err).Fatal("Error creating gerrit client.")
	}
	orgRepoConfigGetter := func() *config.GerritOrgRepoConfigs {
		return &cfg().Tide.Gerrit.Queries
	}
	gerritClient.ApplyGlobalConfig(orgRepoConfigGetter, nil, cookiefilePath, tokenPathOverride, nil)

	return &GerritProvider{
		logger:            logger,
		cfg:               cfg,
		pjclientset:       pjclientset,
		gc:                gerritClient,
		cookiefilePath:    cookiefilePath,
		tokenPathOverride: tokenPathOverride,
		mergeChecker:      mergeChecker,
	}
}

// Query returns all PRs from configured gerrit org/repos.
func (p *GerritProvider) Query() (map[string]CodeReviewCommon, error) {
	// lastUpdate is used by gerrit adapter for achieving incremental query. In
	// tide case we want to get everything so use default time.Time, which
	// should be 1970,1,1.
	var lastUpdate time.Time

	res := make(map[string]CodeReviewCommon)
	// This is querying serially, which would safely guard against quota issues.
	// TODO(chaodai): parallize this to boot the performance.
	for instance, projs := range p.cfg().Tide.Gerrit.Queries.AllRepos() {
		for projName := range projs {
			changes, err := p.gc.QueryChangesForProject(instance, projName, lastUpdate, p.cfg().Gerrit.RateLimit, []string{gerritDefaultQueryParam})
			if err != nil {
				p.logger.WithFields(logrus.Fields{"instance": instance, "project": projName}).WithError(err).Error("Querying gerrit project for changes.")
				continue
			}
			for _, pr := range changes {
				crc := CodeReviewCommonFromGerrit(&pr, instance)
				res[prKey(crc)] = *crc
			}
		}
	}

	return res, nil
}

func (p *GerritProvider) blockers() (blockers.Blockers, error) {
	// This is not supported yet, so return an empty blocker for now.
	return blockers.Blockers{}, nil
}

func (p *GerritProvider) isAllowedToMerge(crc *CodeReviewCommon) (string, error) {
	if crc.Mergeable == string(githubql.MergeableStateConflicting) {
		return "PR has a merge conflict.", nil
	}
	return "", nil
}

// GetRef gets the latest revision from org/repo/branch.
func (p *GerritProvider) GetRef(org, repo, ref string) (string, error) {
	return p.gc.GetBranchRevision(org, repo, ref)
}

// headContexts gets the status contexts for the commit with OID ==
// pr.HeadRefOID
//
// Assuming all submission requirements are already met as the PRs queried are
// already submittable. So the focus here is to ensure that all prowjobs were
// tested against latest baseSHA.
// Prow parses baseSHA from the `Description` field of a context, will make sure
// that all Prow jobs that vote to required labels are represented here.
func (p *GerritProvider) headContexts(crc *CodeReviewCommon) ([]Context, error) {
	var res []Context

	selector := map[string]string{
		kube.GerritRevision:   crc.HeadRefOID,
		kube.ProwJobTypeLabel: string(prowapi.PresubmitJob),
		kube.OrgLabel:         crc.Org,
		kube.RepoLabel:        crc.Repo,
		kube.PullLabel:        strconv.Itoa(crc.Number),
	}
	var pjs v1.ProwJobList
	if err := p.pjclientset.List(context.Background(), &pjs, ctrlruntimeclient.MatchingLabels(selector)); err != nil {
		return nil, fmt.Errorf("Cannot list prowjob with selector %v", selector)
	}

	// keep track of latest prowjobs only
	latestPjs := make(map[string]*prowapi.ProwJob)
	for _, pj := range pjs.Items {
		pj := pj
		if exist, ok := latestPjs[pj.Spec.Context]; ok && exist.CreationTimestamp.After(pj.CreationTimestamp.Time) {
			continue
		}
		latestPjs[pj.Spec.Context] = &pj
	}

	for _, pj := range latestPjs {
		res = append(res, Context{
			Context:     githubql.String(pj.Spec.Context),
			Description: githubql.String(config.ContextDescriptionWithBaseSha(pj.Status.Description, pj.Spec.Refs.BaseSHA)),
			State:       githubql.StatusState(pj.Status.State),
		})
	}

	return res, nil
}

func (p *GerritProvider) mergePRs(sp subpool, prs []CodeReviewCommon, dontUpdateStatus *threadSafePRSet) error {
	p.logger.Info("The merge function hasn't been implemented yet, just logging for now.")
	return nil
}

// GetTideContextPolicy gets context policy defined by users + requirements from
// prow jobs.
func (p *GerritProvider) GetTideContextPolicy(gitClient git.ClientFactory, org, repo, branch string, baseSHAGetter config.RefGetter, crc *CodeReviewCommon) (contextChecker, error) {
	pr := crc.Gerrit
	if pr == nil {
		return nil, errors.New("programmer error: crc.Gerrit cannot be nil for GerritProvider")
	}

	required := sets.NewString()
	requiredIfPresent := sets.NewString()
	optional := sets.NewString()

	headSHAGetter := func() (string, error) {
		return crc.HeadRefOID, nil
	}
	presubmits, err := p.cfg().GetPresubmits(gitClient, org+"/"+repo, baseSHAGetter, headSHAGetter)
	if err != nil {
		return nil, fmt.Errorf("failed to get presubmits: %w", err)
	}

	requireLabels := sets.NewString()
	for l, info := range pr.Labels {
		if !info.Optional {
			requireLabels.Insert(l)
		}
	}

	// generate required and optional entries for Prow Jobs
	for _, pj := range presubmits {
		if !pj.CouldRun(branch) {
			continue
		}

		var isJobRequired bool
		if val, ok := pj.Labels[kube.GerritReportLabel]; ok && requireLabels.Has(val) {
			isJobRequired = true
		}

		if isJobRequired {
			if pj.TriggersConditionally() {
				// jobs that trigger conditionally are required if present.
				requiredIfPresent.Insert(pj.Context)
			} else {
				// jobs that produce required contexts and will
				// always run should be required at all times
				required.Insert(pj.Context)
			}
		} else {
			optional.Insert(pj.Context)
		}
	}

	t := &config.TideContextPolicy{
		RequiredContexts:          required.List(),
		RequiredIfPresentContexts: requiredIfPresent.List(),
		OptionalContexts:          optional.List(),
	}
	if err := t.Validate(); err != nil {
		return t, err
	}
	return t, nil
}

func (p *GerritProvider) prMergeMethod(crc *CodeReviewCommon) (types.PullRequestMergeType, error) {
	var res types.PullRequestMergeType
	pr := crc.Gerrit
	if pr == nil {
		return res, errors.New("programmer error: crc.Gerrit cannot be nil for GerritProvider")
	}

	// Translate merge methods to types that git could understand. The merge
	// methods for gerrit are documented at
	// https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#repository.
	// Git can only understand MergeIfNecessary, MergeMerge, MergeRebase, MergeSquash.
	switch pr.SubmitType {
	case "MERGE_IF_NECESSARY":
		res = types.MergeIfNecessary
	case "FAST_FORWARD_ONLY":
		res = types.MergeMerge
	case "REBASE_IF_NECESSARY":
		res = types.MergeRebase
	case "REBASE_ALWAYS":
		res = types.MergeRebase
	case "MERGE_ALWAYS":
		res = types.MergeMerge
	default:
		res = types.MergeMerge
	}

	return res, nil
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"

	e "github.com/techjanitor/pram-get/errors"
	u "github.com/techjanitor/pram-get/utils"
)

// TagInfoModel holds the parameters from the request and also the key for the cache
type TagInfoModel struct {
	Id     uint
	Result TagInfoType
}

// IndexType is the top level of the JSON response
type TagInfoType struct {
	Tag TagInfo `json:"tag"`
}

// Header for tag page
type TagInfo struct {
	Id   uint   `json:"id"`
	Tag  string `json:"tag"`
	Type uint   `json:"type"`
}

// Get will gather the information from the database and return it as JSON serialized data
func (i *TagInfoModel) Get() (err error) {

	// Initialize response header
	response := TagInfoType{}

	// Initialize struct for tag info
	taginfo := TagInfo{}

	taginfo.Id = i.Id

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// Get tag name and type
	err = db.QueryRow("select tag_name,tagtype_id from tags where tag_id = ?", i.Id).Scan(&taginfo.Tag, &taginfo.Type)
	if err == sql.ErrNoRows {
		return e.ErrNotFound
	} else if err != nil {
		return
	}

	// Add taginfo to the response struct
	response.Tag = taginfo

	// This is the data we will serialize
	i.Result = response

	return

}
<commit_msg>change taginfo json name<commit_after>package models

import (
	"database/sql"

	e "github.com/techjanitor/pram-get/errors"
	u "github.com/techjanitor/pram-get/utils"
)

// TagInfoModel holds the parameters from the request and also the key for the cache
type TagInfoModel struct {
	Id     uint
	Result TagInfoType
}

// IndexType is the top level of the JSON response
type TagInfoType struct {
	Tag TagInfo `json:"taginfo"`
}

// Header for tag page
type TagInfo struct {
	Id   uint   `json:"id"`
	Tag  string `json:"tag"`
	Type uint   `json:"type"`
}

// Get will gather the information from the database and return it as JSON serialized data
func (i *TagInfoModel) Get() (err error) {

	// Initialize response header
	response := TagInfoType{}

	// Initialize struct for tag info
	taginfo := TagInfo{}

	taginfo.Id = i.Id

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// Get tag name and type
	err = db.QueryRow("select tag_name,tagtype_id from tags where tag_id = ?", i.Id).Scan(&taginfo.Tag, &taginfo.Type)
	if err == sql.ErrNoRows {
		return e.ErrNotFound
	} else if err != nil {
		return
	}

	// Add taginfo to the response struct
	response.Tag = taginfo

	// This is the data we will serialize
	i.Result = response

	return

}
<|endoftext|>


<commit_before>package adapters

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/prebid/prebid-server/cache/dummycache"
	"github.com/prebid/prebid-server/pbs"

	"fmt"

	"github.com/mxmCherry/openrtb"
)

type tagInfo struct {
	code        string
	placementID string
	bid         float64
	content     string
	delay       time.Duration
}

type bidInfo struct {
	partnerID   int
	domain      string
	page        string
	publisherID string
	tags        []tagInfo
	deviceIP    string
	deviceUA    string
	buyerUID    string
}

var fbdata bidInfo

type FacebookExt struct {
	PlatformID int `json:"platformid"`
}

func DummyFacebookServer(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var breq openrtb.BidRequest
	err = json.Unmarshal(body, &breq)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if string(breq.Ext) == "" {
		http.Error(w, "No Ext field provided", http.StatusInternalServerError)
		return
	}
	var fext FacebookExt
	err = json.Unmarshal(breq.Ext, &fext)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if fext.PlatformID != fbdata.partnerID {
		http.Error(w, fmt.Sprintf("Platform ID '%d' doesn't match '%d", fext.PlatformID, fbdata.partnerID), http.StatusInternalServerError)
		return
	}
	if breq.Site == nil {
		http.Error(w, fmt.Sprintf("No site object sent"), http.StatusInternalServerError)
		return
	}
	if breq.Site.Domain != fbdata.domain {
		http.Error(w, fmt.Sprintf("Domain '%s' doesn't match '%s", breq.Site.Domain, fbdata.domain), http.StatusInternalServerError)
		return
	}
	if breq.Site.Page != fbdata.page {
		http.Error(w, fmt.Sprintf("Page '%s' doesn't match '%s", breq.Site.Page, fbdata.page), http.StatusInternalServerError)
		return
	}
	if breq.Device.UA != fbdata.deviceUA {
		http.Error(w, fmt.Sprintf("UA '%s' doesn't match '%s", breq.Device.UA, fbdata.deviceUA), http.StatusInternalServerError)
		return
	}
	if breq.Device.IP != fbdata.deviceIP {
		http.Error(w, fmt.Sprintf("IP '%s' doesn't match '%s", breq.Device.IP, fbdata.deviceIP), http.StatusInternalServerError)
		return
	}
	if breq.User.BuyerUID != fbdata.buyerUID {
		http.Error(w, fmt.Sprintf("User ID '%s' doesn't match '%s", breq.User.BuyerUID, fbdata.buyerUID), http.StatusInternalServerError)
		return
	}
	if len(breq.Imp) != 1 {
		http.Error(w, fmt.Sprintf("Wrong number of imp objects sent: %d", len(breq.Imp)), http.StatusInternalServerError)
		return
	}
	var bid *openrtb.Bid
	for _, tag := range fbdata.tags {
		if breq.Imp[0].Banner == nil {
			http.Error(w, fmt.Sprintf("No banner object sent"), http.StatusInternalServerError)
			return
		}
		if breq.Imp[0].Banner.W != 300 || breq.Imp[0].Banner.H != 250 {
			http.Error(w, fmt.Sprintf("Size '%dx%d' not supported", breq.Imp[0].Banner.W, breq.Imp[0].Banner.H), http.StatusInternalServerError)
			return
		}
		if breq.Imp[0].TagID == tag.placementID {
			bid = &openrtb.Bid{
				ID:    "random-id",
				ImpID: breq.Imp[0].ID,
				Price: tag.bid,
				AdM:   tag.content,
			}
			if tag.delay > 0 {
				<-time.After(tag.delay)
			}
		}
	}
	if bid == nil {
		http.Error(w, fmt.Sprintf("Placement ID '%s' not found", breq.Imp[0].TagID), http.StatusInternalServerError)
		return
	}

	resp := openrtb.BidResponse{
		ID:    "a-random-id",
		BidID: "another-random-id",
		Cur:   "USD",
		SeatBid: []openrtb.SeatBid{
			{
				Seat: "FBAN",
				Bid:  []openrtb.Bid{*bid},
			},
		},
	}

	js, err := json.Marshal(resp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func TestFacebookBasicResponse(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(DummyFacebookServer))
	defer server.Close()

	fbdata = bidInfo{
		partnerID:   12345678,
		domain:      "nytimes.com",
		page:        "https://www.nytimes.com/2017/05/04/movies/guardians-of-the-galaxy-2-review-chris-pratt.html?hpw&rref=movies&action=click&pgtype=Homepage&module=well-region&region=bottom-well&WT.nav=bottom-well&_r=0",
		publisherID: "987654321",
		tags:        make([]tagInfo, 2),
		deviceIP:    "25.91.96.36",
		deviceUA:    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30",
		buyerUID:    "need-an-actual-fb-id",
	}
	fbdata.tags[0] = tagInfo{
		code:        "first-tag",
		placementID: fmt.Sprintf("%s_999998888", fbdata.publisherID),
		bid:         1.67,
	}
	fbdata.tags[1] = tagInfo{
		code:        "second-tag",
		placementID: fmt.Sprintf("%s_66775544", fbdata.publisherID),
		bid:         3.22,
	}

	conf := *DefaultHTTPAdapterConfig
	an := NewFacebookAdapter(&conf, fmt.Sprintf("%d", fbdata.partnerID), "localhost")
	an.URI = server.URL
	an.nonSecureUri = server.URL

	pbin := pbs.PBSRequest{
		AdUnits: make([]pbs.AdUnit, 2),
	}
	for i, tag := range fbdata.tags {
		pbin.AdUnits[i] = pbs.AdUnit{
			Code:       tag.code,
			MediaTypes: []string{"BANNER"},
			Sizes: []openrtb.Format{
				{
					W: 300,
					H: 250,
				},
			},
			Bids: []pbs.Bids{
				pbs.Bids{
					BidderCode: "audienceNetwork",
					BidID:      fmt.Sprintf("random-id-from-pbjs-%d", i),
					Params:     json.RawMessage(fmt.Sprintf("{\"placementId\": \"%s\"}", tag.placementID)),
				},
			},
		}
	}

	body := new(bytes.Buffer)
	err := json.NewEncoder(body).Encode(pbin)
	if err != nil {
		t.Fatalf("Json encoding failed: %v", err)
	}

	fmt.Println("body", body)

	req := httptest.NewRequest("POST", server.URL, body)
	req.Header.Add("Referer", fbdata.page)
	req.Header.Add("User-Agent", fbdata.deviceUA)
	req.Header.Add("X-Real-IP", fbdata.deviceIP)

	pc := pbs.ParsePBSCookieFromRequest(req)
	pc.TrySync("audienceNetwork", fbdata.buyerUID)
	fakewriter := httptest.NewRecorder()
	pc.SetCookieOnResponse(fakewriter, "")
	req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

	cacheClient, _ := dummycache.New()
	pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
	if err != nil {
		t.Fatalf("ParsePBSRequest failed: %v", err)
	}
	if len(pbReq.Bidders) != 1 {
		t.Fatalf("ParsePBSRequest returned %d bidders instead of 1", len(pbReq.Bidders))
	}
	if pbReq.Bidders[0].BidderCode != "audienceNetwork" {
		t.Fatalf("ParsePBSRequest returned invalid bidder")
	}

	ctx := context.TODO()
	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
	if err != nil {
		t.Fatalf("Should not have gotten an error: %v", err)
	}
	if len(bids) != 2 {
		t.Fatalf("Received %d bids instead of 2", len(bids))
	}
	for _, bid := range bids {
		matched := false
		for _, tag := range fbdata.tags {
			if bid.AdUnitCode == tag.code {
				matched = true
				if bid.BidderCode != "audienceNetwork" {
					t.Errorf("Incorrect BidderCode '%s'", bid.BidderCode)
				}
				if bid.Price != tag.bid {
					t.Errorf("Incorrect bid price '%.2f' expected '%.2f'", bid.Price, tag.bid)
				}
				if bid.Width != 300 || bid.Height != 250 {
					t.Errorf("Incorrect bid size %dx%d, expected 300x250", bid.Width, bid.Height)
				}
				if bid.Adm != tag.content {
					t.Errorf("Incorrect bid markup '%s' expected '%s'", bid.Adm, tag.content)
				}
			}
		}
		if !matched {
			t.Errorf("Received bid for unknown ad unit '%s'", bid.AdUnitCode)
		}
	}

	// same test but with one request timing out
	fbdata.tags[0].delay = 20 * time.Millisecond
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
	defer cancel()

	bids, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
	if err != nil {
		// only get an error if everything fails
		t.Fatalf("Should not have gotten an error: %v", err)
	}
	if len(bids) != 1 {
		t.Fatalf("Received %d bids instead of 1", len(bids))
	}
	if bids[0].AdUnitCode != fbdata.tags[1].code {
		t.Fatalf("Didn't receive bid from non-timed out request")
	}
	if bids[0].Price != fbdata.tags[1].bid {
		t.Errorf("Incorrect bid price '%.2f' expected '%.2f'", bids[0].Price, fbdata.tags[1].bid)
	}
}

func TestFacebookUserSyncInfo(t *testing.T) {
	url := "https://www.facebook.com/audiencenetwork/idsync/?partner=partnerId&callback=localhost%2Fsetuid%3Fbidder%3DaudienceNetwork%26uid%3D%24UID"

	an := NewFacebookAdapter(DefaultHTTPAdapterConfig, "partnerId", url)
	if an.usersyncInfo.URL != url {
		t.Fatalf("should have matched")
	}
	if an.usersyncInfo.Type != "redirect" {
		t.Fatalf("should be redirect")
	}
	if an.usersyncInfo.SupportCORS != false {
		t.Fatalf("should have been false")
	}
}
<commit_msg>Facebook fix test (fixes issue #103) (#135)<commit_after>package adapters

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/prebid/prebid-server/cache/dummycache"
	"github.com/prebid/prebid-server/pbs"

	"fmt"

	"github.com/mxmCherry/openrtb"
)

type tagInfo struct {
	code        string
	placementID string
	bid         float64
	content     string
	delay       time.Duration
}

type bidInfo struct {
	partnerID   int
	domain      string
	page        string
	publisherID string
	tags        []tagInfo
	deviceIP    string
	deviceUA    string
	buyerUID    string
}

var fbdata bidInfo

type FacebookExt struct {
	PlatformID int `json:"platformid"`
}

func DummyFacebookServer(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var breq openrtb.BidRequest
	err = json.Unmarshal(body, &breq)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if string(breq.Ext) == "" {
		http.Error(w, "No Ext field provided", http.StatusInternalServerError)
		return
	}
	var fext FacebookExt
	err = json.Unmarshal(breq.Ext, &fext)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if fext.PlatformID != fbdata.partnerID {
		http.Error(w, fmt.Sprintf("Platform ID '%d' doesn't match '%d", fext.PlatformID, fbdata.partnerID), http.StatusInternalServerError)
		return
	}
	if breq.Site == nil {
		http.Error(w, fmt.Sprintf("No site object sent"), http.StatusInternalServerError)
		return
	}
	if breq.Site.Domain != fbdata.domain {
		http.Error(w, fmt.Sprintf("Domain '%s' doesn't match '%s", breq.Site.Domain, fbdata.domain), http.StatusInternalServerError)
		return
	}
	if breq.Site.Page != fbdata.page {
		http.Error(w, fmt.Sprintf("Page '%s' doesn't match '%s", breq.Site.Page, fbdata.page), http.StatusInternalServerError)
		return
	}
	if breq.Device.UA != fbdata.deviceUA {
		http.Error(w, fmt.Sprintf("UA '%s' doesn't match '%s", breq.Device.UA, fbdata.deviceUA), http.StatusInternalServerError)
		return
	}
	if breq.Device.IP != fbdata.deviceIP {
		http.Error(w, fmt.Sprintf("IP '%s' doesn't match '%s", breq.Device.IP, fbdata.deviceIP), http.StatusInternalServerError)
		return
	}
	if breq.User.BuyerUID != fbdata.buyerUID {
		http.Error(w, fmt.Sprintf("User ID '%s' doesn't match '%s", breq.User.BuyerUID, fbdata.buyerUID), http.StatusInternalServerError)
		return
	}
	if len(breq.Imp) != 1 {
		http.Error(w, fmt.Sprintf("Wrong number of imp objects sent: %d", len(breq.Imp)), http.StatusInternalServerError)
		return
	}
	var bid *openrtb.Bid
	for _, tag := range fbdata.tags {
		if breq.Imp[0].Banner == nil {
			http.Error(w, fmt.Sprintf("No banner object sent"), http.StatusInternalServerError)
			return
		}
		if breq.Imp[0].Banner.W != 300 || breq.Imp[0].Banner.H != 250 {
			http.Error(w, fmt.Sprintf("Size '%dx%d' not supported", breq.Imp[0].Banner.W, breq.Imp[0].Banner.H), http.StatusInternalServerError)
			return
		}
		if breq.Imp[0].TagID == tag.placementID {
			bid = &openrtb.Bid{
				ID:    "random-id",
				ImpID: breq.Imp[0].ID,
				Price: tag.bid,
				AdM:   tag.content,
			}
			if tag.delay > 0 {
				<-time.After(tag.delay)
			}
		}
	}
	if bid == nil {
		http.Error(w, fmt.Sprintf("Placement ID '%s' not found", breq.Imp[0].TagID), http.StatusInternalServerError)
		return
	}

	resp := openrtb.BidResponse{
		ID:    "a-random-id",
		BidID: "another-random-id",
		Cur:   "USD",
		SeatBid: []openrtb.SeatBid{
			{
				Seat: "FBAN",
				Bid:  []openrtb.Bid{*bid},
			},
		},
	}

	js, err := json.Marshal(resp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func TestFacebookBasicResponse(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(DummyFacebookServer))
	defer server.Close()

	fbdata = bidInfo{
		partnerID:   12345678,
		domain:      "nytimes.com",
		page:        "https://www.nytimes.com/2017/05/04/movies/guardians-of-the-galaxy-2-review-chris-pratt.html?hpw&rref=movies&action=click&pgtype=Homepage&module=well-region&region=bottom-well&WT.nav=bottom-well&_r=0",
		publisherID: "987654321",
		tags:        make([]tagInfo, 2),
		deviceIP:    "25.91.96.36",
		deviceUA:    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30",
		buyerUID:    "need-an-actual-fb-id",
	}
	fbdata.tags[0] = tagInfo{
		code:        "first-tag",
		placementID: fmt.Sprintf("%s_999998888", fbdata.publisherID),
		bid:         1.67,
	}
	fbdata.tags[1] = tagInfo{
		code:        "second-tag",
		placementID: fmt.Sprintf("%s_66775544", fbdata.publisherID),
		bid:         3.22,
	}

	conf := *DefaultHTTPAdapterConfig
	an := NewFacebookAdapter(&conf, fmt.Sprintf("%d", fbdata.partnerID), "localhost")
	an.URI = server.URL
	an.nonSecureUri = server.URL

	pbin := pbs.PBSRequest{
		AdUnits: make([]pbs.AdUnit, 2),
	}
	for i, tag := range fbdata.tags {
		pbin.AdUnits[i] = pbs.AdUnit{
			Code:       tag.code,
			MediaTypes: []string{"BANNER"},
			Sizes: []openrtb.Format{
				{
					W: 300,
					H: 250,
				},
			},
			Bids: []pbs.Bids{
				pbs.Bids{
					BidderCode: "audienceNetwork",
					BidID:      fmt.Sprintf("random-id-from-pbjs-%d", i),
					Params:     json.RawMessage(fmt.Sprintf("{\"placementId\": \"%s\"}", tag.placementID)),
				},
			},
		}
	}

	body := new(bytes.Buffer)
	err := json.NewEncoder(body).Encode(pbin)
	if err != nil {
		t.Fatalf("Json encoding failed: %v", err)
	}

	fmt.Println("body", body)

	req := httptest.NewRequest("POST", server.URL, body)
	req.Header.Add("Referer", fbdata.page)
	req.Header.Add("User-Agent", fbdata.deviceUA)
	req.Header.Add("X-Real-IP", fbdata.deviceIP)

	pc := pbs.ParsePBSCookieFromRequest(req)
	pc.TrySync("audienceNetwork", fbdata.buyerUID)
	fakewriter := httptest.NewRecorder()
	pc.SetCookieOnResponse(fakewriter, "")
	req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

	cacheClient, _ := dummycache.New()
	pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
	if err != nil {
		t.Fatalf("ParsePBSRequest failed: %v", err)
	}
	if len(pbReq.Bidders) != 1 {
		t.Fatalf("ParsePBSRequest returned %d bidders instead of 1", len(pbReq.Bidders))
	}
	if pbReq.Bidders[0].BidderCode != "audienceNetwork" {
		t.Fatalf("ParsePBSRequest returned invalid bidder")
	}

	ctx := context.TODO()
	bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0])
	if err != nil {
		t.Fatalf("Should not have gotten an error: %v", err)
	}
	if len(bids) != 2 {
		t.Fatalf("Received %d bids instead of 2", len(bids))
	}
	for _, bid := range bids {
		matched := false
		for _, tag := range fbdata.tags {
			if bid.AdUnitCode == tag.code {
				matched = true
				if bid.BidderCode != "audienceNetwork" {
					t.Errorf("Incorrect BidderCode '%s'", bid.BidderCode)
				}
				if bid.Price != tag.bid {
					t.Errorf("Incorrect bid price '%.2f' expected '%.2f'", bid.Price, tag.bid)
				}
				if bid.Width != 300 || bid.Height != 250 {
					t.Errorf("Incorrect bid size %dx%d, expected 300x250", bid.Width, bid.Height)
				}
				if bid.Adm != tag.content {
					t.Errorf("Incorrect bid markup '%s' expected '%s'", bid.Adm, tag.content)
				}
			}
		}
		if !matched {
			t.Errorf("Received bid for unknown ad unit '%s'", bid.AdUnitCode)
		}
	}

	// same test but with one request timing out
	fbdata.tags[0].delay = 20 * time.Millisecond
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
	defer cancel()

	bids, err = an.Call(ctx, pbReq, pbReq.Bidders[0])
	if err != nil {
		// only get an error if everything fails
		t.Fatalf("Should not have gotten an error: %v", err)
	}
	if len(bids) != 1 {
		t.Fatalf("Received %d bids instead of 1", len(bids))
	}
	if bids[0].AdUnitCode != fbdata.tags[1].code {
		t.Fatalf("Didn't receive bid from non-timed out request")
	}
	if bids[0].Price != fbdata.tags[1].bid {
		t.Errorf("Incorrect bid price '%.2f' expected '%.2f'", bids[0].Price, fbdata.tags[1].bid)
	}
}

func TestFacebookUserSyncInfo(t *testing.T) {
	url := "https://www.facebook.com/audiencenetwork/idsync/?partner=partnerId&callback=localhost%2Fsetuid%3Fbidder%3DaudienceNetwork%26uid%3D%24UID"

	an := NewFacebookAdapter(DefaultHTTPAdapterConfig, "partnerId", url)
	if an.usersyncInfo.URL != url {
		t.Fatalf("should have matched")
	}
	if an.usersyncInfo.Type != "redirect" {
		t.Fatalf("should be redirect")
	}
	if an.usersyncInfo.SupportCORS != false {
		t.Fatalf("should have been false")
	}
}
<|endoftext|>


<commit_before>package tail

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/parsers"
	"github.com/influxdata/telegraf/plugins/parsers/csv"
	"github.com/influxdata/telegraf/plugins/parsers/json"
	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestTailFromBeginning(t *testing.T) {
	if os.Getenv("CIRCLE_PROJECT_REPONAME") != "" {
		t.Skip("Skipping CI testing due to race conditions")
	}

	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(1)
	acc.AssertContainsTaggedFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		},
		map[string]string{
			"mytag": "foo",
			"path":  tmpfile.Name(),
		})
}

func TestTailFromEnd(t *testing.T) {
	if os.Getenv("CIRCLE_PROJECT_REPONAME") != "" {
		t.Skip("Skipping CI testing due to race conditions")
	}

	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	for _, tailer := range tt.tailers {
		for n, err := tailer.Tell(); err == nil && n == 0; n, err = tailer.Tell() {
			// wait for tailer to jump to end
			runtime.Gosched()
		}
	}

	_, err = tmpfile.WriteString("cpu,othertag=foo usage_idle=100\n")
	require.NoError(t, err)
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(1)
	acc.AssertContainsTaggedFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		},
		map[string]string{
			"othertag": "foo",
			"path":     tmpfile.Name(),
		})
	assert.Len(t, acc.Metrics, 1)
}

func TestTailBadLine(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()

	buf := &bytes.Buffer{}
	log.SetOutput(buf)

	require.NoError(t, acc.GatherError(tt.Gather))

	_, err = tmpfile.WriteString("cpu mytag= foo usage_idle= 100\n")
	require.NoError(t, err)

	time.Sleep(500 * time.Millisecond)
	assert.Contains(t, buf.String(), "Malformed log line")
}

func TestTailDosLineendings(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu usage_idle=100\r\ncpu2 usage_idle=200\r\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(2)
	acc.AssertContainsFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		})
	acc.AssertContainsFields(t, "cpu2",
		map[string]interface{}{
			"usage_idle": float64(200),
		})
}

// The csv parser should only parse the header line once per file.
func TestCSVHeadersParsedOnce(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer func() {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
	}()

	_, err = tmpfile.WriteString(`
measurement,time_idle
cpu,42
cpu,42
`)
	require.NoError(t, err)

	plugin := NewTail()
	plugin.Log = testutil.Logger{}
	plugin.FromBeginning = true
	plugin.Files = []string{tmpfile.Name()}
	plugin.SetParserFunc(func() (parsers.Parser, error) {
		return &csv.Parser{
			MeasurementColumn: "measurement",
			HeaderRowCount:    1,
			TimeFunc:          func() time.Time { return time.Unix(0, 0) },
		}, nil
	})

	err = plugin.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	err = plugin.Start(&acc)
	require.NoError(t, err)
	defer plugin.Stop()
	err = plugin.Gather(&acc)
	require.NoError(t, err)
	acc.Wait(2)
	plugin.Stop()

	expected := []telegraf.Metric{
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42,
			},
			time.Unix(0, 0)),
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42,
			},
			time.Unix(0, 0)),
	}
	testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics())
}

// Ensure that the first line can produce multiple metrics (#6138)
func TestMultipleMetricsOnFirstLine(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer func() {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
	}()

	_, err = tmpfile.WriteString(`
[{"time_idle": 42}, {"time_idle": 42}]
`)
	require.NoError(t, err)

	plugin := NewTail()
	plugin.Log = testutil.Logger{}
	plugin.FromBeginning = true
	plugin.Files = []string{tmpfile.Name()}
	plugin.SetParserFunc(func() (parsers.Parser, error) {
		return json.New(
			&json.Config{
				MetricName: "cpu",
			})
	})

	err = plugin.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	err = plugin.Start(&acc)
	require.NoError(t, err)
	defer plugin.Stop()
	err = plugin.Gather(&acc)
	require.NoError(t, err)
	acc.Wait(2)
	plugin.Stop()

	expected := []telegraf.Metric{
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42.0,
			},
			time.Unix(0, 0)),
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42.0,
			},
			time.Unix(0, 0)),
	}
	testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}
<commit_msg>Fix data race in tail input tests (#7780)<commit_after>package tail

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/parsers"
	"github.com/influxdata/telegraf/plugins/parsers/csv"
	"github.com/influxdata/telegraf/plugins/parsers/json"
	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestTailFromBeginning(t *testing.T) {
	if os.Getenv("CIRCLE_PROJECT_REPONAME") != "" {
		t.Skip("Skipping CI testing due to race conditions")
	}

	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(1)
	acc.AssertContainsTaggedFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		},
		map[string]string{
			"mytag": "foo",
			"path":  tmpfile.Name(),
		})
}

func TestTailFromEnd(t *testing.T) {
	if os.Getenv("CIRCLE_PROJECT_REPONAME") != "" {
		t.Skip("Skipping CI testing due to race conditions")
	}

	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	for _, tailer := range tt.tailers {
		for n, err := tailer.Tell(); err == nil && n == 0; n, err = tailer.Tell() {
			// wait for tailer to jump to end
			runtime.Gosched()
		}
	}

	_, err = tmpfile.WriteString("cpu,othertag=foo usage_idle=100\n")
	require.NoError(t, err)
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(1)
	acc.AssertContainsTaggedFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		},
		map[string]string{
			"othertag": "foo",
			"path":     tmpfile.Name(),
		})
	assert.Len(t, acc.Metrics, 1)
}

func TestTailBadLine(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))

	buf := &bytes.Buffer{}
	log.SetOutput(buf)

	require.NoError(t, acc.GatherError(tt.Gather))

	_, err = tmpfile.WriteString("cpu mytag= foo usage_idle= 100\n")
	require.NoError(t, err)

	time.Sleep(500 * time.Millisecond)
	tt.Stop()
	assert.Contains(t, buf.String(), "Malformed log line")
}

func TestTailDosLineendings(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	_, err = tmpfile.WriteString("cpu usage_idle=100\r\ncpu2 usage_idle=200\r\n")
	require.NoError(t, err)

	tt := NewTail()
	tt.Log = testutil.Logger{}
	tt.FromBeginning = true
	tt.Files = []string{tmpfile.Name()}
	tt.SetParserFunc(parsers.NewInfluxParser)

	err = tt.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	require.NoError(t, tt.Start(&acc))
	defer tt.Stop()
	require.NoError(t, acc.GatherError(tt.Gather))

	acc.Wait(2)
	acc.AssertContainsFields(t, "cpu",
		map[string]interface{}{
			"usage_idle": float64(100),
		})
	acc.AssertContainsFields(t, "cpu2",
		map[string]interface{}{
			"usage_idle": float64(200),
		})
}

// The csv parser should only parse the header line once per file.
func TestCSVHeadersParsedOnce(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer func() {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
	}()

	_, err = tmpfile.WriteString(`
measurement,time_idle
cpu,42
cpu,42
`)
	require.NoError(t, err)

	plugin := NewTail()
	plugin.Log = testutil.Logger{}
	plugin.FromBeginning = true
	plugin.Files = []string{tmpfile.Name()}
	plugin.SetParserFunc(func() (parsers.Parser, error) {
		return &csv.Parser{
			MeasurementColumn: "measurement",
			HeaderRowCount:    1,
			TimeFunc:          func() time.Time { return time.Unix(0, 0) },
		}, nil
	})

	err = plugin.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	err = plugin.Start(&acc)
	require.NoError(t, err)
	defer plugin.Stop()
	err = plugin.Gather(&acc)
	require.NoError(t, err)
	acc.Wait(2)
	plugin.Stop()

	expected := []telegraf.Metric{
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42,
			},
			time.Unix(0, 0)),
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42,
			},
			time.Unix(0, 0)),
	}
	testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics())
}

// Ensure that the first line can produce multiple metrics (#6138)
func TestMultipleMetricsOnFirstLine(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "")
	require.NoError(t, err)
	defer func() {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
	}()

	_, err = tmpfile.WriteString(`
[{"time_idle": 42}, {"time_idle": 42}]
`)
	require.NoError(t, err)

	plugin := NewTail()
	plugin.Log = testutil.Logger{}
	plugin.FromBeginning = true
	plugin.Files = []string{tmpfile.Name()}
	plugin.SetParserFunc(func() (parsers.Parser, error) {
		return json.New(
			&json.Config{
				MetricName: "cpu",
			})
	})

	err = plugin.Init()
	require.NoError(t, err)

	acc := testutil.Accumulator{}
	err = plugin.Start(&acc)
	require.NoError(t, err)
	defer plugin.Stop()
	err = plugin.Gather(&acc)
	require.NoError(t, err)
	acc.Wait(2)
	plugin.Stop()

	expected := []telegraf.Metric{
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42.0,
			},
			time.Unix(0, 0)),
		testutil.MustMetric("cpu",
			map[string]string{
				"path": tmpfile.Name(),
			},
			map[string]interface{}{
				"time_idle": 42.0,
			},
			time.Unix(0, 0)),
	}
	testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}
<|endoftext|>


<commit_before>package buildbox

import (
  "fmt"
  "time"
  "github.com/kr/pty"
  "os/exec"
  "io"
  "os"
  "bytes"
  "log"
  "path"
  "path/filepath"
  "errors"
  "syscall"
)

type Process struct {
  Output string
  Pid int
  Running bool
  ExitStatus int
  command *exec.Cmd
}

// Implement the Stringer thingy
func (p Process) String() string {
  return fmt.Sprintf("Process{Pid: %d, Running: %t, ExitStatus: %d}", p.Pid, p.Running, p.ExitStatus)
}

func (p Process) Kill() error {
  return p.command.Process.Kill()
}

func RunScript(dir string, script string, env []string, callback func(Process)) (*Process, error) {
  // Create a new instance of our process struct
  var process Process

  // Find the script to run
  absoluteDir, _ := filepath.Abs(dir)
  pathToScript := path.Join(absoluteDir, script)

  log.Printf("Running script `%s` from inside %s\n", script, absoluteDir)

  process.command = exec.Command(pathToScript)
  process.command.Dir = absoluteDir

  // Copy the current processes ENV and merge in the
  // new ones. We do this so the sub process gets PATH
  // and stuff.
  // TODO: Is this correct?
  currentEnv := os.Environ()
  process.command.Env = append(currentEnv, env...)

  // Start our process
  pty, err := pty.Start(process.command)
  if err != nil {
    // The process essentially failed, so we'll just make up
    // and exit status.
    process.ExitStatus = 1

    return &process, err
  }

  process.Pid = process.command.Process.Pid
  process.Running = true

  var buffer bytes.Buffer

  go func() {
    // Copy the pty to our buffer. This will block until it EOF's
    // or something breaks.
    _, err = io.Copy(&buffer, pty)
    if err != nil {
      log.Printf("io.Copy failed with error: %s\n", err)
    }
  }()

  go func(){
    for process.Running {
      // Convert the stdout buffer to a string
      process.Output = buffer.String()

      // Call the callback and pass in our process object
      callback(process)

      // Sleep for 1 second
      time.Sleep(1000 * time.Millisecond)
    }
  }()

  // Wait until the process has finished
  waitResult := process.command.Wait()

  // Update the process with the final results
  // of the script
  process.Running = false
  process.ExitStatus = getExitStatus(waitResult)
  process.Output = buffer.String()

  // No error occured so we can return nil
  return &process, nil
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// WTF? Computers... (shrug)
// TODO: Can this be better?
func getExitStatus(waitResult error) int {
  if waitResult != nil {
    if err, ok := waitResult.(*exec.ExitError); ok {
      if s, ok := err.Sys().(syscall.WaitStatus); ok {
        return s.ExitStatus()
      } else {
        panic(errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus."))
      }
    }
  }
  return 0
}
<commit_msg>Ensure output and monitoring goroutines complete before finalising job.<commit_after>package buildbox

import (
  "fmt"
  "time"
  "github.com/kr/pty"
  "os/exec"
  "io"
  "os"
  "bytes"
  "log"
  "path"
  "path/filepath"
  "errors"
  "syscall"
  "sync"
)

type Process struct {
  Output string
  Pid int
  Running bool
  ExitStatus int
  command *exec.Cmd
}

// Implement the Stringer thingy
func (p Process) String() string {
  return fmt.Sprintf("Process{Pid: %d, Running: %t, ExitStatus: %d}", p.Pid, p.Running, p.ExitStatus)
}

func (p Process) Kill() error {
  return p.command.Process.Kill()
}

func RunScript(dir string, script string, env []string, callback func(Process)) (*Process, error) {
  // Create a new instance of our process struct
  var process Process

  // Find the script to run
  absoluteDir, _ := filepath.Abs(dir)
  pathToScript := path.Join(absoluteDir, script)

  log.Printf("Running script `%s` from inside %s\n", script, absoluteDir)

  process.command = exec.Command(pathToScript)
  process.command.Dir = absoluteDir

  // Copy the current processes ENV and merge in the
  // new ones. We do this so the sub process gets PATH
  // and stuff.
  // TODO: Is this correct?
  currentEnv := os.Environ()
  process.command.Env = append(currentEnv, env...)

  // Start our process
  pty, err := pty.Start(process.command)
  if err != nil {
    // The process essentially failed, so we'll just make up
    // and exit status.
    process.ExitStatus = 1

    return &process, err
  }

  process.Pid = process.command.Process.Pid
  process.Running = true

  var buffer bytes.Buffer
  var w sync.WaitGroup
  w.Add(2)

  go func() {
    // Copy the pty to our buffer. This will block until it EOF's
    // or something breaks.
    _, err = io.Copy(&buffer, pty)
    if err != nil {
      log.Printf("io.Copy failed with error: %s\n", err)
    }
    w.Done()
  }()

  go func(){
    for process.Running {
      // Convert the stdout buffer to a string
      process.Output = buffer.String()

      // Call the callback and pass in our process object
      callback(process)

      // Sleep for 1 second
      time.Sleep(1000 * time.Millisecond)
    }
    w.Done()
  }()

  // Wait until the process has finished
  waitResult := process.command.Wait()

  // Update the process with the final results
  // of the script
  process.Running = false
  process.ExitStatus = getExitStatus(waitResult)

  // wait for the Copy and incremental output to finish first
  w.Wait()
  process.Output = buffer.String()

  // No error occured so we can return nil
  return &process, nil
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// WTF? Computers... (shrug)
// TODO: Can this be better?
func getExitStatus(waitResult error) int {
  if waitResult != nil {
    if err, ok := waitResult.(*exec.ExitError); ok {
      if s, ok := err.Sys().(syscall.WaitStatus); ok {
        return s.ExitStatus()
      } else {
        panic(errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus."))
      }
    }
  }
  return 0
}
<|endoftext|>


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

// builder package defines the library for invoking the full compilation of Serulian code.
package builder

import (
	"io/ioutil"
	"log"
	"path"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/generator/es5"
	"github.com/serulian/compiler/graphs/scopegraph"
	"github.com/serulian/compiler/packageloader"

	"github.com/fatih/color"
)

// CORE_LIBRARY contains the location of the Serulian core library.
var CORE_LIBRARY = packageloader.Library{
	// TODO: this should be set to a defined tag once the compiler is stable.
	PathOrURL: "github.com/Serulian/corelib:master",
	IsSCM:     true,
}

func outputWarnings(warnings []compilercommon.SourceWarning) {
	highlight := color.New(color.FgYellow, color.Bold)
	location := color.New(color.FgWhite)
	message := color.New(color.FgHiWhite)

	for _, warning := range warnings {
		highlight.Print("WARNING: ")
		location.Printf("At %v:%v:%v: ", warning.SourceAndLocation().Source(), warning.SourceAndLocation().Location().LineNumber(), warning.SourceAndLocation().Location().ColumnPosition())
		message.Printf("%s\n", warning.String())
	}
}

func outputErrors(errors []compilercommon.SourceError) {
	highlight := color.New(color.FgRed, color.Bold)
	location := color.New(color.FgWhite)
	message := color.New(color.FgHiWhite)

	for _, err := range errors {
		highlight.Print("ERROR: ")
		location.Printf("At %v:%v:%v: ", err.SourceAndLocation().Source(), err.SourceAndLocation().Location().LineNumber(), err.SourceAndLocation().Location().ColumnPosition())
		message.Printf("%s\n", err.Error())
	}
}

// BuildSource invokes the compiler starting at the given root source file path.
func BuildSource(rootSourceFilePath string, debug bool) bool {
	// Disable logging unless the debug flag is on.
	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	// Build a scope graph for the project. This will conduct parsing and type graph
	// construction on our behalf.
	scopeResult := scopegraph.ParseAndBuildScopeGraph(rootSourceFilePath, CORE_LIBRARY)

	outputWarnings(scopeResult.Warnings)
	if !scopeResult.Status {
		outputErrors(scopeResult.Errors)
		return false
	}

	// Generate the program's source.
	generated, err := es5.GenerateES5(scopeResult.Graph)
	if err != nil {
		panic(err)
	}

	// Write the source.
	filename := path.Base(rootSourceFilePath) + ".js"
	ioutil.WriteFile(filename, []byte(generated), 0644)
	return true
}
<commit_msg>Fix error and warning messages to be 1-indexed instead of 0<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// builder package defines the library for invoking the full compilation of Serulian code.
package builder

import (
	"io/ioutil"
	"log"
	"path"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/generator/es5"
	"github.com/serulian/compiler/graphs/scopegraph"
	"github.com/serulian/compiler/packageloader"

	"github.com/fatih/color"
)

// CORE_LIBRARY contains the location of the Serulian core library.
var CORE_LIBRARY = packageloader.Library{
	// TODO: this should be set to a defined tag once the compiler is stable.
	PathOrURL: "github.com/Serulian/corelib:master",
	IsSCM:     true,
}

func outputWarnings(warnings []compilercommon.SourceWarning) {
	highlight := color.New(color.FgYellow, color.Bold)
	location := color.New(color.FgWhite)
	message := color.New(color.FgHiWhite)

	for _, warning := range warnings {
		highlight.Print("WARNING: ")
		location.Printf("At %v:%v:%v: ", warning.SourceAndLocation().Source(), warning.SourceAndLocation().Location().LineNumber()+1, warning.SourceAndLocation().Location().ColumnPosition()+1)
		message.Printf("%s\n", warning.String())
	}
}

func outputErrors(errors []compilercommon.SourceError) {
	highlight := color.New(color.FgRed, color.Bold)
	location := color.New(color.FgWhite)
	message := color.New(color.FgHiWhite)

	for _, err := range errors {
		highlight.Print("ERROR: ")
		location.Printf("At %v:%v:%v: ", err.SourceAndLocation().Source(), err.SourceAndLocation().Location().LineNumber()+1, err.SourceAndLocation().Location().ColumnPosition()+1)
		message.Printf("%s\n", err.Error())
	}
}

// BuildSource invokes the compiler starting at the given root source file path.
func BuildSource(rootSourceFilePath string, debug bool) bool {
	// Disable logging unless the debug flag is on.
	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	// Build a scope graph for the project. This will conduct parsing and type graph
	// construction on our behalf.
	scopeResult := scopegraph.ParseAndBuildScopeGraph(rootSourceFilePath, CORE_LIBRARY)

	outputWarnings(scopeResult.Warnings)
	if !scopeResult.Status {
		outputErrors(scopeResult.Errors)
		return false
	}

	// Generate the program's source.
	generated, err := es5.GenerateES5(scopeResult.Graph)
	if err != nil {
		panic(err)
	}

	// Write the source.
	filename := path.Base(rootSourceFilePath) + ".js"
	ioutil.WriteFile(filename, []byte(generated), 0644)
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/cloudfoundry-incubator/garden/warden"
	"github.com/concourse/atc/api"
	"github.com/concourse/atc/builds"
	thijack "github.com/concourse/turbine/api/hijack"
	"github.com/kr/pty"
	"github.com/pkg/term"
	"github.com/tedsuo/rata"
)

func hijack(reqGenerator *rata.RequestGenerator) {
	argv := flag.Args()[1:]

	var path string
	var args []string

	switch len(argv) {
	case 0:
		path = "bash"
	case 1:
		path = argv[0]
	default:
		path = argv[0]
		args = argv[1:]
	}

	var ttySpec *warden.TTYSpec

	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		ttySpec = &warden.TTYSpec{
			WindowSize: &warden.WindowSize{
				Columns: cols,
				Rows:    rows,
			},
		}
	}

	spec := warden.ProcessSpec{
		Path: path,
		Args: args,
		Env:  []string{"TERM=" + os.Getenv("TERM")},

		Privileged: true,
		TTY:        ttySpec,
	}

	buildsReq, err := reqGenerator.CreateRequest(
		api.ListBuilds,
		nil,
		nil,
	)
	if err != nil {
		log.Fatalln(err)
	}

	buildsResp, err := http.DefaultClient.Do(buildsReq)
	if err != nil {
		log.Fatalln(err)
	}

	var builds []builds.Build
	err = json.NewDecoder(buildsResp.Body).Decode(&builds)
	if err != nil {
		log.Fatalln(err)
	}

	if len(builds) == 0 {
		println("no builds to hijack")
		os.Exit(1)
	}

	build := builds[0]

	payload, err := json.Marshal(spec)
	if err != nil {
		log.Fatalln(err)
	}

	hijackReq, err := reqGenerator.CreateRequest(
		api.HijackBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		bytes.NewBuffer(payload),
	)
	if err != nil {
		log.Fatalln(err)
	}

	conn, err := net.Dial("tcp", hijackReq.URL.Host)
	if err != nil {
		log.Fatalln("failed to dial hijack endpoint:", err)
	}

	client := httputil.NewClientConn(conn, nil)

	resp, err := client.Do(hijackReq)
	if err != nil {
		log.Fatalln("failed to hijack:", err)
	}

	if resp.StatusCode != http.StatusOK {
		log.Println("bad response:", resp.Status)
		resp.Body.Close()
		resp.Write(os.Stderr)
		os.Exit(1)
	}

	cconn, cbr := client.Hijack()

	term, err := term.Open(os.Stdin.Name())
	if err != nil {
		log.Fatalln("failed to open terminal:", err)
	}

	err = term.SetRaw()
	if err != nil {
		log.Fatalln("failed to set raw:", term)
	}

	defer term.Restore()

	encoder := gob.NewEncoder(cconn)

	resized := make(chan os.Signal, 10)
	signal.Notify(resized, syscall.SIGWINCH)

	go func() {
		for {
			<-resized
			sendSize(encoder)
		}
	}()

	go io.Copy(&stdinWriter{encoder}, term)

	io.Copy(os.Stdout, cbr)
}

func sendSize(enc *gob.Encoder) {
	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		enc.Encode(thijack.ProcessPayload{
			TTYSpec: &warden.TTYSpec{
				WindowSize: &warden.WindowSize{
					Columns: cols,
					Rows:    rows,
				},
			},
		})
	}
}

type stdinWriter struct {
	enc *gob.Encoder
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	err := w.enc.Encode(thijack.ProcessPayload{
		Stdin: d,
	})
	if err != nil {
		return 0, err
	}

	return len(d), nil
}
<commit_msg>support basic auth for hijacking<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/cloudfoundry-incubator/garden/warden"
	"github.com/concourse/atc/api"
	"github.com/concourse/atc/builds"
	thijack "github.com/concourse/turbine/api/hijack"
	"github.com/kr/pty"
	"github.com/pkg/term"
	"github.com/tedsuo/rata"
)

func hijack(reqGenerator *rata.RequestGenerator) {
	argv := flag.Args()[1:]

	var path string
	var args []string

	switch len(argv) {
	case 0:
		path = "bash"
	case 1:
		path = argv[0]
	default:
		path = argv[0]
		args = argv[1:]
	}

	var ttySpec *warden.TTYSpec

	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		ttySpec = &warden.TTYSpec{
			WindowSize: &warden.WindowSize{
				Columns: cols,
				Rows:    rows,
			},
		}
	}

	spec := warden.ProcessSpec{
		Path: path,
		Args: args,
		Env:  []string{"TERM=" + os.Getenv("TERM")},

		Privileged: true,
		TTY:        ttySpec,
	}

	buildsReq, err := reqGenerator.CreateRequest(
		api.ListBuilds,
		nil,
		nil,
	)
	if err != nil {
		log.Fatalln(err)
	}

	buildsResp, err := http.DefaultClient.Do(buildsReq)
	if err != nil {
		log.Fatalln(err)
	}

	var builds []builds.Build
	err = json.NewDecoder(buildsResp.Body).Decode(&builds)
	if err != nil {
		log.Fatalln(err)
	}

	if len(builds) == 0 {
		println("no builds to hijack")
		os.Exit(1)
	}

	build := builds[0]

	payload, err := json.Marshal(spec)
	if err != nil {
		log.Fatalln(err)
	}

	hijackReq, err := reqGenerator.CreateRequest(
		api.HijackBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		bytes.NewBuffer(payload),
	)
	if err != nil {
		log.Fatalln(err)
	}

	if hijackReq.URL.User != nil {
		hijackReq.Header.Add("Authorization", basicAuth(hijackReq.URL.User))
		hijackReq.URL.User = nil
	}

	conn, err := net.Dial("tcp", hijackReq.URL.Host)
	if err != nil {
		log.Fatalln("failed to dial hijack endpoint:", err)
	}

	client := httputil.NewClientConn(conn, nil)

	resp, err := client.Do(hijackReq)
	if err != nil {
		log.Fatalln("failed to hijack:", err)
	}

	if resp.StatusCode != http.StatusOK {
		log.Println("bad response:", resp.Status)
		resp.Body.Close()
		resp.Write(os.Stderr)
		os.Exit(1)
	}

	cconn, cbr := client.Hijack()

	term, err := term.Open(os.Stdin.Name())
	if err != nil {
		log.Fatalln("failed to open terminal:", err)
	}

	err = term.SetRaw()
	if err != nil {
		log.Fatalln("failed to set raw:", term)
	}

	defer term.Restore()

	encoder := gob.NewEncoder(cconn)

	resized := make(chan os.Signal, 10)
	signal.Notify(resized, syscall.SIGWINCH)

	go func() {
		for {
			<-resized
			sendSize(encoder)
		}
	}()

	go io.Copy(&stdinWriter{encoder}, term)

	io.Copy(os.Stdout, cbr)
}

func sendSize(enc *gob.Encoder) {
	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		enc.Encode(thijack.ProcessPayload{
			TTYSpec: &warden.TTYSpec{
				WindowSize: &warden.WindowSize{
					Columns: cols,
					Rows:    rows,
				},
			},
		})
	}
}

type stdinWriter struct {
	enc *gob.Encoder
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	err := w.enc.Encode(thijack.ProcessPayload{
		Stdin: d,
	})
	if err != nil {
		return 0, err
	}

	return len(d), nil
}

func basicAuth(user *url.Userinfo) string {
	username := user.Username()
	password, _ := user.Password()
	return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
}
<|endoftext|>


<commit_before>package psh

import (
	"github.com/coocood/assrt"
	"os/exec"
	"sync"
	"testing"
	"time"
)

func TestStateConsts(t *testing.T) {
	if !(UNSTARTED == 0 && RUNNING == 1 && FINISHED == 2 && PANICKED == 3) {
		t.Fail()
	}
}

// Test that we can exec something, wait, and it returns quickly and with an exit code.
func TestPshExecBasic(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		0,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshWaitTimeout(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("sleep", "1"),
	)
	cmdr.startCalmly()
	assert.Equal(
		false,
		cmdr.WaitSoon(20*time.Millisecond),
	)
	assert.Equal(
		-1,
		cmdr.exitCode,
	)
	assert.Equal(
		RUNNING,
		cmdr.State(),
	)
}

func TestPshExitListeners(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	var wg sync.WaitGroup
	wg.Add(1)
	cmdr.AddExitListener(func(*RunningCommand) {
		defer wg.Done()
		assert.Equal(
			0,
			cmdr.GetExitCode(),
		)
		assert.Equal(
			nil,
			cmdr.err,
		)
		assert.Equal(
			FINISHED,
			cmdr.State(),
		)
	})
	wg.Wait()
	assert.Equal(
		0,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshExitCode(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("sh", []string{"-c", "exit 22"}...),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		22,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshNonexistentCommandPanics(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("/thishadbetternotbeacommand"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		-1,
		cmdr.GetExitCode(),
	)
	assert.NotEqual(
		nil,
		cmdr.err,
	)
	assert.Equal(
		PANICKED,
		cmdr.State(),
	)
}
<commit_msg>comment out part of a test case that the go race detector (correctly) flags as a race.<commit_after>package psh

import (
	"github.com/coocood/assrt"
	"os/exec"
	"sync"
	"testing"
	"time"
)

func TestStateConsts(t *testing.T) {
	if !(UNSTARTED == 0 && RUNNING == 1 && FINISHED == 2 && PANICKED == 3) {
		t.Fail()
	}
}

// Test that we can exec something, wait, and it returns quickly and with an exit code.
func TestPshExecBasic(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		0,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshWaitTimeout(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("sleep", "1"),
	)
	cmdr.startCalmly()
	assert.Equal(
		false,
		cmdr.WaitSoon(20*time.Millisecond),
	)
	// the go race detector would flag this as a race.
	// and correctly so!  that's why this field is private.
	// assert.Equal(
	// 	-1,
	// 	cmdr.exitCode,
	// )
	assert.Equal(
		RUNNING,
		cmdr.State(),
	)
}

func TestPshExitListeners(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("echo"),
	)
	cmdr.startCalmly()
	var wg sync.WaitGroup
	wg.Add(1)
	cmdr.AddExitListener(func(*RunningCommand) {
		defer wg.Done()
		assert.Equal(
			0,
			cmdr.GetExitCode(),
		)
		assert.Equal(
			nil,
			cmdr.err,
		)
		assert.Equal(
			FINISHED,
			cmdr.State(),
		)
	})
	wg.Wait()
	assert.Equal(
		0,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshExitCode(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("sh", []string{"-c", "exit 22"}...),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		22,
		cmdr.GetExitCode(),
	)
	assert.Equal(
		nil,
		cmdr.err,
	)
	assert.Equal(
		FINISHED,
		cmdr.State(),
	)
}

func TestPshNonexistentCommandPanics(t *testing.T) {
	assert := assrt.NewAssert(t)

	cmdr := NewRunningCommand(
		exec.Command("/thishadbetternotbeacommand"),
	)
	cmdr.startCalmly()
	cmdr.WaitSoon(1 * time.Second)
	assert.Equal(
		-1,
		cmdr.GetExitCode(),
	)
	assert.NotEqual(
		nil,
		cmdr.err,
	)
	assert.Equal(
		PANICKED,
		cmdr.State(),
	)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Thomas Minier. All rights reserved.
// Use of this source code is governed by a MIT License
// license that can be found in the LICENSE file.

// Package joseki is a pure Go library for working with RDF, a powerful framework for representing informations as graphs.
// For more informations about RDF itself, please see https://www.w3.org/TR/rdf11-concepts
//
// Features
//
// Joseki provides the following features to work with RDF :
//
// * Structures to represent and manipulate the RDF model (URIs, Literals, Blank Nodes, Triples, etc).
//
// * RDF Graphs to store data, with several implentations provided.
//
// * A Low level API to query data stored in graphs.
//
// * A High level API to query data using the SPARQL 1.1 query language.
//
// * Query are processed using modern techniques such as join ordering or optimized query execution plans.
//
// * Load RDF data stored in files in various formats (N-Triples, Turtle, etc) into any graph.
//
// * Serialize a RDF Graph into various formats.
//
// Getting Started
//
// This package aims to work with RDF graphs, which are composed of RDF Triple {Subject Object Predicate}.
// Using joseki, you can represent an RDF Triple as followed :
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "fmt"
//  )
//  subject := rdf.NewURI("http://example.org/book/book1")
//  predicate := rdf.NewURI("http://purl.org/dc/terms/title")
//  object := rdf.NewLiteral("Harry Potter and the Order of the Phoenix")
//  triple := rdf.NewTriple(subject, predicate, object)
//  fmt.Println(triple)
//  // Output : {<http://example.org/book/book1> <http://purl.org/dc/terms/title> "Harry Potter and the Order of the Phoenix"}
//
// You can also store your RDF Triples in a RDF Graph, using various type of graphs.
// Here, we use a HDT Graph to store our triple :
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "github.com/Callidon/joseki/graph"
//  )
//  subject := rdf.NewURI("http://example.org/book/book1")
//  predicate := rdf.NewURI("http://purl.org/dc/terms/title")
//  object := rdf.NewLiteral("Harry Potter and the Order of the Phoenix")
//  graph := graph.NewHDTGraph()
//  graph.Add(rdf.NewTriple(subject, predicate, object))
//
// You can also query any triple from a RDF Graph, using a low level API or a SPARQL query.
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "github.com/Callidon/joseki/graph"
//      "fmt"
//  )
//  graph := graph.NewHDTGraph()
//  // Datas stored in a file can be easily loaded into a graph
//  graph.LoadFromFile("datas/awesome-books.ttl", "turtle")
//  // Let's fetch the authors of all the books in our graph !
//  subject := rdf.NewBlankNode("title")
//  predicate := rdf.NewURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
//  object := rdf.NewURI("https://schema.org/Book")
//  for triple := range graph.Filter(subject, predicate, object) {
//      fmt.Println(triple)
//  }
//
// For more informations about specific features, see the documentation of each subpackage.
// Author : Thomas Minier
package joseki

import "fmt"

func ComingSoon() {
    fmt.Println("More to come ;)")
}
<commit_msg>fix typo<commit_after>// Copyright (c) 2016 Thomas Minier. All rights reserved.
// Use of this source code is governed by a MIT License
// license that can be found in the LICENSE file.

// Package joseki is a pure Go library for working with RDF, a powerful framework for representing informations as graphs.
// For more informations about RDF itself, please see https://www.w3.org/TR/rdf11-concepts
//
// Features
//
// Joseki provides the following features to work with RDF :
//
// * Structures to represent and manipulate the RDF model (URIs, Literals, Blank Nodes, Triples, etc).
//
// * RDF Graphs to store data, with several implentations provided.
//
// * A Low level API to query data stored in graphs.
//
// * A High level API to query data using the SPARQL 1.1 query language.
//
// * Query are processed using modern techniques such as join ordering or optimized query execution plans.
//
// * Load RDF data stored in files in various formats (N-Triples, Turtle, etc) into any graph.
//
// * Serialize a RDF Graph into various formats.
//
// Getting Started
//
// This package aims to work with RDF graphs, which are composed of RDF Triple {Subject Object Predicate}.
// Using joseki, you can represent an RDF Triple as followed :
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "fmt"
//  )
//  subject := rdf.NewURI("http://example.org/book/book1")
//  predicate := rdf.NewURI("http://purl.org/dc/terms/title")
//  object := rdf.NewLiteral("Harry Potter and the Order of the Phoenix")
//  triple := rdf.NewTriple(subject, predicate, object)
//  fmt.Println(triple)
//  // Output : {<http://example.org/book/book1> <http://purl.org/dc/terms/title> "Harry Potter and the Order of the Phoenix"}
//
// You can also store your RDF Triples in a RDF Graph, using various type of graphs.
// Here, we use a HDT Graph to store our triple :
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "github.com/Callidon/joseki/graph"
//  )
//  subject := rdf.NewURI("http://example.org/book/book1")
//  predicate := rdf.NewURI("http://purl.org/dc/terms/title")
//  object := rdf.NewLiteral("Harry Potter and the Order of the Phoenix")
//  graph := graph.NewHDTGraph()
//  graph.Add(rdf.NewTriple(subject, predicate, object))
//
// You can also query any triple from a RDF Graph, using a low level API or a SPARQL query.
//
//  import (
//      "github.com/Callidon/joseki/rdf"
//      "github.com/Callidon/joseki/graph"
//      "fmt"
//  )
//  graph := graph.NewHDTGraph()
//  // Datas stored in a file can be easily loaded into a graph
//  graph.LoadFromFile("datas/awesome-books.ttl", "turtle")
//  // Let's fetch the authors of all the books in our graph !
//  subject := rdf.NewBlankNode("title")
//  predicate := rdf.NewURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
//  object := rdf.NewURI("https://schema.org/Book")
//  for triple := range graph.Filter(subject, predicate, object) {
//      fmt.Println(triple)
//  }
//
// For more informations about specific features, see the documentation of each subpackage.
//
// Author : Thomas Minier
package joseki

import "fmt"

func ComingSoon() {
    fmt.Println("More to come ;)")
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/utils"
	"regexp"
	"strconv"
	"strings"
)

func List(c client.Client) error {
	err := c.List()
	return err
}

func PullImage(service string) error {
	dockercli, _, _ := utils.GetNewClient()
	fmt.Println("pulling image :" + strings.Replace(strings.Split(service, ".")[0], "-", "/", 1) + ":latest")
	err := utils.PullImage(dockercli, strings.Replace(strings.Split(service, ".")[0], "-", "/", 1)+":latest")
	if err != nil {
		return err
	}
	return nil
}

func Scale(c client.Client, targets []string) error {
	for _, target := range targets {
		component, num, err := splitScaleTarget(target)
		if err != nil {
			return err
		}
		err = c.Scale(component, num)
		if err != nil {
			return err
		}
	}
	return nil
}

func Start(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Start(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Stop(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Stop(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Status(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Status(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Install(c client.Client, targets []string) error {
	// if targets, install all services
	if len(targets) == 0 {
		err := installDataContainers(c)
		if err != nil {
			return err
		}
		err = installDefaultServices(c)
		if err != nil {
			return err
		}
	} else {
		// otherwise create and start the specific targets
		for _, target := range targets {
			err := c.Create(target)
			if err != nil {
				return err
			}
			err = c.Start(target)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func installDataContainers(c client.Client) error {
	// data containers
	dataContainers := []string{
		"database-data",
		"registry-data",
		"logger-data",
		"builder-data",
	}
	fmt.Println("Scheduling data containers...")
	for _, dataContainer := range dataContainers {
		c.Create(dataContainer)
		// if err != nil {
		// 	return err
		// }
	}
	fmt.Println("Activating data containers...")
	for _, dataContainer := range dataContainers {
		c.Start(dataContainer)
		// if err != nil {
		// 	return err
		// }
	}
	return nil
}

func installDefaultServices(c client.Client) error {
	// start service containers
	targets := []string{
		"database=1",
		"cache=1",
		"logger=1",
		"registry=1",
		"controller=1",
		"builder=1",
		"router=1"}
	fmt.Println("Scheduling units...")
	err := Scale(c, targets)
	fmt.Println("Activating units...")
	err = Start(c, []string{"registry", "logger", "cache", "database"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"controller"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"builder"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"router"})
	if err != nil {
		return err
	}
	fmt.Println("Done.")
	return nil
}

func Uninstall(c client.Client, targets []string) error {
	// if targets, uninstall all services
	if len(targets) == 0 {
		err := uninstallAllServices(c)
		if err != nil {
			return err
		}

	} else {
		// uninstall the specific target
		for _, target := range targets {
			err := c.Destroy(target)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func uninstallAllServices(c client.Client) error {
	targets := []string{
		"database=0",
		"cache=0",
		"logger=0",
		"registry=0",
		"controller=0",
		"builder=0",
		"router=0"}
	err := Scale(c, targets)
	return err
}

func splitScaleTarget(target string) (c string, num int, err error) {
	r := regexp.MustCompile(`([a-z-]+)=([\d]+)`)
	match := r.FindStringSubmatch(target)
	if len(match) == 0 {
		err = fmt.Errorf("Could not parse: %v", target)
		return
	}
	c = match[1]
	num, err = strconv.Atoi(match[2])
	if err != nil {
		return
	}
	return
}
<commit_msg>fix(install): install registry after cache<commit_after>package cmd

import (
	"fmt"
	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/utils"
	"regexp"
	"strconv"
	"strings"
)

func List(c client.Client) error {
	err := c.List()
	return err
}

func PullImage(service string) error {
	dockercli, _, _ := utils.GetNewClient()
	fmt.Println("pulling image :" + strings.Replace(strings.Split(service, ".")[0], "-", "/", 1) + ":latest")
	err := utils.PullImage(dockercli, strings.Replace(strings.Split(service, ".")[0], "-", "/", 1)+":latest")
	if err != nil {
		return err
	}
	return nil
}

func Scale(c client.Client, targets []string) error {
	for _, target := range targets {
		component, num, err := splitScaleTarget(target)
		if err != nil {
			return err
		}
		err = c.Scale(component, num)
		if err != nil {
			return err
		}
	}
	return nil
}

func Start(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Start(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Stop(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Stop(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Status(c client.Client, targets []string) error {
	for _, target := range targets {
		err := c.Status(target)
		if err != nil {
			return err
		}
	}
	return nil
}

func Install(c client.Client, targets []string) error {
	// if targets, install all services
	if len(targets) == 0 {
		err := installDataContainers(c)
		if err != nil {
			return err
		}
		err = installDefaultServices(c)
		if err != nil {
			return err
		}
	} else {
		// otherwise create and start the specific targets
		for _, target := range targets {
			err := c.Create(target)
			if err != nil {
				return err
			}
			err = c.Start(target)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func installDataContainers(c client.Client) error {
	// data containers
	dataContainers := []string{
		"database-data",
		"registry-data",
		"logger-data",
		"builder-data",
	}
	fmt.Println("Scheduling data containers...")
	for _, dataContainer := range dataContainers {
		c.Create(dataContainer)
		// if err != nil {
		// 	return err
		// }
	}
	fmt.Println("Activating data containers...")
	for _, dataContainer := range dataContainers {
		c.Start(dataContainer)
		// if err != nil {
		// 	return err
		// }
	}
	return nil
}

func installDefaultServices(c client.Client) error {
	// start service containers
	targets := []string{
		"database=1",
		"cache=1",
		"logger=1",
		"registry=1",
		"controller=1",
		"builder=1",
		"router=1"}
	fmt.Println("Scheduling units...")
	err := Scale(c, targets)
	fmt.Println("Activating units...")
	err = Start(c, []string{"logger", "cache", "database"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"registry"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"controller"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"builder"})
	if err != nil {
		return err
	}
	err = Start(c, []string{"router"})
	if err != nil {
		return err
	}
	fmt.Println("Done.")
	return nil
}

func Uninstall(c client.Client, targets []string) error {
	// if targets, uninstall all services
	if len(targets) == 0 {
		err := uninstallAllServices(c)
		if err != nil {
			return err
		}

	} else {
		// uninstall the specific target
		for _, target := range targets {
			err := c.Destroy(target)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func uninstallAllServices(c client.Client) error {
	targets := []string{
		"database=0",
		"cache=0",
		"logger=0",
		"registry=0",
		"controller=0",
		"builder=0",
		"router=0"}
	err := Scale(c, targets)
	return err
}

func splitScaleTarget(target string) (c string, num int, err error) {
	r := regexp.MustCompile(`([a-z-]+)=([\d]+)`)
	match := r.FindStringSubmatch(target)
	if len(match) == 0 {
		err = fmt.Errorf("Could not parse: %v", target)
		return
	}
	c = match[1]
	num, err = strconv.Atoi(match[2])
	if err != nil {
		return
	}
	return
}
<|endoftext|>