<commit_before>package domain_migration

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/controller/worker/types"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/tlscert"
	routerc "github.com/flynn/flynn/router/client"
	"github.com/flynn/flynn/router/types"
	"github.com/flynn/que-go"
	"gopkg.in/inconshreveable/log15.v2"
)

// limits the number of concurrent requests to the router
const maxActiveRouteUpdates = 5

type context struct {
	db     *postgres.DB
	client controller.Client
	logger log15.Logger
}

type migration struct {
	db                 *postgres.DB
	client             controller.Client
	rc                 routerc.Client
	logger             log15.Logger
	dm                 *ct.DomainMigration
	activeRouteUpdates chan struct{}
	stop               chan struct{}
}

func JobHandler(db *postgres.DB, client controller.Client, logger log15.Logger) func(*que.Job) error {
	return (&context{db, client, logger}).HandleDomainMigration
}

func deployTimeout() <-chan struct{} {
	ch := make(chan struct{})
	time.AfterFunc(5*time.Minute, func() { close(ch) })
	return ch
}

func (c *context) HandleDomainMigration(job *que.Job) (err error) {
	log := c.logger.New("fn", "HandleDomainMigration")
	log.Info("handling domain migration", "job_id", job.ID, "error_count", job.ErrorCount)

	var dm *ct.DomainMigration
	if err := json.Unmarshal(job.Args, &dm); err != nil {
		log.Error("error unmarshaling job", "err", err)
		return err
	}

	log = log.New("domain_migration", dm.ID)

	m := &migration{
		db:                 c.db,
		client:             c.client,
		rc:                 routerc.New(),
		logger:             log,
		dm:                 dm,
		activeRouteUpdates: make(chan struct{}, maxActiveRouteUpdates),
		stop:               job.Stop,
	}

	if err := m.db.QueryRow("SELECT old_domain, domain, old_tls_cert, tls_cert, created_at, finished_at FROM domain_migrations WHERE migration_id = $1", dm.ID).Scan(&dm.OldDomain, &dm.Domain, &dm.OldTLSCert, &dm.TLSCert, &dm.CreatedAt, &dm.FinishedAt); err != nil {
		log.Error("error fetching postgres record", "err", err)
		m.createEvent(err)
		return err
	}

	if dm.FinishedAt != nil {
		// Already done
		return nil
	}

	return m.Run()
}

func (m *migration) Run() error {
	log := m.logger
	dm := m.dm

	// Generate TLS Cert if not already present
	var cert *tlscert.Cert
	if dm.TLSCert == nil {
		var err error
		cert, err = m.generateTLSCert()
		if err != nil {
			log.Error("error generating TLS cert", "err", err)
			m.createEvent(err)
			return err
		}
		dm.TLSCert = cert
	}

	if err := m.maybeDeployController(); err != nil {
		log.Error("error deploying controller", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.maybeDeployRouter(); err != nil {
		log.Error("error deploying router", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.maybeDeployDashboard(); err != nil {
		log.Error("error deploying dashboard", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.createMissingRoutes(); err != nil {
		log.Error("error creating missing routes", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.db.QueryRow("UPDATE domain_migrations SET finished_at = now() RETURNING finished_at").Scan(&dm.FinishedAt); err != nil {
		log.Error("error setting finished_at", "err", err)
		m.createEvent(err)
		return err
	}

	log.Info("domain migration complete")

	m.createEvent(nil)

	return nil
}

func (m *migration) generateTLSCert() (*tlscert.Cert, error) {
	hosts := []string{
		m.dm.Domain,
		fmt.Sprintf("*.%s", m.dm.Domain),
	}
	cert, err := tlscert.Generate(hosts)
	if err != nil {
		return nil, err
	}
	if _, err := m.db.Query("UPDATE domain_migrations SET tls_cert = $1 WHERE migration_id = $2", cert, m.dm.ID); err != nil {
		return nil, err
	}
	return cert, nil
}

func dupRelease(release *ct.Release) *ct.Release {
	return &ct.Release{
		ArtifactIDs: release.ArtifactIDs,
		Env:         release.Env,
		Meta:        release.Meta,
		Processes:   release.Processes,
	}
}

func (m *migration) waitForDeployment(app string) error {
	list, err := m.client.DeploymentList(app)
	if err != nil || len(list) == 0 {
		return err
	}
	d := list[0]
	if d.Status != "pending" && d.Status != "running" {
		return nil
	}

	events := make(chan *ct.Event)
	stream, err := m.client.StreamEvents(ct.StreamEventsOptions{
		AppID:       d.AppID,
		ObjectID:    d.ID,
		ObjectTypes: []ct.EventType{ct.EventTypeDeployment},
	}, events)
	if err != nil {
		return err
	}
	defer stream.Close()

	log := m.logger.New("app.name", app, "deployment", d.ID)
	log.Info("waiting for deployment")

	timeout := time.After(2 * time.Minute)
	for {
		select {
		case event := <-events:
			var data *ct.DeploymentEvent
			if err := json.Unmarshal(event.Data, &data); err != nil {
				return err
			}
			if data.Status == "complete" {
				log.Info("deployment complete")
				return nil
			}
			if data.Status == "failed" {
				log.Error("deployment failed", "error", data.Error)
				return errors.New(data.Error)
			}
		case <-timeout:
			err := errors.New("timed out waiting for deployment")
			log.Error(err.Error())
			return err
		case <-m.stop:
			return worker.ErrStopped
		}
	}
}

func (m *migration) maybeDeployController() error {
	const appName = "controller"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["DEFAULT_ROUTE_DOMAIN"] != m.dm.OldDomain {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["DEFAULT_ROUTE_DOMAIN"] = m.dm.Domain
	release.Env["CA_CERT"] = m.dm.TLSCert.CACert
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, deployTimeout()); err != nil {
		log.Error("error deploying release", "error", err)
		return err
	}
	return nil
}

func (m *migration) maybeDeployRouter() error {
	const appName = "router"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["TLSCERT"] != m.dm.OldTLSCert.Cert {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["TLSCERT"] = m.dm.TLSCert.Cert
	release.Env["TLSKEY"] = m.dm.TLSCert.PrivateKey
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, deployTimeout()); err != nil {
		log.Error("error deploying release", "error", err)
		return err
	}
	return nil
}

func (m *migration) maybeDeployDashboard() error {
	const appName = "dashboard"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["DEFAULT_ROUTE_DOMAIN"] != m.dm.OldDomain {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["CA_CERT"] = m.dm.TLSCert.CACert
	release.Env["DEFAULT_ROUTE_DOMAIN"] = m.dm.Domain
	release.Env["CONTROLLER_DOMAIN"] = fmt.Sprintf("controller.%s", m.dm.Domain)
	release.Env["URL"] = fmt.Sprintf("https://dashboard.%s", m.dm.Domain)
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, deployTimeout()); err != nil {
		log.Error("error deploying release", "error", err)
		return err
	}
	return nil
}

func (m *migration) createMissingRoutes() error {
	// Get list of all routes from router
	routes, err := m.rc.ListRoutes("")
	if err != nil {
		return err
	}

	apps, err := m.client.AppList()
	if err != nil {
		return err
	}

	// Index routes by appID
	appRoutes := make(map[string][]*router.Route, len(apps))
	for _, r := range routes {
		if !strings.HasPrefix(r.ParentRef, ct.RouteParentRefPrefix) {
			continue
		}
		appID := strings.TrimPrefix(r.ParentRef, ct.RouteParentRefPrefix)
		if appRoutes[appID] == nil {
			appRoutes[appID] = make([]*router.Route, 0, 1)
		}
		appRoutes[appID] = append(appRoutes[appID], r)
	}

	errChan := make(chan error, len(apps))
	createMissingRoutes := func(app *ct.App) {
		errChan <- m.appCreateMissingRoutes(app.ID, appRoutes[app.ID])
	}
	for _, app := range apps {
		go createMissingRoutes(app)
	}
	var returnErr error
	for range apps {
		if err := <-errChan; err != nil && returnErr == nil {
			returnErr = err
		}
	}
	return returnErr
}

func (m *migration) appCreateMissingRoutes(appID string, routes []*router.Route) error {
	if routes == nil {
		// There are no routes for this app
		return nil
	}
	m.activeRouteUpdates <- struct{}{}
	for _, route := range routes {
		if route.Type != "http" {
			continue
		}
		if strings.HasSuffix(route.Domain, m.dm.OldDomain) {
			if err := m.appMaybeCreateRoute(appID, route, routes); err != nil {
				return err
			}
		}
	}
	<-m.activeRouteUpdates
	return nil
}

func (m *migration) appMaybeCreateRoute(appID string, oldRoute *router.Route, routes []*router.Route) error {
	prefix := strings.TrimSuffix(oldRoute.Domain, m.dm.OldDomain)
	for _, route := range routes {
		if strings.HasPrefix(route.Domain, prefix) && strings.HasSuffix(route.Domain, m.dm.Domain) {
			// Route already exists
			return nil
		}
	}
	route := &router.Route{
		Type:    "http",
		Domain:  strings.Join([]string{prefix, m.dm.Domain}, ""),
		Sticky:  oldRoute.Sticky,
		Service: oldRoute.Service,
	}
	if oldRoute.Certificate != nil && oldRoute.Certificate.Cert == strings.TrimSpace(m.dm.OldTLSCert.Cert) {
		route.Certificate = &router.Certificate{
			Cert: m.dm.TLSCert.Cert,
			Key:  m.dm.TLSCert.PrivateKey,
		}
	} else {
		route.Certificate = oldRoute.Certificate
	}
	err := m.client.CreateRoute(appID, route)
	if err != nil && err.Error() == "conflict: Duplicate route" {
		return nil
	}
	return err
}

func (m *migration) createEvent(err error) error {
	e := ct.DomainMigrationEvent{DomainMigration: m.dm}
	if err != nil {
		e.Error = err.Error()
	}
	query := "INSERT INTO events (object_id, object_type, data) VALUES ($1, $2, $3)"
	return m.db.Exec(query, m.dm.ID, string(ct.EventTypeDomainMigration), e)
}
<commit_msg>controller: Handle worker shutdown when migrating domains<commit_after>package domain_migration

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/controller/worker/types"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/tlscert"
	routerc "github.com/flynn/flynn/router/client"
	"github.com/flynn/flynn/router/types"
	"github.com/flynn/que-go"
	"gopkg.in/inconshreveable/log15.v2"
)

// limits the number of concurrent requests to the router
const maxActiveRouteUpdates = 5

type context struct {
	db     *postgres.DB
	client controller.Client
	logger log15.Logger
}

type migration struct {
	db                 *postgres.DB
	client             controller.Client
	rc                 routerc.Client
	logger             log15.Logger
	dm                 *ct.DomainMigration
	activeRouteUpdates chan struct{}
	stop               chan struct{}
}

func JobHandler(db *postgres.DB, client controller.Client, logger log15.Logger) func(*que.Job) error {
	return (&context{db, client, logger}).HandleDomainMigration
}

func (m *migration) cancelDeploy() <-chan struct{} {
	ch := make(chan struct{})
	timeout := time.After(5 * time.Minute)
	go func() {
		defer close(ch)
		select {
		case <-m.stop:
		case <-timeout:
		}
	}()
	return ch
}

func (c *context) HandleDomainMigration(job *que.Job) (err error) {
	log := c.logger.New("fn", "HandleDomainMigration")
	log.Info("handling domain migration", "job_id", job.ID, "error_count", job.ErrorCount)

	var dm *ct.DomainMigration
	if err := json.Unmarshal(job.Args, &dm); err != nil {
		log.Error("error unmarshaling job", "err", err)
		return err
	}

	log = log.New("domain_migration", dm.ID)

	m := &migration{
		db:                 c.db,
		client:             c.client,
		rc:                 routerc.New(),
		logger:             log,
		dm:                 dm,
		activeRouteUpdates: make(chan struct{}, maxActiveRouteUpdates),
		stop:               job.Stop,
	}

	if err := m.db.QueryRow("SELECT old_domain, domain, old_tls_cert, tls_cert, created_at, finished_at FROM domain_migrations WHERE migration_id = $1", dm.ID).Scan(&dm.OldDomain, &dm.Domain, &dm.OldTLSCert, &dm.TLSCert, &dm.CreatedAt, &dm.FinishedAt); err != nil {
		log.Error("error fetching postgres record", "err", err)
		m.createEvent(err)
		return err
	}

	if dm.FinishedAt != nil {
		// Already done
		return nil
	}

	return m.Run()
}

func (m *migration) Run() error {
	log := m.logger
	dm := m.dm

	// Generate TLS Cert if not already present
	var cert *tlscert.Cert
	if dm.TLSCert == nil {
		var err error
		cert, err = m.generateTLSCert()
		if err != nil {
			log.Error("error generating TLS cert", "err", err)
			m.createEvent(err)
			return err
		}
		dm.TLSCert = cert
	}

	if err := m.maybeDeployController(); err != nil {
		log.Error("error deploying controller", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.maybeDeployRouter(); err != nil {
		log.Error("error deploying router", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.maybeDeployDashboard(); err != nil {
		log.Error("error deploying dashboard", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.createMissingRoutes(); err != nil {
		log.Error("error creating missing routes", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.db.QueryRow("UPDATE domain_migrations SET finished_at = now() RETURNING finished_at").Scan(&dm.FinishedAt); err != nil {
		log.Error("error setting finished_at", "err", err)
		m.createEvent(err)
		return err
	}

	log.Info("domain migration complete")

	m.createEvent(nil)

	return nil
}

func (m *migration) generateTLSCert() (*tlscert.Cert, error) {
	hosts := []string{
		m.dm.Domain,
		fmt.Sprintf("*.%s", m.dm.Domain),
	}
	cert, err := tlscert.Generate(hosts)
	if err != nil {
		return nil, err
	}
	if _, err := m.db.Query("UPDATE domain_migrations SET tls_cert = $1 WHERE migration_id = $2", cert, m.dm.ID); err != nil {
		return nil, err
	}
	return cert, nil
}

func dupRelease(release *ct.Release) *ct.Release {
	return &ct.Release{
		ArtifactIDs: release.ArtifactIDs,
		Env:         release.Env,
		Meta:        release.Meta,
		Processes:   release.Processes,
	}
}

func (m *migration) waitForDeployment(app string) error {
	list, err := m.client.DeploymentList(app)
	if err != nil || len(list) == 0 {
		return err
	}
	d := list[0]
	if d.Status != "pending" && d.Status != "running" {
		return nil
	}

	events := make(chan *ct.Event)
	stream, err := m.client.StreamEvents(ct.StreamEventsOptions{
		AppID:       d.AppID,
		ObjectID:    d.ID,
		ObjectTypes: []ct.EventType{ct.EventTypeDeployment},
	}, events)
	if err != nil {
		return err
	}
	defer stream.Close()

	log := m.logger.New("app.name", app, "deployment", d.ID)
	log.Info("waiting for deployment")

	timeout := time.After(2 * time.Minute)
	for {
		select {
		case event := <-events:
			var data *ct.DeploymentEvent
			if err := json.Unmarshal(event.Data, &data); err != nil {
				return err
			}
			if data.Status == "complete" {
				log.Info("deployment complete")
				return nil
			}
			if data.Status == "failed" {
				log.Error("deployment failed", "error", data.Error)
				return errors.New(data.Error)
			}
		case <-timeout:
			err := errors.New("timed out waiting for deployment")
			log.Error(err.Error())
			return err
		case <-m.stop:
			return worker.ErrStopped
		}
	}
}

func (m *migration) maybeDeployController() error {
	const appName = "controller"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["DEFAULT_ROUTE_DOMAIN"] != m.dm.OldDomain {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["DEFAULT_ROUTE_DOMAIN"] = m.dm.Domain
	release.Env["CA_CERT"] = m.dm.TLSCert.CACert
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, m.cancelDeploy()); err != nil {
		log.Error("error deploying release", "error", err)
		select {
		case <-m.stop:
			return worker.ErrStopped
		default:
			return err
		}
	}
	return nil
}

func (m *migration) maybeDeployRouter() error {
	const appName = "router"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["TLSCERT"] != m.dm.OldTLSCert.Cert {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["TLSCERT"] = m.dm.TLSCert.Cert
	release.Env["TLSKEY"] = m.dm.TLSCert.PrivateKey
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, m.cancelDeploy()); err != nil {
		log.Error("error deploying release", "error", err)
		select {
		case <-m.stop:
			return worker.ErrStopped
		default:
			return err
		}
	}
	return nil
}

func (m *migration) maybeDeployDashboard() error {
	const appName = "dashboard"
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	if release.Env["DEFAULT_ROUTE_DOMAIN"] != m.dm.OldDomain {
		log.Info("already migrated")
		return nil
	}
	release = dupRelease(release)
	release.Env["CA_CERT"] = m.dm.TLSCert.CACert
	release.Env["DEFAULT_ROUTE_DOMAIN"] = m.dm.Domain
	release.Env["CONTROLLER_DOMAIN"] = fmt.Sprintf("controller.%s", m.dm.Domain)
	release.Env["URL"] = fmt.Sprintf("https://dashboard.%s", m.dm.Domain)
	if err := m.client.CreateRelease(release); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(appName, release.ID, m.cancelDeploy()); err != nil {
		log.Error("error deploying release", "error", err)
		select {
		case <-m.stop:
			return worker.ErrStopped
		default:
			return err
		}
	}
	return nil
}

func (m *migration) createMissingRoutes() error {
	// Get list of all routes from router
	routes, err := m.rc.ListRoutes("")
	if err != nil {
		return err
	}

	apps, err := m.client.AppList()
	if err != nil {
		return err
	}

	// Index routes by appID
	appRoutes := make(map[string][]*router.Route, len(apps))
	for _, r := range routes {
		if !strings.HasPrefix(r.ParentRef, ct.RouteParentRefPrefix) {
			continue
		}
		appID := strings.TrimPrefix(r.ParentRef, ct.RouteParentRefPrefix)
		if appRoutes[appID] == nil {
			appRoutes[appID] = make([]*router.Route, 0, 1)
		}
		appRoutes[appID] = append(appRoutes[appID], r)
	}

	errChan := make(chan error, len(apps))
	createMissingRoutes := func(app *ct.App) {
		errChan <- m.appCreateMissingRoutes(app.ID, appRoutes[app.ID])
	}
	for _, app := range apps {
		go createMissingRoutes(app)
	}
	var returnErr error
	for range apps {
		if err := <-errChan; err != nil && returnErr == nil {
			returnErr = err
		}
	}
	return returnErr
}

func (m *migration) appCreateMissingRoutes(appID string, routes []*router.Route) error {
	if routes == nil {
		// There are no routes for this app
		return nil
	}
	m.activeRouteUpdates <- struct{}{}
	for _, route := range routes {
		if route.Type != "http" {
			continue
		}
		if strings.HasSuffix(route.Domain, m.dm.OldDomain) {
			if err := m.appMaybeCreateRoute(appID, route, routes); err != nil {
				return err
			}
		}
	}
	<-m.activeRouteUpdates
	return nil
}

func (m *migration) appMaybeCreateRoute(appID string, oldRoute *router.Route, routes []*router.Route) error {
	prefix := strings.TrimSuffix(oldRoute.Domain, m.dm.OldDomain)
	for _, route := range routes {
		if strings.HasPrefix(route.Domain, prefix) && strings.HasSuffix(route.Domain, m.dm.Domain) {
			// Route already exists
			return nil
		}
	}
	route := &router.Route{
		Type:    "http",
		Domain:  strings.Join([]string{prefix, m.dm.Domain}, ""),
		Sticky:  oldRoute.Sticky,
		Service: oldRoute.Service,
	}
	if oldRoute.Certificate != nil && oldRoute.Certificate.Cert == strings.TrimSpace(m.dm.OldTLSCert.Cert) {
		route.Certificate = &router.Certificate{
			Cert: m.dm.TLSCert.Cert,
			Key:  m.dm.TLSCert.PrivateKey,
		}
	} else {
		route.Certificate = oldRoute.Certificate
	}
	err := m.client.CreateRoute(appID, route)
	if err != nil && err.Error() == "conflict: Duplicate route" {
		return nil
	}
	return err
}

func (m *migration) createEvent(err error) error {
	if err == worker.ErrStopped {
		return nil
	}
	e := ct.DomainMigrationEvent{DomainMigration: m.dm}
	if err != nil {
		e.Error = err.Error()
	}
	query := "INSERT INTO events (object_id, object_type, data) VALUES ($1, $2, $3)"
	return m.db.Exec(query, m.dm.ID, string(ct.EventTypeDomainMigration), e)
}
<|endoftext|>


<commit_before>package wrap

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/Songmu/wrapcommander"
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mkr/logger"
	"golang.org/x/sync/errgroup"
	cli "gopkg.in/urfave/cli.v1"
)

// CommandPlugin is definition of mkr wrap
var Command = cli.Command{
	Name:      "wrap",
	Usage:     "wrap command status",
	ArgsUsage: "[--verbose | -v] [--name | -n <name>] [--memo | -m <memo>] -- /path/to/batch",
	Description: `
    wrap command line
`,
	Action: doWrap,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "name, n", Value: "", Usage: "monitor <name>"},
		cli.BoolFlag{Name: "verbose, v", Usage: "verbose output mode"},
		cli.StringFlag{Name: "memo, m", Value: "", Usage: "monitor <memo>"},
		cli.StringFlag{Name: "H, host", Value: "", Usage: "<hostId>"},
		cli.BoolFlag{Name: "warning, w", Usage: "alert as warning"},
	},
}

func doWrap(c *cli.Context) error {
	confFile := c.GlobalString("conf")
	conf, err := config.LoadConfig(confFile)
	if err != nil {
		return err
	}
	apibase := c.GlobalString("apibase")
	apikey := conf.Apikey
	if apikey == "" {
		apikey = os.Getenv("MACKEREL_APIKEY")
	}
	if apikey == "" {
		logger.Log("error", "[mkr wrap] failed to detect Mackerel APIKey. Try to specify in mackerel-agent.conf or export MACKEREL_APIKEY='<Your apikey>'")
	}
	hostID, _ := conf.LoadHostID()
	if c.String("host") != "" {
		hostID = c.String("host")
	}
	if hostID == "" {
		logger.Log("error", "[mkr wrap] failed to load hostID. Try to specify -host option explicitly")
	}
	// Since command execution has the highest priority, even when apikey or
	// hostID is empty, we don't return errors and only output the log here.

	cmd := c.Args()
	if len(cmd) > 0 && cmd[0] == "--" {
		cmd = cmd[1:]
	}
	if len(cmd) < 1 {
		return fmt.Errorf("no commands specified")
	}

	return (&app{
		apibase: apibase,
		name:    c.String("name"),
		verbose: c.Bool("verbose"),
		memo:    c.String("memo"),
		warning: c.Bool("warning"),
		hostID:  hostID,
		apikey:  apikey,
		cmd:     cmd,
	}).run()
}

type app struct {
	apibase string
	name    string
	verbose bool
	memo    string
	warning bool
	hostID  string
	apikey  string
	cmd     []string
}

type result struct {
	Cmd        []string
	Name, Memo string

	Output, Stdout, Stderr string `json:"-"`
	Pid                    int
	ExitCode               *int
	Signaled               bool
	StartAt, EndAt         time.Time

	Msg     string
	Success bool
}

var reg = regexp.MustCompile(`[^-a-zA-Z0-9_]`)

func normalizeName(devName string) string {
	return reg.ReplaceAllString(strings.TrimSpace(devName), "_")
}

func (re *result) checkName() string {
	if re.Name != "" {
		return re.Name
	}
	sum := md5.Sum([]byte(strings.Join(re.Cmd, " ")))
	return fmt.Sprintf("mkrwrap-%s-%x",
		normalizeName(filepath.Base(re.Cmd[0])),
		sum[0:3])
}

func (re *result) resultFile() string {
	return filepath.Join(os.TempDir(), fmt.Sprintf("mkrwrap-%s.json", re.checkName()))
}

func (re *result) loadLastResult() (*result, error) {
	prevRe := &result{}
	fname := re.resultFile()

	f, err := os.Open(fname)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(prevRe)
	return prevRe, err
}

func (ap *app) run() error {
	re := ap.runCmd()
	return ap.report(re)
}

func (ap *app) runCmd() *result {
	cmd := exec.Command(ap.cmd[0], ap.cmd[1:]...)
	re := &result{
		Cmd:  ap.cmd,
		Name: ap.name,
		Memo: ap.memo,
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	defer stdoutPipe.Close()

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	defer stderrPipe.Close()

	var (
		bufStdout = &bytes.Buffer{}
		bufStderr = &bytes.Buffer{}
		bufMerged = &bytes.Buffer{}
	)
	stdoutPipe2 := io.TeeReader(stdoutPipe, io.MultiWriter(bufStdout, bufMerged))
	stderrPipe2 := io.TeeReader(stderrPipe, io.MultiWriter(bufStderr, bufMerged))

	re.StartAt = time.Now()
	err = cmd.Start()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	re.Pid = cmd.Process.Pid
	eg := &errgroup.Group{}

	eg.Go(func() error {
		defer stdoutPipe.Close()
		_, err := io.Copy(os.Stdout, stdoutPipe2)
		return err
	})
	eg.Go(func() error {
		defer stderrPipe.Close()
		_, err := io.Copy(os.Stderr, stderrPipe2)
		return err
	})
	eg.Wait()

	cmdErr := cmd.Wait()
	re.EndAt = time.Now()
	ex := wrapcommander.ResolveExitCode(cmdErr)
	re.ExitCode = &ex
	if *re.ExitCode > 128 {
		w, ok := wrapcommander.ErrorToWaitStatus(cmdErr)
		if ok {
			re.Signaled = w.Signaled()
		}
	}
	if !re.Signaled {
		re.Msg = fmt.Sprintf("command exited with code: %d", *re.ExitCode)
	} else {
		re.Msg = fmt.Sprintf("command died with signal: %d", *re.ExitCode&127)
	}
	re.Stdout = bufStdout.String()
	re.Stderr = bufStderr.String()
	re.Output = bufMerged.String()

	re.Success = *re.ExitCode == 0
	return re
}

func (ap *app) report(re *result) error {
	fmt.Println(re.checkName())
	return nil
}
<commit_msg>adjustment<commit_after>package wrap

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/Songmu/wrapcommander"
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mkr/logger"
	"golang.org/x/sync/errgroup"
	cli "gopkg.in/urfave/cli.v1"
)

// CommandPlugin is definition of mkr wrap
var Command = cli.Command{
	Name:      "wrap",
	Usage:     "wrap command status",
	ArgsUsage: "[--verbose | -v] [--name | -n <name>] [--memo | -m <memo>] -- /path/to/batch",
	Description: `
    wrap command line
`,
	Action: doWrap,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "name, n", Value: "", Usage: "monitor <name>"},
		cli.BoolFlag{Name: "verbose, v", Usage: "verbose output mode"},
		cli.StringFlag{Name: "memo, m", Value: "", Usage: "monitor <memo>"},
		cli.StringFlag{Name: "H, host", Value: "", Usage: "<hostId>"},
		cli.BoolFlag{Name: "warning, w", Usage: "alert as warning"},
	},
}

func doWrap(c *cli.Context) error {
	confFile := c.GlobalString("conf")
	conf, err := config.LoadConfig(confFile)
	if err != nil {
		return err
	}
	apibase := c.GlobalString("apibase")
	if apibase == "" {
		apibase = conf.Apibase
	}

	apikey := conf.Apikey
	if apikey == "" {
		apikey = os.Getenv("MACKEREL_APIKEY")
	}
	if apikey == "" {
		logger.Log("error", "[mkr wrap] failed to detect Mackerel APIKey. Try to specify in mackerel-agent.conf or export MACKEREL_APIKEY='<Your apikey>'")
	}
	hostID, _ := conf.LoadHostID()
	if c.String("host") != "" {
		hostID = c.String("host")
	}
	if hostID == "" {
		logger.Log("error", "[mkr wrap] failed to load hostID. Try to specify -host option explicitly")
	}
	// Since command execution has the highest priority, even when apikey or
	// hostID is empty, we don't return errors and only output the log here.

	cmd := c.Args()
	if len(cmd) > 0 && cmd[0] == "--" {
		cmd = cmd[1:]
	}
	if len(cmd) < 1 {
		return fmt.Errorf("no commands specified")
	}

	return (&app{
		apibase: apibase,
		name:    c.String("name"),
		verbose: c.Bool("verbose"),
		memo:    c.String("memo"),
		warning: c.Bool("warning"),
		hostID:  hostID,
		apikey:  apikey,
		cmd:     cmd,
	}).run()
}

type app struct {
	apibase string
	name    string
	verbose bool
	memo    string
	warning bool
	hostID  string
	apikey  string
	cmd     []string
}

type result struct {
	Cmd        []string
	Name, Memo string

	Output, Stdout, Stderr string `json:"-"`
	Pid                    int
	ExitCode               *int
	Signaled               bool
	StartAt, EndAt         time.Time

	Msg     string
	Success bool
}

var reg = regexp.MustCompile(`[^-a-zA-Z0-9_]`)

func normalizeName(devName string) string {
	return reg.ReplaceAllString(strings.TrimSpace(devName), "_")
}

func (re *result) checkName() string {
	if re.Name != "" {
		return re.Name
	}
	sum := md5.Sum([]byte(strings.Join(re.Cmd, " ")))
	return fmt.Sprintf("mkrwrap-%s-%x",
		normalizeName(filepath.Base(re.Cmd[0])),
		sum[0:3])
}

func (re *result) resultFile() string {
	return filepath.Join(os.TempDir(), fmt.Sprintf("mkrwrap-%s.json", re.checkName()))
}

func (re *result) loadLastResult() (*result, error) {
	prevRe := &result{}
	fname := re.resultFile()

	f, err := os.Open(fname)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(prevRe)
	return prevRe, err
}

func (ap *app) run() error {
	re := ap.runCmd()
	return ap.report(re)
}

func (ap *app) runCmd() *result {
	cmd := exec.Command(ap.cmd[0], ap.cmd[1:]...)
	re := &result{
		Cmd:  ap.cmd,
		Name: ap.name,
		Memo: ap.memo,
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	defer stdoutPipe.Close()

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	defer stderrPipe.Close()

	var (
		bufStdout = &bytes.Buffer{}
		bufStderr = &bytes.Buffer{}
		bufMerged = &bytes.Buffer{}
	)
	stdoutPipe2 := io.TeeReader(stdoutPipe, io.MultiWriter(bufStdout, bufMerged))
	stderrPipe2 := io.TeeReader(stderrPipe, io.MultiWriter(bufStderr, bufMerged))

	re.StartAt = time.Now()
	err = cmd.Start()
	if err != nil {
		re.Msg = fmt.Sprintf("command invocation failed with follwing error: %s", err)
		return re
	}
	re.Pid = cmd.Process.Pid
	eg := &errgroup.Group{}

	eg.Go(func() error {
		defer stdoutPipe.Close()
		_, err := io.Copy(os.Stdout, stdoutPipe2)
		return err
	})
	eg.Go(func() error {
		defer stderrPipe.Close()
		_, err := io.Copy(os.Stderr, stderrPipe2)
		return err
	})
	eg.Wait()

	cmdErr := cmd.Wait()
	re.EndAt = time.Now()
	ex := wrapcommander.ResolveExitCode(cmdErr)
	re.ExitCode = &ex
	if *re.ExitCode > 128 {
		w, ok := wrapcommander.ErrorToWaitStatus(cmdErr)
		if ok {
			re.Signaled = w.Signaled()
		}
	}
	if !re.Signaled {
		re.Msg = fmt.Sprintf("command exited with code: %d", *re.ExitCode)
	} else {
		re.Msg = fmt.Sprintf("command died with signal: %d", *re.ExitCode&127)
	}
	re.Stdout = bufStdout.String()
	re.Stderr = bufStderr.String()
	re.Output = bufMerged.String()

	re.Success = *re.ExitCode == 0
	return re
}

func (ap *app) report(re *result) error {
	if ap.apikey == "" || ap.hostID == "" {
		return fmt.Errorf("Both apikey and hostID are needed to report result to Mackerel")
	}

	fmt.Println(re.checkName())
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The etcd 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 integration

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestWatchFragmentDisable ensures that large watch
// response exceeding server-side request limit can
// arrive even without watch response fragmentation.
func TestWatchFragmentDisable(t *testing.T) {
	testWatchFragment(t, false, false)
}

// TestWatchFragmentDisableWithGRPCLimit verifies
// large watch response exceeding server-side request
// limit and client-side gRPC response receive limit
// cannot arrive without watch events fragmentation,
// because multiple events exceed client-side gRPC
// response receive limit.
func TestWatchFragmentDisableWithGRPCLimit(t *testing.T) {
	testWatchFragment(t, false, true)
}

// TestWatchFragmentEnable ensures that large watch
// response exceeding server-side request limit arrive
// with watch response fragmentation.
func TestWatchFragmentEnable(t *testing.T) {
	testWatchFragment(t, true, false)
}

// TestWatchFragmentEnableWithGRPCLimit verifies
// large watch response exceeding server-side request
// limit and client-side gRPC response receive limit
// can arrive only when watch events are fragmented.
func TestWatchFragmentEnableWithGRPCLimit(t *testing.T) {
	testWatchFragment(t, true, true)
}

// testWatchFragment triggers watch response that spans over multiple
// revisions exceeding server request limits when combined.
func testWatchFragment(t *testing.T, fragment, exceedRecvLimit bool) {
	cfg := &integration.ClusterConfig{
		Size:            1,
		MaxRequestBytes: 1.5 * 1024 * 1024,
	}
	if exceedRecvLimit {
		cfg.ClientMaxCallRecvMsgSize = 1.5 * 1024 * 1024
	}
	clus := integration.NewClusterV3(t, cfg)
	defer clus.Terminate(t)

	cli := clus.Client(0)
	errc := make(chan error)
	for i := 0; i < 10; i++ {
		go func(i int) {
			_, err := cli.Put(context.TODO(),
				fmt.Sprint("foo", i),
				strings.Repeat("a", 1024*1024),
			)
			errc <- err
		}(i)
	}
	for i := 0; i < 10; i++ {
		if err := <-errc; err != nil {
			t.Fatalf("failed to put: %v", err)
		}
	}

	opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(1)}
	if fragment {
		opts = append(opts, clientv3.WithFragment())
	}
	wch := cli.Watch(context.TODO(), "foo", opts...)

	// expect 10 MiB watch response
	select {
	case ws := <-wch:
		// without fragment, should exceed gRPC client receive limit
		if !fragment && exceedRecvLimit {
			if len(ws.Events) != 0 {
				t.Fatalf("expected 0 events with watch fragmentation, got %d", len(ws.Events))
			}
			exp := "code = ResourceExhausted desc = grpc: received message larger than max ("
			if !strings.Contains(ws.Err().Error(), exp) {
				t.Fatalf("expected 'ResourceExhausted' error, got %v", ws.Err())
			}
			return
		}

		// still expect merged watch events
		if len(ws.Events) != 10 {
			t.Fatalf("expected 10 events with watch fragmentation, got %d", len(ws.Events))
		}
		if ws.Err() != nil {
			t.Fatalf("unexpected error %v", ws.Err())
		}

	case <-time.After(testutil.RequestTimeout):
		t.Fatalf("took too long to receive events")
	}
}
<commit_msg>clientv3/integration: do not run fragmentation tests with gRPC Proxy<commit_after>// Copyright 2018 The etcd 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.

// +build !cluster_proxy

package integration

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestWatchFragmentDisable ensures that large watch
// response exceeding server-side request limit can
// arrive even without watch response fragmentation.
func TestWatchFragmentDisable(t *testing.T) {
	testWatchFragment(t, false, false)
}

// TestWatchFragmentDisableWithGRPCLimit verifies
// large watch response exceeding server-side request
// limit and client-side gRPC response receive limit
// cannot arrive without watch events fragmentation,
// because multiple events exceed client-side gRPC
// response receive limit.
func TestWatchFragmentDisableWithGRPCLimit(t *testing.T) {
	testWatchFragment(t, false, true)
}

// TestWatchFragmentEnable ensures that large watch
// response exceeding server-side request limit arrive
// with watch response fragmentation.
func TestWatchFragmentEnable(t *testing.T) {
	testWatchFragment(t, true, false)
}

// TestWatchFragmentEnableWithGRPCLimit verifies
// large watch response exceeding server-side request
// limit and client-side gRPC response receive limit
// can arrive only when watch events are fragmented.
func TestWatchFragmentEnableWithGRPCLimit(t *testing.T) {
	testWatchFragment(t, true, true)
}

// testWatchFragment triggers watch response that spans over multiple
// revisions exceeding server request limits when combined.
func testWatchFragment(t *testing.T, fragment, exceedRecvLimit bool) {
	cfg := &integration.ClusterConfig{
		Size:            1,
		MaxRequestBytes: 1.5 * 1024 * 1024,
	}
	if exceedRecvLimit {
		cfg.ClientMaxCallRecvMsgSize = 1.5 * 1024 * 1024
	}
	clus := integration.NewClusterV3(t, cfg)
	defer clus.Terminate(t)

	cli := clus.Client(0)
	errc := make(chan error)
	for i := 0; i < 10; i++ {
		go func(i int) {
			_, err := cli.Put(context.TODO(),
				fmt.Sprint("foo", i),
				strings.Repeat("a", 1024*1024),
			)
			errc <- err
		}(i)
	}
	for i := 0; i < 10; i++ {
		if err := <-errc; err != nil {
			t.Fatalf("failed to put: %v", err)
		}
	}

	opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(1)}
	if fragment {
		opts = append(opts, clientv3.WithFragment())
	}
	wch := cli.Watch(context.TODO(), "foo", opts...)

	// expect 10 MiB watch response
	select {
	case ws := <-wch:
		// without fragment, should exceed gRPC client receive limit
		if !fragment && exceedRecvLimit {
			if len(ws.Events) != 0 {
				t.Fatalf("expected 0 events with watch fragmentation, got %d", len(ws.Events))
			}
			exp := "code = ResourceExhausted desc = grpc: received message larger than max ("
			if !strings.Contains(ws.Err().Error(), exp) {
				t.Fatalf("expected 'ResourceExhausted' error, got %v", ws.Err())
			}
			return
		}

		// still expect merged watch events
		if len(ws.Events) != 10 {
			t.Fatalf("expected 10 events with watch fragmentation, got %d", len(ws.Events))
		}
		if ws.Err() != nil {
			t.Fatalf("unexpected error %v", ws.Err())
		}

	case <-time.After(testutil.RequestTimeout):
		t.Fatalf("took too long to receive events")
	}
}
<|endoftext|>


<commit_before>package metadata

import (
	"strings"

	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstComputer []Win32_ComputerSystem
	q = wmi.CreateQuery(&dstComputer, "")
	err = wmi.Query(q, &dstComputer)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstBIOS []Win32_BIOS
	q = wmi.CreateQuery(&dstBIOS, "")
	err = wmi.Query(q, &dstBIOS)
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}

	for _, v := range dstComputer {
		AddMeta("", nil, "manufacturer", v.Manufacturer, true)
		AddMeta("", nil, "model", v.Model, true)
		AddMeta("", nil, "memoryTotal", v.TotalPhysicalMemory, true)
	}

	for _, v := range dstBIOS {
		AddMeta("", nil, "serialNumber", v.SerialNumber, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

type Win32_ComputerSystem struct {
	Manufacturer        string
	Model               string
	TotalPhysicalMemory uint64
}

type Win32_BIOS struct {
	SerialNumber string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "WHERE MACAddress != null")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": v.InterfaceName} //Should be v.Name. See https://github.com/bosun-monitor/scollector/issues/119
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		AddMeta("", tag, "speed", v.Speed, true)

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", strings.Replace(nicConfig.MACAddress, ":", "", -1), true)
			for _, ip := range *nicConfig.IPAddress {
				AddMeta("", tag, "addr", ip, true) // blocked by array support in WMI See https://github.com/StackExchange/wmi/issues/5
			}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string //NY-WEB09-PRI-NIC-A
	Speed                uint64 //Bits per Second
	InterfaceDescription string //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string //Ethernet_10
	InterfaceGuid        string //unique id
}

type Win32_NetworkAdapterConfiguration struct {
	IPAddress  *[]string //Both IPv4 and IPv6
	MACAddress string    //00:1B:21:93:00:00
	SettingID  string    //Matches InterfaceGuid
}
<commit_msg>cmd/scollector: Use iface=Interface1 format for tag<commit_after>package metadata

import (
	"fmt"
	"strings"

	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstComputer []Win32_ComputerSystem
	q = wmi.CreateQuery(&dstComputer, "")
	err = wmi.Query(q, &dstComputer)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstBIOS []Win32_BIOS
	q = wmi.CreateQuery(&dstBIOS, "")
	err = wmi.Query(q, &dstBIOS)
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}

	for _, v := range dstComputer {
		AddMeta("", nil, "manufacturer", v.Manufacturer, true)
		AddMeta("", nil, "model", v.Model, true)
		AddMeta("", nil, "memoryTotal", v.TotalPhysicalMemory, true)
	}

	for _, v := range dstBIOS {
		AddMeta("", nil, "serialNumber", v.SerialNumber, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

type Win32_ComputerSystem struct {
	Manufacturer        string
	Model               string
	TotalPhysicalMemory uint64
}

type Win32_BIOS struct {
	SerialNumber string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "WHERE MACAddress != null")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": fmt.Sprint("Interface", v.InterfaceIndex)}
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		AddMeta("", tag, "speed", v.Speed, true)

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", strings.Replace(nicConfig.MACAddress, ":", "", -1), true)
			for _, ip := range *nicConfig.IPAddress {
				AddMeta("", tag, "addr", ip, true) // blocked by array support in WMI See https://github.com/StackExchange/wmi/issues/5
			}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string //NY-WEB09-PRI-NIC-A
	Speed                uint64 //Bits per Second
	InterfaceDescription string //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string //Ethernet_10
	InterfaceGuid        string //unique id
	InterfaceIndex       uint32
}

type Win32_NetworkAdapterConfiguration struct {
	IPAddress  *[]string //Both IPv4 and IPv6
	MACAddress string    //00:1B:21:93:00:00
	SettingID  string    //Matches InterfaceGuid
}
<|endoftext|>


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

package pde

import (
	"sort"

	"github.com/cpmech/gosl/io"
)

// Equations organises the identification numbers (IDs) of equations in a linear system: K⋅u=f
// Some of the components of the u vector may be known in advance——i.e. some u values are
// prescribed——therefore the system of equations needs to be reduced by removing the
// "known equations". The resulting equations contain the variables that needed to be solved for
// and are named here "unknown equations". The structure Equations will then hold the IDs
// of two sets of equations:
// 1 -- "unknown" indicated by the number "1"; and
// 2 -- "prescribed" indicated by the number "2"
//
//  In summary:
//
//   * We call reduced system # 1 a system of equations with the unknown equations only;
//             ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄                                ┄┄┄┄┄┄┄
//     i.e. the "effective" system that needs to be solved: K11⋅u1 = f1
//
//   * We call reduced system # 2 a system of equations with the prescribed equations only
//             ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄                                ┄┄┄┄┄┄┄┄┄┄
//
//   * We call the system K⋅u=f the "full" system, with all equations
//     (unknown and prescribed)
//
//  The "full" linear system is:
//                                  K ⋅ u = f     or    [ K11 K12 ] ⋅ { u1 } = { f1 }
//                                                      [ K21 K22 ]   { u2 }   { f2 }
//  We then split the K matrix into four parts:
//
//                                K11 K12  1─────> unknown
//                                K21 K22  2─────> prescribed
//                                  1   2
//                                  │   └────────> prescribed
//                                  └────────────> unknown
//
//  The Equations structure uses arrays to map the indices of equations from the "full"
//  system to the reduced systems and vice-versa. We call these arrays "maps" but they are
//  not Go maps; they are simple slices of integers and should give better performance than
//  using maps.
//
//  Two "maps" (slices of integers) are built:
//   * "RF" means "reduced to full"
//   * "FR" means "full to reduced"
//
//
//  EXAMPLE:
//
//   N   = 9          ⇒  total number of equations
//   peq = [0  3  6]  ⇒  prescribed equations
//
//     0  1  2  3  4  5  6  7  8                       0   1  2   3   4  5   6   7  8
//   0 +--------+--------+------ 0 -- prescribed -- 0 [22] 21 21 [22] 21 21 [22] 21 21 0
//   1 |        |        |       1                  1  12  .. ..  12  .. ..  12  .. .. 1
//   2 |        |        |       2                  2  12  .. ..  12  .. ..  12  .. .. 2
//   3 +--------+--------+------ 3 -- prescribed -- 3 [22] 21 21 [22] 21 21 [22] 21 21 3
//   4 |        |        |       4                  4  12  .. ..  12  .. ..  12  .. .. 4
//   5 |        |        |       5                  5  12  .. ..  12  .. ..  12  .. .. 5
//   6 +--------+--------+------ 6 -- prescribed -- 6 [22] 21 21 [22] 21 21 [22] 21 21 6
//   7 |        |        |       7                  7  12  .. ..  12  .. ..  12  .. .. 7
//   8 |        |        |       8                  8  12  .. ..  12  .. ..  12  .. .. 8
//     0  1  2  3  4  5  6  7  8                       0   1  2   3   4  5   6   7  8
//     |        |        |
//    pre      pre      pre                                 .. ⇒ 11 equations
//
//   N1 = 6 => number of equations of type 1 (unknowns)
//   N2 = 3 => number of equations of type 2 (prescribed)
//   N1 + N2 == N
//
//          0  1  2  3  4  5              Example:
//   RF1 = [1  2  4  5  7  8]           ⇒ RF1[3] returns equation # 5 of full system
//
//          0  1  2  3  4  5  6  7  8
//   FR1 = [   0  1     2  3     4  5]  ⇒ FR1[5] returns equation # 3 of reduced system
//         -1       -1       -1         ⇒ -1 indicates 'value not set'
//
//          0  1  2
//   RF2 = [0  3  6]                    ⇒ RF2[1] returns equation # 3 of full system
//
//          0  1  2  3  4  5  6  7  8
//   FR2 = [0        1        2      ]  ⇒ FR2[3] returns equation # 1 of reduced system
//            -1 -1    -1 -1    -1 -1   ⇒ -1 indicates 'value not set'
//
type Equations struct {
	N1, N2, N int   // unknowns, prescribed, total numbers
	RF1, FR1  []int // reduced=>full/full=>reduced maps for unknowns
	RF2, FR2  []int // reduced=>full/full=>reduced maps for prescribed
}

// Init initialises Equations
func (e *Equations) Init(n int, peqUsorted []int) {
	peq := make([]int, len(peqUsorted))
	copy(peq, peqUsorted)
	sort.Ints(peq)
	e.N = n
	e.N2 = len(peq)
	e.N1 = e.N - e.N2
	e.RF1 = make([]int, e.N1)
	e.FR1 = make([]int, e.N)
	e.RF2 = make([]int, e.N2)
	e.FR2 = make([]int, e.N)
	var i1, i2 int
	for eq := 0; eq < n; eq++ {
		e.FR1[eq] = -1 // indicates 'not set'
		e.FR2[eq] = -1
		idx := sort.SearchInts(peq, eq)
		if idx < len(peq) && peq[idx] == eq { // found => prescribed
			e.RF2[i2] = eq
			e.FR2[eq] = i2
			i2++
		} else { // not found => unknowns
			e.RF1[i1] = eq
			e.FR1[eq] = i1
			i1++
		}
	}
}

// Print prints information about Equations
func (e *Equations) Print() {
	io.Pf("N1 = %v, N2 = %v, N = %v\n", e.N1, e.N2, e.N)
	io.Pf("RF1 (unknown) =\n %v\n", e.RF1)
	io.Pf("FR1 = \n%v\n", e.FR1)
	io.Pf("RF2 (prescribed) =\n %v\n", e.RF2)
	io.Pf("FR2 = \n%v\n", e.FR2)
}
<commit_msg>Format comment text to fit godoc page<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pde

import (
	"sort"

	"github.com/cpmech/gosl/io"
)

// Equations organises the identification numbers (IDs) of equations in a linear system: K⋅u=f
// Some of the components of the u vector may be known in advance——i.e. some u values are
// prescribed——therefore the system of equations needs to be reduced by removing the
// "known equations". The resulting equations contain the variables that needed to be solved for
// and are named here "unknown equations". The structure Equations will then hold the IDs
// of two sets of equations:
// 1 -- "unknown" indicated by the number "1"; and
// 2 -- "prescribed" indicated by the number "2"
//
//  In summary:
//
//   * We call reduced system # 1 a system of equations with the unknown equations only;
//             ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄                                ┄┄┄┄┄┄┄
//     i.e. the "effective" system that needs to be solved: K11⋅u1 = f1
//
//   * We call reduced system # 2 a system of equations with the prescribed equations only
//             ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄                                ┄┄┄┄┄┄┄┄┄┄
//
//   * We call the system K⋅u=f the "full" system, with all equations
//     (unknown and prescribed)
//
//  The "full" linear system is:
//                                  K ⋅ u = f     or    [ K11 K12 ] ⋅ { u1 } = { f1 }
//                                                      [ K21 K22 ]   { u2 }   { f2 }
//  We then split the K matrix into four parts:
//
//                                K11 K12  1─────> unknown
//                                K21 K22  2─────> prescribed
//                                  1   2
//                                  │   └────────> prescribed
//                                  └────────────> unknown
//
//  The Equations structure uses arrays to map the indices of equations from the "full"
//  system to the reduced systems and vice-versa. We call these arrays "maps" but they
//  are not Go maps; they are simple slices of integers and should give better
//  performance than using maps.
//
//  Two "maps" (slices of integers) are built:
//   * "RF" means "reduced to full"
//   * "FR" means "full to reduced"
//
//
//  EXAMPLE:
//
//   N   = 9          ⇒  total number of equations
//   peq = [0  3  6]  ⇒  prescribed equations
//
//     0  1  2  3  4  5  6  7  8                       0   1  2   3   4  5   6   7  8
//   0 +--------+--------+------ 0 -- prescribed -- 0 [22] 21 21 [22] 21 21 [22] 21 21 0
//   1 |        |        |       1                  1  12  .. ..  12  .. ..  12  .. .. 1
//   2 |        |        |       2                  2  12  .. ..  12  .. ..  12  .. .. 2
//   3 +--------+--------+------ 3 -- prescribed -- 3 [22] 21 21 [22] 21 21 [22] 21 21 3
//   4 |        |        |       4                  4  12  .. ..  12  .. ..  12  .. .. 4
//   5 |        |        |       5                  5  12  .. ..  12  .. ..  12  .. .. 5
//   6 +--------+--------+------ 6 -- prescribed -- 6 [22] 21 21 [22] 21 21 [22] 21 21 6
//   7 |        |        |       7                  7  12  .. ..  12  .. ..  12  .. .. 7
//   8 |        |        |       8                  8  12  .. ..  12  .. ..  12  .. .. 8
//     0  1  2  3  4  5  6  7  8                       0   1  2   3   4  5   6   7  8
//     |        |        |
//    pre      pre      pre                                 .. ⇒ 11 equations
//
//   N1 = 6 => number of equations of type 1 (unknowns)
//   N2 = 3 => number of equations of type 2 (prescribed)
//   N1 + N2 == N
//
//          0  1  2  3  4  5              Example:
//   RF1 = [1  2  4  5  7  8]           ⇒ RF1[3] returns equation # 5 of full system
//
//          0  1  2  3  4  5  6  7  8
//   FR1 = [   0  1     2  3     4  5]  ⇒ FR1[5] returns equation # 3 of reduced system
//         -1       -1       -1         ⇒ -1 indicates 'value not set'
//
//          0  1  2
//   RF2 = [0  3  6]                    ⇒ RF2[1] returns equation # 3 of full system
//
//          0  1  2  3  4  5  6  7  8
//   FR2 = [0        1        2      ]  ⇒ FR2[3] returns equation # 1 of reduced system
//            -1 -1    -1 -1    -1 -1   ⇒ -1 indicates 'value not set'
//
type Equations struct {
	N1, N2, N int   // unknowns, prescribed, total numbers
	RF1, FR1  []int // reduced=>full/full=>reduced maps for unknowns
	RF2, FR2  []int // reduced=>full/full=>reduced maps for prescribed
}

// Init initialises Equations
func (e *Equations) Init(n int, peqUsorted []int) {
	peq := make([]int, len(peqUsorted))
	copy(peq, peqUsorted)
	sort.Ints(peq)
	e.N = n
	e.N2 = len(peq)
	e.N1 = e.N - e.N2
	e.RF1 = make([]int, e.N1)
	e.FR1 = make([]int, e.N)
	e.RF2 = make([]int, e.N2)
	e.FR2 = make([]int, e.N)
	var i1, i2 int
	for eq := 0; eq < n; eq++ {
		e.FR1[eq] = -1 // indicates 'not set'
		e.FR2[eq] = -1
		idx := sort.SearchInts(peq, eq)
		if idx < len(peq) && peq[idx] == eq { // found => prescribed
			e.RF2[i2] = eq
			e.FR2[eq] = i2
			i2++
		} else { // not found => unknowns
			e.RF1[i1] = eq
			e.FR1[eq] = i1
			i1++
		}
	}
}

// Print prints information about Equations
func (e *Equations) Print() {
	io.Pf("N1 = %v, N2 = %v, N = %v\n", e.N1, e.N2, e.N)
	io.Pf("RF1 (unknown) =\n %v\n", e.RF1)
	io.Pf("FR1 = \n%v\n", e.FR1)
	io.Pf("RF2 (prescribed) =\n %v\n", e.RF2)
	io.Pf("FR2 = \n%v\n", e.FR2)
}
<|endoftext|>


<commit_before>// Copyright 2016 Peter Goetz
//
// 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 main

import (
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/petergtz/pegomock/pegomock/filehandling"
	"github.com/petergtz/pegomock/pegomock/remove"
	"github.com/petergtz/pegomock/pegomock/util"
	"github.com/petergtz/pegomock/pegomock/watch"
)

var (
	app = kingpin.New("pegomock", "Generates mocks based on interfaces.")
)

func main() {
	Run(os.Args, os.Stderr, os.Stdin, app, make(chan bool))
}

func Run(cliArgs []string, out io.Writer, in io.Reader, app *kingpin.Application, done chan bool) {

	workingDir, err := os.Getwd()
	app.FatalIfError(err, "")

	var (
		generateCmd    = app.Command("generate", "Generate mocks based on the args provided. ")
		destination    = generateCmd.Flag("output", "Output file; defaults to mock_<interface>_test.go.").Short('o').String()
		destinationDir = generateCmd.Flag("output-dir", "Output directory; defaults to current directory. If set, package name defaults to this directory, unless explicitly overridden.").String()
		packageOut     = generateCmd.Flag("package", "Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test").String()
		// TODO: self_package was taken as is from GoMock.
		//       Still don't understand what it's really there for.
		//       So for now it's not tested.
		selfPackage            = generateCmd.Flag("self_package", "If set, the package this mock will be part of.").String()
		debugParser            = generateCmd.Flag("debug", "Print debug information.").Short('d').Bool()
		shouldGenerateMatchers = generateCmd.Flag("generate-matchers", "Generate matchers for all non built-in types in a \"matchers\" "+
			"directory in the same directory where the mock file gets generated.").Short('m').Default("false").Bool()
		matchersDestination = generateCmd.Flag("matchers-dir", "Generate matchers in the specified directory; defaults to "+
			filepath.Join("<mockdir>", "matchers")).Short('p').String()
		useExperimentalModelGen = generateCmd.Flag("use-experimental-model-gen", "pegomock includes a new experimental source parser based on "+
			"golang.org/x/tools/go/loader. It's currently experimental, but should be more powerful "+
			"than the current reflect-based modelgen. E.g. reflect cannot detect method parameter names,"+
			" and has to generate them based on a pattern. In a code editor with code assistence, this doesn't provide good help. "+
			"\n\nThis option only works when specifying package path + interface, not with .go source files. Also, you can only specify *one* interface. This option cannot be used with the watch command.").Bool()
		generateCmdArgs = generateCmd.Arg("args", "A (optional) Go package path + space-separated interface or a .go file").Required().Strings()

		watchCmd       = app.Command("watch", "Watch over changes in interfaces and regenerate mocks if changes are detected.")
		watchRecursive = watchCmd.Flag("recursive", "Recursively watch sub-directories as well.").Short('r').Bool()
		watchPackages  = watchCmd.Arg("directories...", "One or more directories of Go packages to watch").Strings()

		removeMocks          = app.Command("remove", "Remove mocks generated by Pegomock")
		removeRecursive      = removeMocks.Flag("recursive", "Remove recursively in all sub-directories").Default("false").Short('r').Bool()
		removeNonInteractive = removeMocks.Flag("non-interactive", "TODO").Default("false").Short('n').Bool()
		removeDryRun         = removeMocks.Flag("dry-run", "TODO").Default("false").Short('d').Bool()
		removeSilent         = removeMocks.Flag("silent", "TODO").Default("false").Short('s').Bool()
		removePath           = removeMocks.Arg("path", "TODO").Default("").String()
	)

	app.Writer(out)
	switch kingpin.MustParse(app.Parse(cliArgs[1:])) {

	case generateCmd.FullCommand():
		if err := util.ValidateArgs(*generateCmdArgs); err != nil {
			app.FatalUsage(err.Error())
		}
		sourceArgs, err := util.SourceArgs(*generateCmdArgs)
		if err != nil {
			app.FatalUsage(err.Error())
		}

		if *destination != "" && *destinationDir != "" {
			app.FatalUsage("Cannot use --output and --output-dir together")
		}

		realPackageOut := *packageOut
		if *packageOut == "" {
			realPackageOut = filepath.Base(workingDir) + "_test"
		}

		realDestination := *destination
		realDestinationDir := workingDir
		if *destinationDir != "" {
			realDestinationDir, err = filepath.Abs(*destinationDir)
			app.FatalIfError(err, "")
			if *packageOut == "" {
				realPackageOut = filepath.Base(*destinationDir)
			}
			if util.SourceMode(sourceArgs) {
				realDestination = filepath.Join(*destinationDir, "mock_"+strings.TrimSuffix(sourceArgs[0], ".go")+".go")
			} else {
				realDestination = filepath.Join(*destinationDir, "mock_"+strings.ToLower(sourceArgs[len(sourceArgs)-1])+".go")
			}
		}

		filehandling.GenerateMockFileInOutputDir(
			sourceArgs,
			realDestinationDir,
			realDestination,
			realPackageOut,
			*selfPackage,
			*debugParser,
			out,
			*useExperimentalModelGen,
			*shouldGenerateMatchers,
			*matchersDestination)

	case watchCmd.FullCommand():
		var targetPaths []string
		if len(*watchPackages) == 0 {
			targetPaths = []string{workingDir}
		} else {
			targetPaths = *watchPackages
		}
		watch.CreateWellKnownInterfaceListFilesIfNecessary(targetPaths)
		util.Ticker(watch.NewMockFileUpdater(targetPaths, *watchRecursive).Update, 2*time.Second, done)

	case removeMocks.FullCommand():
		path := *removePath
		if path == "" {
			var e error
			path, e = os.Getwd()
			app.FatalIfError(e, "Could not get current working directory")
		}
		remove.Remove(path, *removeRecursive, !*removeNonInteractive, *removeDryRun, *removeSilent, out, in, os.Remove)
	}
}
<commit_msg>Replace TODOs in remove command help<commit_after>// Copyright 2016 Peter Goetz
//
// 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 main

import (
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/petergtz/pegomock/pegomock/filehandling"
	"github.com/petergtz/pegomock/pegomock/remove"
	"github.com/petergtz/pegomock/pegomock/util"
	"github.com/petergtz/pegomock/pegomock/watch"
)

var (
	app = kingpin.New("pegomock", "Generates mocks based on interfaces.")
)

func main() {
	Run(os.Args, os.Stderr, os.Stdin, app, make(chan bool))
}

func Run(cliArgs []string, out io.Writer, in io.Reader, app *kingpin.Application, done chan bool) {

	workingDir, err := os.Getwd()
	app.FatalIfError(err, "")

	var (
		generateCmd    = app.Command("generate", "Generate mocks based on the args provided. ")
		destination    = generateCmd.Flag("output", "Output file; defaults to mock_<interface>_test.go.").Short('o').String()
		destinationDir = generateCmd.Flag("output-dir", "Output directory; defaults to current directory. If set, package name defaults to this directory, unless explicitly overridden.").String()
		packageOut     = generateCmd.Flag("package", "Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test").String()
		// TODO: self_package was taken as is from GoMock.
		//       Still don't understand what it's really there for.
		//       So for now it's not tested.
		selfPackage            = generateCmd.Flag("self_package", "If set, the package this mock will be part of.").String()
		debugParser            = generateCmd.Flag("debug", "Print debug information.").Short('d').Bool()
		shouldGenerateMatchers = generateCmd.Flag("generate-matchers", "Generate matchers for all non built-in types in a \"matchers\" "+
			"directory in the same directory where the mock file gets generated.").Short('m').Default("false").Bool()
		matchersDestination = generateCmd.Flag("matchers-dir", "Generate matchers in the specified directory; defaults to "+
			filepath.Join("<mockdir>", "matchers")).Short('p').String()
		useExperimentalModelGen = generateCmd.Flag("use-experimental-model-gen", "pegomock includes a new experimental source parser based on "+
			"golang.org/x/tools/go/loader. It's currently experimental, but should be more powerful "+
			"than the current reflect-based modelgen. E.g. reflect cannot detect method parameter names,"+
			" and has to generate them based on a pattern. In a code editor with code assistence, this doesn't provide good help. "+
			"\n\nThis option only works when specifying package path + interface, not with .go source files. Also, you can only specify *one* interface. This option cannot be used with the watch command.").Bool()
		generateCmdArgs = generateCmd.Arg("args", "A (optional) Go package path + space-separated interface or a .go file").Required().Strings()

		watchCmd       = app.Command("watch", "Watch over changes in interfaces and regenerate mocks if changes are detected.")
		watchRecursive = watchCmd.Flag("recursive", "Recursively watch sub-directories as well.").Short('r').Bool()
		watchPackages  = watchCmd.Arg("directories...", "One or more directories of Go packages to watch").Strings()

		removeMocks          = app.Command("remove", "Remove mocks generated by Pegomock")
		removeRecursive      = removeMocks.Flag("recursive", "Remove recursively in all sub-directories").Default("false").Short('r').Bool()
		removeNonInteractive = removeMocks.Flag("non-interactive", "Don't ask for confirmation. Useful for scripts.").Default("false").Short('n').Bool()
		removeDryRun         = removeMocks.Flag("dry-run", "Just show what would be done. Don't delete anything.").Default("false").Short('d').Bool()
		removeSilent         = removeMocks.Flag("silent", "Don't write anything to standard out.").Default("false").Short('s').Bool()
		removePath           = removeMocks.Arg("path", "Use as root directory instead of current working directory.").Default("").String()
	)

	app.Writer(out)
	switch kingpin.MustParse(app.Parse(cliArgs[1:])) {

	case generateCmd.FullCommand():
		if err := util.ValidateArgs(*generateCmdArgs); err != nil {
			app.FatalUsage(err.Error())
		}
		sourceArgs, err := util.SourceArgs(*generateCmdArgs)
		if err != nil {
			app.FatalUsage(err.Error())
		}

		if *destination != "" && *destinationDir != "" {
			app.FatalUsage("Cannot use --output and --output-dir together")
		}

		realPackageOut := *packageOut
		if *packageOut == "" {
			realPackageOut = filepath.Base(workingDir) + "_test"
		}

		realDestination := *destination
		realDestinationDir := workingDir
		if *destinationDir != "" {
			realDestinationDir, err = filepath.Abs(*destinationDir)
			app.FatalIfError(err, "")
			if *packageOut == "" {
				realPackageOut = filepath.Base(*destinationDir)
			}
			if util.SourceMode(sourceArgs) {
				realDestination = filepath.Join(*destinationDir, "mock_"+strings.TrimSuffix(sourceArgs[0], ".go")+".go")
			} else {
				realDestination = filepath.Join(*destinationDir, "mock_"+strings.ToLower(sourceArgs[len(sourceArgs)-1])+".go")
			}
		}

		filehandling.GenerateMockFileInOutputDir(
			sourceArgs,
			realDestinationDir,
			realDestination,
			realPackageOut,
			*selfPackage,
			*debugParser,
			out,
			*useExperimentalModelGen,
			*shouldGenerateMatchers,
			*matchersDestination)

	case watchCmd.FullCommand():
		var targetPaths []string
		if len(*watchPackages) == 0 {
			targetPaths = []string{workingDir}
		} else {
			targetPaths = *watchPackages
		}
		watch.CreateWellKnownInterfaceListFilesIfNecessary(targetPaths)
		util.Ticker(watch.NewMockFileUpdater(targetPaths, *watchRecursive).Update, 2*time.Second, done)

	case removeMocks.FullCommand():
		path := *removePath
		if path == "" {
			var e error
			path, e = os.Getwd()
			app.FatalIfError(e, "Could not get current working directory")
		}
		remove.Remove(path, *removeRecursive, !*removeNonInteractive, *removeDryRun, *removeSilent, out, in, os.Remove)
	}
}
<|endoftext|>


<commit_before>package terraform

import (
	"bytes"
	"crypto/sha1"
	"encoding/gob"
	"encoding/hex"
	"path/filepath"
	"sync"
	"testing"

	"github.com/hashicorp/terraform/config"
)

// This is the directory where our test fixtures are.
const fixtureDir = "./test-fixtures"

func checksumStruct(t *testing.T, i interface{}) string {
	// TODO(mitchellh): write a library to do this because gob is not
	// deterministic in order
	return "foo"

	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(i); err != nil {
		t.Fatalf("err: %s", err)
	}

	sum := sha1.Sum(buf.Bytes())
	return hex.EncodeToString(sum[:])
}

func testConfig(t *testing.T, name string) *config.Config {
	c, err := config.Load(filepath.Join(fixtureDir, name, "main.tf"))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	return c
}

func testProviderFuncFixed(rp ResourceProvider) ResourceProviderFactory {
	return func() (ResourceProvider, error) {
		return rp, nil
	}
}

func testProvisionerFuncFixed(rp ResourceProvisioner) ResourceProvisionerFactory {
	return func() (ResourceProvisioner, error) {
		return rp, nil
	}
}

// HookRecordApplyOrder is a test hook that records the order of applies
// by recording the PreApply event.
type HookRecordApplyOrder struct {
	NilHook

	Active bool

	IDs    []string
	States []*InstanceState
	Diffs  []*InstanceDiff

	l sync.Mutex
}

func (h *HookRecordApplyOrder) PreApply(
	id string,
	s *InstanceState,
	d *InstanceDiff) (HookAction, error) {
	if h.Active {
		h.l.Lock()
		defer h.l.Unlock()

		h.IDs = append(h.IDs, id)
		h.Diffs = append(h.Diffs, d)
		h.States = append(h.States, s)
	}

	return HookActionContinue, nil
}

// Below are all the constant strings that are the expected output for
// various tests.

const testTerraformApplyStr = `
aws_instance.bar:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyCancelStr = `
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyComputeStr = `
aws_instance.bar:
  ID = foo
  foo = computed_dynamical
  type = aws_instance
aws_instance.foo:
  ID = foo
  dynamical = computed_dynamical
  num = 2
  type = aws_instance
`

const testTerraformApplyMinimalStr = `
aws_instance.bar:
  ID = foo
aws_instance.foo:
  ID = foo
`

const testTerraformApplyProvisionerStr = `
aws_instance.bar:
  ID = foo
aws_instance.foo:
  ID = foo
  dynamical = computed_dynamical
  num = 2
  type = aws_instance
`

const testTerraformApplyProvisionerFailStr = `
aws_instance.bar: (tainted)
  ID = foo
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyProvisionerResourceRefStr = `
aws_instance.bar:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyDestroyStr = `
<no state>
`

const testTerraformApplyErrorStr = `
aws_instance.bar:
  ID = bar
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyErrorPartialStr = `
aws_instance.bar:
  ID = bar
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyTaintStr = `
aws_instance.bar:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyOutputStr = `
aws_instance.bar:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = 2
`

const testTerraformApplyOutputMultiStr = `
aws_instance.bar.0:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.1:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.2:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = bar,bar,bar
`

const testTerraformApplyOutputMultiIndexStr = `
aws_instance.bar.0:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.1:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.2:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = bar
`

const testTerraformApplyUnknownAttrStr = `
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyVarsStr = `
aws_instance.bar:
  ID = foo
  bar = foo
  baz = override
  foo = us-west-2
  type = aws_instance
aws_instance.foo:
  ID = foo
  bar = baz
  num = 2
  type = aws_instance
`

const testTerraformPlanStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  foo:  "" => "<computed>"
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedIdStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  foo:  "" => "<computed>"
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedListStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  list.#: "" => "<computed>"
  num:    "" => "2"
  type:   "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanCountStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "foo,foo,foo,foo,foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.0
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.3
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.4
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanCountDecreaseStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
DESTROY: aws_instance.foo.1
DESTROY: aws_instance.foo.2

STATE:

aws_instance.foo.0:
  ID = bar
  foo = foo
  type = aws_instance
aws_instance.foo.1:
  ID = bar
aws_instance.foo.2:
  ID = bar
`

const testTerraformPlanCountIncreaseStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

aws_instance.foo:
  ID = bar
  foo = foo
  type = aws_instance
`

const testTerraformPlanCountIncreaseFromOneStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

aws_instance.foo.0:
  ID = bar
  foo = foo
  type = aws_instance
`

const testTerraformPlanDestroyStr = `
DIFF:

DESTROY: aws_instance.one
DESTROY: aws_instance.two

STATE:

aws_instance.one:
  ID = bar
aws_instance.two:
  ID = baz
`

const testTerraformPlanDiffVarStr = `
DIFF:

CREATE: aws_instance.bar
  num:  "" => "3"
  type: "" => "aws_instance"
UPDATE: aws_instance.foo
  num: "2" => "3"

STATE:

aws_instance.foo:
  ID = bar
  num = 2
`

const testTerraformPlanEmptyStr = `
DIFF:

CREATE: aws_instance.bar
CREATE: aws_instance.foo

STATE:

<no state>
`

const testTerraformPlanOrphanStr = `
DIFF:

DESTROY: aws_instance.baz
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

aws_instance.baz:
  ID = bar
`

const testTerraformPlanStateStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
UPDATE: aws_instance.foo
  num:  "" => "2"
  type: "" => ""

STATE:

aws_instance.foo:
  ID = bar
`

const testTerraformPlanTaintStr = `
DIFF:

DESTROY: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"

STATE:

aws_instance.bar: (tainted)
  ID = baz
aws_instance.foo:
  ID = bar
  num = 2
`

const testTerraformPlanVarMultiCountOneStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`
<commit_msg>terraform: fixing ContextApply_compute test<commit_after>package terraform

import (
	"bytes"
	"crypto/sha1"
	"encoding/gob"
	"encoding/hex"
	"path/filepath"
	"sync"
	"testing"

	"github.com/hashicorp/terraform/config"
)

// This is the directory where our test fixtures are.
const fixtureDir = "./test-fixtures"

func checksumStruct(t *testing.T, i interface{}) string {
	// TODO(mitchellh): write a library to do this because gob is not
	// deterministic in order
	return "foo"

	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(i); err != nil {
		t.Fatalf("err: %s", err)
	}

	sum := sha1.Sum(buf.Bytes())
	return hex.EncodeToString(sum[:])
}

func testConfig(t *testing.T, name string) *config.Config {
	c, err := config.Load(filepath.Join(fixtureDir, name, "main.tf"))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	return c
}

func testProviderFuncFixed(rp ResourceProvider) ResourceProviderFactory {
	return func() (ResourceProvider, error) {
		return rp, nil
	}
}

func testProvisionerFuncFixed(rp ResourceProvisioner) ResourceProvisionerFactory {
	return func() (ResourceProvisioner, error) {
		return rp, nil
	}
}

// HookRecordApplyOrder is a test hook that records the order of applies
// by recording the PreApply event.
type HookRecordApplyOrder struct {
	NilHook

	Active bool

	IDs    []string
	States []*InstanceState
	Diffs  []*InstanceDiff

	l sync.Mutex
}

func (h *HookRecordApplyOrder) PreApply(
	id string,
	s *InstanceState,
	d *InstanceDiff) (HookAction, error) {
	if h.Active {
		h.l.Lock()
		defer h.l.Unlock()

		h.IDs = append(h.IDs, id)
		h.Diffs = append(h.Diffs, d)
		h.States = append(h.States, s)
	}

	return HookActionContinue, nil
}

// Below are all the constant strings that are the expected output for
// various tests.

const testTerraformApplyStr = `
aws_instance.bar:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyCancelStr = `
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyComputeStr = `
aws_instance.bar:
  ID = foo
  foo = computed_dynamical
  type = aws_instance

  Dependencies:
    aws_instance.foo
aws_instance.foo:
  ID = foo
  dynamical = computed_dynamical
  num = 2
  type = aws_instance
`

const testTerraformApplyMinimalStr = `
aws_instance.bar:
  ID = foo
aws_instance.foo:
  ID = foo
`

const testTerraformApplyProvisionerStr = `
aws_instance.bar:
  ID = foo
aws_instance.foo:
  ID = foo
  dynamical = computed_dynamical
  num = 2
  type = aws_instance
`

const testTerraformApplyProvisionerFailStr = `
aws_instance.bar: (tainted)
  ID = foo
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyProvisionerResourceRefStr = `
aws_instance.bar:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyDestroyStr = `
<no state>
`

const testTerraformApplyErrorStr = `
aws_instance.bar:
  ID = bar
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyErrorPartialStr = `
aws_instance.bar:
  ID = bar
aws_instance.foo:
  ID = foo
  num = 2
`

const testTerraformApplyTaintStr = `
aws_instance.bar:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyOutputStr = `
aws_instance.bar:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = 2
`

const testTerraformApplyOutputMultiStr = `
aws_instance.bar.0:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.1:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.2:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = bar,bar,bar
`

const testTerraformApplyOutputMultiIndexStr = `
aws_instance.bar.0:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.1:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.bar.2:
  ID = foo
  foo = bar
  type = aws_instance
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance

Outputs:

foo_num = bar
`

const testTerraformApplyUnknownAttrStr = `
aws_instance.foo:
  ID = foo
  num = 2
  type = aws_instance
`

const testTerraformApplyVarsStr = `
aws_instance.bar:
  ID = foo
  bar = foo
  baz = override
  foo = us-west-2
  type = aws_instance
aws_instance.foo:
  ID = foo
  bar = baz
  num = 2
  type = aws_instance
`

const testTerraformPlanStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  foo:  "" => "<computed>"
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedIdStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  foo:  "" => "<computed>"
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanComputedListStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "<computed>"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  list.#: "" => "<computed>"
  num:    "" => "2"
  type:   "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanCountStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "foo,foo,foo,foo,foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.0
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.3
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.4
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

<no state>
`

const testTerraformPlanCountDecreaseStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
DESTROY: aws_instance.foo.1
DESTROY: aws_instance.foo.2

STATE:

aws_instance.foo.0:
  ID = bar
  foo = foo
  type = aws_instance
aws_instance.foo.1:
  ID = bar
aws_instance.foo.2:
  ID = bar
`

const testTerraformPlanCountIncreaseStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

aws_instance.foo:
  ID = bar
  foo = foo
  type = aws_instance
`

const testTerraformPlanCountIncreaseFromOneStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "bar"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.1
  foo:  "" => "foo"
  type: "" => "aws_instance"
CREATE: aws_instance.foo.2
  foo:  "" => "foo"
  type: "" => "aws_instance"

STATE:

aws_instance.foo.0:
  ID = bar
  foo = foo
  type = aws_instance
`

const testTerraformPlanDestroyStr = `
DIFF:

DESTROY: aws_instance.one
DESTROY: aws_instance.two

STATE:

aws_instance.one:
  ID = bar
aws_instance.two:
  ID = baz
`

const testTerraformPlanDiffVarStr = `
DIFF:

CREATE: aws_instance.bar
  num:  "" => "3"
  type: "" => "aws_instance"
UPDATE: aws_instance.foo
  num: "2" => "3"

STATE:

aws_instance.foo:
  ID = bar
  num = 2
`

const testTerraformPlanEmptyStr = `
DIFF:

CREATE: aws_instance.bar
CREATE: aws_instance.foo

STATE:

<no state>
`

const testTerraformPlanOrphanStr = `
DIFF:

DESTROY: aws_instance.baz
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

aws_instance.baz:
  ID = bar
`

const testTerraformPlanStateStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
UPDATE: aws_instance.foo
  num:  "" => "2"
  type: "" => ""

STATE:

aws_instance.foo:
  ID = bar
`

const testTerraformPlanTaintStr = `
DIFF:

DESTROY: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"

STATE:

aws_instance.bar: (tainted)
  ID = baz
aws_instance.foo:
  ID = bar
  num = 2
`

const testTerraformPlanVarMultiCountOneStr = `
DIFF:

CREATE: aws_instance.bar
  foo:  "" => "2"
  type: "" => "aws_instance"
CREATE: aws_instance.foo
  num:  "" => "2"
  type: "" => "aws_instance"

STATE:

<no state>
`
<|endoftext|>


<commit_before>// +build freebsd netbsd openbsd dragonfly darwin

package immortal

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestNewScanDir(t *testing.T) {
	_, err := NewScanDir("/tmp")
	if err != nil {
		t.Error(err)
	}
}

func TestNewScanDirNonexistent(t *testing.T) {
	_, err := NewScanDir("/dev/null/non-existent")
	if err == nil {
		t.Error("Expecting error")
	}
}

// TestWathFile create a dummy file, do changes on it and WathFile should return
// the same file
func TestWathFile(t *testing.T) {
	dir, err := ioutil.TempDir("", "TestWathFile")
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dir)
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	file := filepath.Join(dir, "run.yml")
	if err = ioutil.WriteFile(file, []byte(""), 0644); err != nil {
		t.Fatal(err)
	}
	go s.WatchFile(file)
	go func() {
		time.Sleep(time.Second)
		if err = ioutil.WriteFile(file, []byte("--"), 0644); err != nil {
			t.Fatal(err)
		}
	}()
	select {
	case watch := <-s.watch:
		expect(t, file, watch)
	case <-time.After(time.Second * 2):
		t.Fatal("time out waiting for file to change")
	}
}

type mockController struct{}

func (mc *mockController) GetStatus(socket string) (*Status, error)                  { return &Status{}, nil }
func (mc *mockController) SendSignal(socket, signal string) (*SignalResponse, error) { return nil, nil }
func (mc *mockController) FindServices(dir string) ([]*ServiceStatus, error)         { return nil, nil }
func (mc *mockController) PurgeServices(dir string) error                            { return nil }
func (mc *mockController) Run(command string) ([]byte, error) {
	// TODO
	return nil, nil
}

func TestScandir(t *testing.T) {
	dir, err := ioutil.TempDir("", "TestWathFile")
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dir)
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	dir, err = filepath.EvalSymlinks(dir)
	if err != nil {
		t.Fatal(err)
	}
	file := filepath.Join(dir, "run.yml")
	if err = ioutil.WriteFile(file, []byte(""), 0644); err != nil {
		t.Fatal(err)
	}
	go func() {
		time.Sleep(time.Second)
		if err = ioutil.WriteFile(file, []byte("--"), 0644); err != nil {
			t.Fatal(err)
		}
	}()
	ctl := &mockController{}
	s.Scandir(ctl)
	select {
	case watch := <-s.watch:
		expect(t, file, watch)
	case <-time.After(time.Second * 2):
		t.Fatal("time out waiting for file to change")
	}
	_, ok := s.services.Load("run.yml")
	if ok {
		t.Fatalf("Not expecting value for %s", "run.yml")
	}
}
<commit_msg>scandir test<commit_after>// +build freebsd netbsd openbsd dragonfly darwin

package immortal

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestNewScanDir(t *testing.T) {
	_, err := NewScanDir("/tmp")
	if err != nil {
		t.Error(err)
	}
}

func TestNewScanDirNonexistent(t *testing.T) {
	_, err := NewScanDir("/dev/null/non-existent")
	if err == nil {
		t.Error("Expecting error")
	}
}

// TestWathFile create a dummy file, do changes on it and WathFile should return
// the same file
func TestWathFile(t *testing.T) {
	dir, err := ioutil.TempDir("", "TestWathFile")
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dir)
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	file := filepath.Join(dir, "run.yml")
	if err = ioutil.WriteFile(file, []byte(""), 0644); err != nil {
		t.Fatal(err)
	}
	go s.WatchFile(file)
	go func() {
		time.Sleep(time.Second)
		if err = ioutil.WriteFile(file, []byte("--"), 0644); err != nil {
			t.Fatal(err)
		}
	}()
	select {
	case watch := <-s.watch:
		expect(t, file, watch)
	case <-time.After(time.Second * 2):
		t.Fatal("time out waiting for file to change")
	}
}

type mockController struct{}

func (mc *mockController) GetStatus(socket string) (*Status, error)                  { return &Status{}, nil }
func (mc *mockController) SendSignal(socket, signal string) (*SignalResponse, error) { return nil, nil }
func (mc *mockController) FindServices(dir string) ([]*ServiceStatus, error)         { return nil, nil }
func (mc *mockController) PurgeServices(dir string) error                            { return nil }
func (mc *mockController) Run(command string) ([]byte, error) {
	// TODO
	return nil, nil
}

func TestScandir(t *testing.T) {
	dir, err := ioutil.TempDir("", "TestWathFile")
	if err != nil {
		t.Error(err)
	}
	defer os.RemoveAll(dir)
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	dir, err = filepath.EvalSymlinks(dir)
	if err != nil {
		t.Fatal(err)
	}
	file := filepath.Join(dir, "run.yml")
	if err = ioutil.WriteFile(file, []byte(""), 0644); err != nil {
		t.Fatal(err)
	}
	go func() {
		time.Sleep(time.Second)
		if err = ioutil.WriteFile(file, []byte("--"), 0644); err != nil {
			t.Fatal(err)
		}
	}()
	ctl := &mockController{}
	s.Scandir(ctl)
	select {
	case watch := <-s.watch:
		expect(t, file, watch)
	case <-time.After(time.Second * 2):
		t.Fatal("time out waiting for file to change")
	}
	val, ok := s.services.Load("run")
	if !ok {
		t.Fatalf("Not expecting value for %s", "run.yml")
	}
	expect(t, val, "d41d8cd98f00b204e9800998ecf8427e")
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 schema

import (
	"encoding/json"
	"io"

	"go4.org/errorutil"
)

// A SyntaxError is a description of a JSON syntax error
// including line, column and offset in the JSON file.
type SyntaxError struct {
	msg       string
	Line, Col int
	Offset    int64
}

func (e *SyntaxError) Error() string { return e.msg }

// WrapSyntaxError checks whether the given error is a *json.SyntaxError
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
// If the given error is not a *json.SyntaxError it is returned unchanged.
func WrapSyntaxError(r io.Reader, err error) error {
	if serr, ok := err.(*json.SyntaxError); ok {
		line, col, _ := errorutil.HighlightBytePosition(r, serr.Offset)
		return &SyntaxError{serr.Error(), line, col, serr.Offset}
	}

	return err
}
<commit_msg>Remove go4.org dependency<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 schema

import (
	"bufio"
	"encoding/json"
	"io"
)

// A SyntaxError is a description of a JSON syntax error
// including line, column and offset in the JSON file.
type SyntaxError struct {
	msg       string
	Line, Col int
	Offset    int64
}

func (e *SyntaxError) Error() string { return e.msg }

// WrapSyntaxError checks whether the given error is a *json.SyntaxError
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
// If the given error is not a *json.SyntaxError it is returned unchanged.
func WrapSyntaxError(r io.Reader, err error) error {
	if serr, ok := err.(*json.SyntaxError); ok {
		buf := bufio.NewReader(r)
		line := 0
		col := 0
		for i := int64(0); i < serr.Offset; i++ {
			b, berr := buf.ReadByte()
			if berr != nil {
				break
			}
			if b == '\n' {
				line++
				col = 1
			} else {
				col++
			}
		}
		return &SyntaxError{serr.Error(), line, col, serr.Offset}
	}

	return err
}
<|endoftext|>