<commit_before>// Package audit contains the password-strength auditing implementation
package audit

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/gopasspw/gopass/internal/backend"
	"github.com/gopasspw/gopass/internal/notify"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/store"

	"github.com/fatih/color"
	"github.com/muesli/crunchy"
	"github.com/muesli/goprogressbar"
)

// auditedSecret with its name, content a warning message and a pipeline error.
type auditedSecret struct {
	name string

	// the secret's content as a string. Needed for checking for duplicates.
	content string

	// message to the user about some flaw in the secret
	messages []string

	// real error that something in the pipeline went wrong
	err error
}

type secretGetter interface {
	Get(context.Context, string) (store.Secret, error)
	ListRevisions(context.Context, string) ([]backend.Revision, error)
}

// Batch runs a password strength audit on multiple secrets
func Batch(ctx context.Context, secrets []string, secStore secretGetter) error {
	out.Print(ctx, "Checking %d secrets. This may take some time ...\n", len(secrets))

	// Secrets that still need auditing.
	pending := make(chan string, 100)

	// Secrets that have been audited.
	checked := make(chan auditedSecret, 100)

	// Spawn workers that run the auditing of all secrets concurrently.
	validator := crunchy.NewValidator()

	// It would be nice to parallelize this operation and limit the maxJobs to
	// runtime.NumCPU(), but sadly this causes various problems with multiple
	// gnupg jobs running parallelly. See the entire discussion here:
	//
	// https://github.com/gopasspw/gopass/pull/245

	maxJobs := 1 // do not change
	done := make(chan struct{}, maxJobs)
	for jobs := 0; jobs < maxJobs; jobs++ {
		go audit(ctx, secStore, validator, pending, checked, done)
	}

	go func() {
		for _, secret := range secrets {
			pending <- secret
		}
		close(pending)
	}()
	go func() {
		for i := 0; i < maxJobs; i++ {
			<-done
		}
		close(checked)
	}()

	duplicates := make(map[string][]string)
	messages := make(map[string][]string)
	errors := make(map[string][]string)

	bar := &goprogressbar.ProgressBar{
		Total: int64(len(secrets)),
		Width: 120,
	}
	if out.IsHidden(ctx) {
		old := goprogressbar.Stdout
		goprogressbar.Stdout = ioutil.Discard
		defer func() {
			goprogressbar.Stdout = old
		}()
	}

	i := 0
	for secret := range checked {
		if secret.err != nil {
			en := secret.err.Error()
			errors[en] = append(errors[en], secret.name)
		} else {
			duplicates[secret.content] = append(duplicates[secret.content], secret.name)
		}
		for _, m := range secret.messages {
			messages[m] = append(messages[m], secret.name)
		}

		i++
		bar.Current = int64(i)
		if bar.Current > bar.Total {
			bar.Total = bar.Current
		}
		bar.Text = fmt.Sprintf("%d of %d secrets checked", bar.Current, bar.Total)
		bar.LazyPrint()

		if i == len(secrets) {
			break
		}
	}
	fmt.Fprintln(goprogressbar.Stdout) // Print empty line after the progressbar.

	return auditPrintResults(ctx, duplicates, messages, errors)
}

func audit(ctx context.Context, secStore secretGetter, validator *crunchy.Validator, secrets <-chan string, checked chan<- auditedSecret, done chan struct{}) {
	for secret := range secrets {
		as := auditedSecret{
			name: secret,
		}
		// check for context cancelation
		select {
		case <-ctx.Done():
			as.err = errors.New("user aborted")
			checked <- as
			continue
		default:
		}

		sec, err := secStore.Get(ctx, secret)
		if err != nil {
			as.err = err
			if sec != nil {
				as.content = sec.Password()
			}
			// failed to properly retrieve the secret
			checked <- as
			continue
		}

		as.content = sec.Password()

		// handle password validation errors
		if err := validator.Check(as.content); err != nil {
			as.messages = append(as.messages, err.Error())
			checked <- as
			continue
		}

		// handle old passwords
		revs, err := secStore.ListRevisions(ctx, secret)
		if err != nil {
			as.messages = append(as.messages, err.Error())
		} else {
			if len(revs) > 0 && time.Since(revs[0].Date) > 90*24*time.Hour {
				as.messages = append(as.messages, "Password too old (90d)")
			}
		}

		// record every password for possible duplicates
		checked <- as
	}
	done <- struct{}{}
}

func printAuditResults(m map[string][]string, format string, color func(format string, a ...interface{}) string) bool {
	b := false

	for msg, secrets := range m {
		b = true
		fmt.Fprint(goprogressbar.Stdout, color(format, msg))
		for _, secret := range secrets {
			fmt.Fprint(goprogressbar.Stdout, color("\t- %s\n", secret))
		}
	}

	return b
}

// Single runs a password strength audit on a single password
func Single(ctx context.Context, password string) {
	validator := crunchy.NewValidator()
	if err := validator.Check(password); err != nil {
		out.Cyan(ctx, fmt.Sprintf("Warning: %s", err))
	}
}

func auditPrintResults(ctx context.Context, duplicates, messages, errors map[string][]string) error {
	foundDuplicates := false
	for _, secrets := range duplicates {
		if len(secrets) > 1 {
			foundDuplicates = true

			out.Cyan(ctx, "Detected a shared secret for:")
			for _, secret := range secrets {
				out.Cyan(ctx, "\t- %s", secret)
			}
		}
	}
	if !foundDuplicates {
		out.Green(ctx, "No shared secrets found.")
	}

	foundWeakPasswords := printAuditResults(messages, "%s:\n", color.CyanString)
	if !foundWeakPasswords {
		out.Green(ctx, "No weak secrets detected.")
	}
	foundErrors := printAuditResults(errors, "%s:\n", color.RedString)

	if foundWeakPasswords || foundDuplicates || foundErrors {
		_ = notify.Notify(ctx, "gopass - audit", "Finished. Found weak passwords and/or duplicates")
		return fmt.Errorf("found weak passwords or duplicates")
	}

	_ = notify.Notify(ctx, "gopass - audit", "Finished. No weak passwords or duplicates found!")
	return nil
}
<commit_msg>Ignore binary secrets during audit (#1398)<commit_after>// Package audit contains the password-strength auditing implementation
package audit

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/gopasspw/gopass/internal/backend"
	"github.com/gopasspw/gopass/internal/debug"
	"github.com/gopasspw/gopass/internal/notify"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/store"

	"github.com/fatih/color"
	"github.com/muesli/crunchy"
	"github.com/muesli/goprogressbar"
)

// auditedSecret with its name, content a warning message and a pipeline error.
type auditedSecret struct {
	name string

	// the secret's content as a string. Needed for checking for duplicates.
	content string

	// message to the user about some flaw in the secret
	messages []string

	// real error that something in the pipeline went wrong
	err error
}

type secretGetter interface {
	Get(context.Context, string) (store.Secret, error)
	ListRevisions(context.Context, string) ([]backend.Revision, error)
}

// Batch runs a password strength audit on multiple secrets
func Batch(ctx context.Context, secrets []string, secStore secretGetter) error {
	out.Print(ctx, "Checking %d secrets. This may take some time ...\n", len(secrets))

	// Secrets that still need auditing.
	pending := make(chan string, 100)

	// Secrets that have been audited.
	checked := make(chan auditedSecret, 100)

	// Spawn workers that run the auditing of all secrets concurrently.
	validator := crunchy.NewValidator()

	// It would be nice to parallelize this operation and limit the maxJobs to
	// runtime.NumCPU(), but sadly this causes various problems with multiple
	// gnupg jobs running parallelly. See the entire discussion here:
	//
	// https://github.com/gopasspw/gopass/pull/245

	maxJobs := 1 // do not change
	done := make(chan struct{}, maxJobs)
	for jobs := 0; jobs < maxJobs; jobs++ {
		go audit(ctx, secStore, validator, pending, checked, done)
	}

	go func() {
		for _, secret := range secrets {
			pending <- secret
		}
		close(pending)
	}()
	go func() {
		for i := 0; i < maxJobs; i++ {
			<-done
		}
		close(checked)
	}()

	duplicates := make(map[string][]string)
	messages := make(map[string][]string)
	errors := make(map[string][]string)

	bar := &goprogressbar.ProgressBar{
		Total: int64(len(secrets)),
		Width: 120,
	}
	if out.IsHidden(ctx) {
		old := goprogressbar.Stdout
		goprogressbar.Stdout = ioutil.Discard
		defer func() {
			goprogressbar.Stdout = old
		}()
	}

	i := 0
	for secret := range checked {
		if secret.err != nil {
			en := secret.err.Error()
			errors[en] = append(errors[en], secret.name)
		} else if secret.content != "" {
			duplicates[secret.content] = append(duplicates[secret.content], secret.name)
		}
		for _, m := range secret.messages {
			messages[m] = append(messages[m], secret.name)
		}

		i++
		bar.Current = int64(i)
		if bar.Current > bar.Total {
			bar.Total = bar.Current
		}
		bar.Text = fmt.Sprintf("%d of %d secrets checked", bar.Current, bar.Total)
		bar.LazyPrint()

		if i == len(secrets) {
			break
		}
	}
	fmt.Fprintln(goprogressbar.Stdout) // Print empty line after the progressbar.

	return auditPrintResults(ctx, duplicates, messages, errors)
}

func audit(ctx context.Context, secStore secretGetter, validator *crunchy.Validator, secrets <-chan string, checked chan<- auditedSecret, done chan struct{}) {
	for secret := range secrets {
		as := auditedSecret{
			name: secret,
		}
		// check for context cancelation
		select {
		case <-ctx.Done():
			as.err = errors.New("user aborted")
			checked <- as
			continue
		default:
		}

		debug.Log("Checking %s", secret)
		sec, err := secStore.Get(ctx, secret)
		if err != nil {
			as.err = err
			if sec != nil {
				as.content = sec.Password()
			}
			// failed to properly retrieve the secret
			checked <- as
			continue
		}

		as.content = sec.Password()

		// do not check binary secrets
		if as.content == "" || strings.HasSuffix(secret, ".b64") {
			checked <- as
			continue
		}

		// handle password validation errors
		if err := validator.Check(as.content); err != nil {
			as.messages = append(as.messages, err.Error())
			checked <- as
			continue
		}

		// handle old passwords
		revs, err := secStore.ListRevisions(ctx, secret)
		if err != nil {
			as.messages = append(as.messages, err.Error())
		} else {
			if len(revs) > 0 && time.Since(revs[0].Date) > 90*24*time.Hour {
				as.messages = append(as.messages, "Password too old (90d)")
			}
		}

		// record every password for possible duplicates
		checked <- as
	}
	done <- struct{}{}
}

func printAuditResults(m map[string][]string, format string, color func(format string, a ...interface{}) string) bool {
	b := false

	for msg, secrets := range m {
		b = true
		fmt.Fprint(goprogressbar.Stdout, color(format, msg))
		for _, secret := range secrets {
			fmt.Fprint(goprogressbar.Stdout, color("\t- %s\n", secret))
		}
	}

	return b
}

// Single runs a password strength audit on a single password
func Single(ctx context.Context, password string) {
	validator := crunchy.NewValidator()
	if err := validator.Check(password); err != nil {
		out.Cyan(ctx, fmt.Sprintf("Warning: %s", err))
	}
}

func auditPrintResults(ctx context.Context, duplicates, messages, errors map[string][]string) error {
	foundDuplicates := false
	for _, secrets := range duplicates {
		if len(secrets) > 1 {
			foundDuplicates = true

			out.Cyan(ctx, "Detected a shared secret for:")
			for _, secret := range secrets {
				out.Cyan(ctx, "\t- %s", secret)
			}
		}
	}
	if !foundDuplicates {
		out.Green(ctx, "No shared secrets found.")
	}

	foundWeakPasswords := printAuditResults(messages, "%s:\n", color.CyanString)
	if !foundWeakPasswords {
		out.Green(ctx, "No weak secrets detected.")
	}
	foundErrors := printAuditResults(errors, "%s:\n", color.RedString)

	if foundWeakPasswords || foundDuplicates || foundErrors {
		_ = notify.Notify(ctx, "gopass - audit", "Finished. Found weak passwords and/or duplicates")
		return fmt.Errorf("found weak passwords or duplicates")
	}

	_ = notify.Notify(ctx, "gopass - audit", "Finished. No weak passwords or duplicates found!")
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go 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 queue provides queue implementations that can be used for
// asynchronous scheduling of fetch actions.
package queue

import (
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"io"
	"math"
	"strings"
	"time"

	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
	"github.com/golang/protobuf/ptypes"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// A Queue provides an interface for asynchronous scheduling of fetch actions.
type Queue interface {
	ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (bool, error)
}

// New creates a new Queue with name queueName based on the configuration
// in cfg. When running locally, Queue uses numWorkers concurrent workers.
func New(ctx context.Context, cfg *config.Config, queueName string, numWorkers int, expGetter middleware.ExperimentGetter, processFunc inMemoryProcessFunc) (Queue, error) {
	if !cfg.OnGCP() {
		experiments, err := expGetter(ctx)
		if err != nil {
			return nil, err
		}
		var names []string
		for _, e := range experiments {
			if e.Rollout > 0 {
				names = append(names, e.Name)
			}
		}
		return NewInMemory(ctx, numWorkers, names, processFunc), nil
	}

	client, err := cloudtasks.NewClient(ctx)
	if err != nil {
		return nil, err
	}
	g, err := newGCP(cfg, client, queueName)
	if err != nil {
		return nil, err
	}
	log.Infof(ctx, "enqueuing at %s with queueService=%q, queueURL=%q", g.queueName, g.queueService, g.queueURL)
	return g, nil
}

// GCP provides a Queue implementation backed by the Google Cloud Tasks
// API.
type GCP struct {
	client       *cloudtasks.Client
	queueName    string // full GCP name of the queue
	queueService string // AppEngine service to post tasks to
	queueURL     string // non-AppEngine URL to post tasks to
	// token holds information that lets the task queue construct an authorized request to the worker.
	// Since the worker sits behind the IAP, the queue needs an identity token that includes the
	// identity of a service account that has access, and the client ID for the IAP.
	// We use the service account of the current process.
	token *taskspb.HttpRequest_OidcToken
}

// NewGCP returns a new Queue that can be used to enqueue tasks using the
// cloud tasks API.  The given queueID should be the name of the queue in the
// cloud tasks console.
func newGCP(cfg *config.Config, client *cloudtasks.Client, queueID string) (_ *GCP, err error) {
	defer derrors.Wrap(&err, "newGCP(cfg, client, %q)", queueID)
	if queueID == "" {
		return nil, errors.New("empty queueID")
	}
	if cfg.ProjectID == "" {
		return nil, errors.New("empty ProjectID")
	}
	if cfg.LocationID == "" {
		return nil, errors.New("empty LocationID")
	}
	if cfg.QueueService == "" && cfg.QueueURL == "" {
		return nil, errors.New("both QueueService and QueueURL are empty")
	}
	if cfg.QueueService != "" && cfg.QueueURL != "" {
		return nil, errors.New("both  QueueService and QueueURL are non-empty")
	}
	if cfg.OnAppEngine() && cfg.QueueService == "" {
		return nil, errors.New("on AppEngine, but QueueService is empty")
	}
	if cfg.QueueURL != "" {
		if cfg.ServiceAccount == "" {
			return nil, errors.New("need ServiceAccount with QueueURL")
		}
		if cfg.QueueAudience == "" {
			return nil, errors.New("need QueueAudience with QueueURL")
		}
	}
	return &GCP{
		client:       client,
		queueName:    fmt.Sprintf("projects/%s/locations/%s/queues/%s", cfg.ProjectID, cfg.LocationID, queueID),
		queueService: cfg.QueueService,
		queueURL:     cfg.QueueURL,
		token: &taskspb.HttpRequest_OidcToken{
			OidcToken: &taskspb.OidcToken{
				ServiceAccountEmail: cfg.ServiceAccount,
				Audience:            cfg.QueueAudience,
			},
		},
	}, nil
}

// ScheduleFetch enqueues a task on GCP to fetch the given modulePath and
// version. It returns an error if there was an error hashing the task name, or
// an error pushing the task to GCP. If the task was a duplicate, it returns (false, nil).
func (q *GCP) ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (enqueued bool, err error) {
	// the new taskqueue API requires a deadline of <= 30s
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
	defer derrors.Wrap(&err, "queue.ScheduleFetch(%q, %q, %q)", modulePath, version, suffix)

	req := q.newTaskRequest(modulePath, version, suffix)
	enqueued = true
	if _, err := q.client.CreateTask(ctx, req); err != nil {
		if status.Code(err) == codes.AlreadyExists {
			log.Debugf(ctx, "ignoring duplicate task ID %s: %s@%s", req.Task.Name, modulePath, version)
			enqueued = false
		} else {
			return false, fmt.Errorf("q.client.CreateTask(ctx, req): %v", err)
		}
	}
	return enqueued, nil
}

// Maximum timeout for HTTP tasks.
// See https://cloud.google.com/tasks/docs/creating-http-target-tasks.
const maxCloudTasksTimeout = 30 * time.Minute

func (q *GCP) newTaskRequest(modulePath, version, suffix string) *taskspb.CreateTaskRequest {
	taskID := newTaskID(modulePath, version)
	relativeURI := fmt.Sprintf("/fetch/%s/@v/%s", modulePath, version)
	task := &taskspb.Task{
		Name:             fmt.Sprintf("%s/tasks/%s", q.queueName, taskID),
		DispatchDeadline: ptypes.DurationProto(maxCloudTasksTimeout),
	}
	if q.queueService != "" {
		task.MessageType = &taskspb.Task_AppEngineHttpRequest{
			AppEngineHttpRequest: &taskspb.AppEngineHttpRequest{
				HttpMethod:  taskspb.HttpMethod_POST,
				RelativeUri: relativeURI,
				AppEngineRouting: &taskspb.AppEngineRouting{
					Service: q.queueService,
				},
			},
		}
	} else {
		task.MessageType = &taskspb.Task_HttpRequest{
			HttpRequest: &taskspb.HttpRequest{
				HttpMethod:          taskspb.HttpMethod_POST,
				Url:                 q.queueURL + relativeURI,
				AuthorizationHeader: q.token,
			},
		}
	}
	req := &taskspb.CreateTaskRequest{
		Parent: q.queueName,
		Task:   task,
	}
	// If suffix is non-empty, append it to the task name. This lets us force reprocessing
	// of tasks that would normally be de-duplicated.
	if suffix != "" {
		req.Task.Name += "-" + suffix
	}
	return req
}

// Create a task ID for the given module path and version.
// Task IDs can contain only letters ([A-Za-z]), numbers ([0-9]), hyphens (-), or underscores (_).
func newTaskID(modulePath, version string) string {
	mv := modulePath + "@" + version
	// Compute a hash to use as a prefix, so the task IDs are distributed uniformly.
	// See https://cloud.google.com/tasks/docs/reference/rpc/google.cloud.tasks.v2#task
	// under "Task De-duplication".
	hasher := fnv.New32()
	io.WriteString(hasher, mv)
	hash := hasher.Sum32() % math.MaxUint16
	// Escape the name so it contains only valid characters. Do our best to make it readable.
	var b strings.Builder
	for _, r := range mv {
		switch {
		case r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '-':
			b.WriteRune(r)
		case r == '_':
			b.WriteString("__")
		case r == '/':
			b.WriteString("_-")
		case r == '@':
			b.WriteString("_v")
		case r == '.':
			b.WriteString("_o")
		default:
			fmt.Fprintf(&b, "_%04x", r)
		}
	}
	return fmt.Sprintf("%04x-%s", hash, &b)
}

type moduleVersion struct {
	modulePath, version string
}

// InMemory is a Queue implementation that schedules in-process fetch
// operations. Unlike the GCP task queue, it will not automatically retry tasks
// on failure.
//
// This should only be used for local development.
type InMemory struct {
	queue       chan moduleVersion
	sem         chan struct{}
	experiments []string
}

type inMemoryProcessFunc func(context.Context, string, string) (int, error)

// NewInMemory creates a new InMemory that asynchronously fetches
// from proxyClient and stores in db. It uses workerCount parallelism to
// execute these fetches.
func NewInMemory(ctx context.Context, workerCount int, experiments []string, processFunc inMemoryProcessFunc) *InMemory {
	q := &InMemory{
		queue:       make(chan moduleVersion, 1000),
		sem:         make(chan struct{}, workerCount),
		experiments: experiments,
	}
	go func() {
		for v := range q.queue {
			select {
			case <-ctx.Done():
				return
			case q.sem <- struct{}{}:
			}

			// If a worker is available, make a request to the fetch service inside a
			// goroutine and wait for it to finish.
			go func(v moduleVersion) {
				defer func() { <-q.sem }()

				log.Infof(ctx, "Fetch requested: %q %q (workerCount = %d)", v.modulePath, v.version, cap(q.sem))

				fetchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
				fetchCtx = experiment.NewContext(fetchCtx, experiments...)
				defer cancel()

				if _, err := processFunc(fetchCtx, v.modulePath, v.version); err != nil {
					log.Error(fetchCtx, err)
				}
			}(v)
		}
	}()
	return q
}

// ScheduleFetch pushes a fetch task into the local queue to be processed
// asynchronously.
func (q *InMemory) ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (bool, error) {
	q.queue <- moduleVersion{modulePath, version}
	return true, nil
}

// WaitForTesting waits for all queued requests to finish. It should only be
// used by test code.
func (q InMemory) WaitForTesting(ctx context.Context) {
	for i := 0; i < cap(q.sem); i++ {
		select {
		case <-ctx.Done():
			return
		case q.sem <- struct{}{}:
		}
	}
	close(q.queue)
}
<commit_msg>internal/queue: allow GAE frontend/GKE worker setup<commit_after>// Copyright 2019 The Go 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 queue provides queue implementations that can be used for
// asynchronous scheduling of fetch actions.
package queue

import (
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"io"
	"math"
	"strings"
	"time"

	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
	"github.com/golang/protobuf/ptypes"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	taskspb "google.golang.org/genproto/googleapis/cloud/tasks/v2"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// A Queue provides an interface for asynchronous scheduling of fetch actions.
type Queue interface {
	ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (bool, error)
}

// New creates a new Queue with name queueName based on the configuration
// in cfg. When running locally, Queue uses numWorkers concurrent workers.
func New(ctx context.Context, cfg *config.Config, queueName string, numWorkers int, expGetter middleware.ExperimentGetter, processFunc inMemoryProcessFunc) (Queue, error) {
	if !cfg.OnGCP() {
		experiments, err := expGetter(ctx)
		if err != nil {
			return nil, err
		}
		var names []string
		for _, e := range experiments {
			if e.Rollout > 0 {
				names = append(names, e.Name)
			}
		}
		return NewInMemory(ctx, numWorkers, names, processFunc), nil
	}

	client, err := cloudtasks.NewClient(ctx)
	if err != nil {
		return nil, err
	}
	g, err := newGCP(cfg, client, queueName)
	if err != nil {
		return nil, err
	}
	log.Infof(ctx, "enqueuing at %s with queueService=%q, queueURL=%q", g.queueName, g.queueService, g.queueURL)
	return g, nil
}

// GCP provides a Queue implementation backed by the Google Cloud Tasks
// API.
type GCP struct {
	client       *cloudtasks.Client
	queueName    string // full GCP name of the queue
	queueService string // AppEngine service to post tasks to
	queueURL     string // non-AppEngine URL to post tasks to
	// token holds information that lets the task queue construct an authorized request to the worker.
	// Since the worker sits behind the IAP, the queue needs an identity token that includes the
	// identity of a service account that has access, and the client ID for the IAP.
	// We use the service account of the current process.
	token *taskspb.HttpRequest_OidcToken
}

// NewGCP returns a new Queue that can be used to enqueue tasks using the
// cloud tasks API.  The given queueID should be the name of the queue in the
// cloud tasks console.
func newGCP(cfg *config.Config, client *cloudtasks.Client, queueID string) (_ *GCP, err error) {
	defer derrors.Wrap(&err, "newGCP(cfg, client, %q)", queueID)
	if queueID == "" {
		return nil, errors.New("empty queueID")
	}
	if cfg.ProjectID == "" {
		return nil, errors.New("empty ProjectID")
	}
	if cfg.LocationID == "" {
		return nil, errors.New("empty LocationID")
	}
	if cfg.QueueService == "" && cfg.QueueURL == "" {
		return nil, errors.New("both QueueService and QueueURL are empty")
	}
	if cfg.QueueService != "" && cfg.QueueURL != "" {
		return nil, errors.New("both  QueueService and QueueURL are non-empty")
	}
	if cfg.QueueURL != "" {
		if cfg.ServiceAccount == "" {
			return nil, errors.New("need ServiceAccount with QueueURL")
		}
		if cfg.QueueAudience == "" {
			return nil, errors.New("need QueueAudience with QueueURL")
		}
	}
	return &GCP{
		client:       client,
		queueName:    fmt.Sprintf("projects/%s/locations/%s/queues/%s", cfg.ProjectID, cfg.LocationID, queueID),
		queueService: cfg.QueueService,
		queueURL:     cfg.QueueURL,
		token: &taskspb.HttpRequest_OidcToken{
			OidcToken: &taskspb.OidcToken{
				ServiceAccountEmail: cfg.ServiceAccount,
				Audience:            cfg.QueueAudience,
			},
		},
	}, nil
}

// ScheduleFetch enqueues a task on GCP to fetch the given modulePath and
// version. It returns an error if there was an error hashing the task name, or
// an error pushing the task to GCP. If the task was a duplicate, it returns (false, nil).
func (q *GCP) ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (enqueued bool, err error) {
	// the new taskqueue API requires a deadline of <= 30s
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
	defer derrors.Wrap(&err, "queue.ScheduleFetch(%q, %q, %q)", modulePath, version, suffix)

	req := q.newTaskRequest(modulePath, version, suffix)
	enqueued = true
	if _, err := q.client.CreateTask(ctx, req); err != nil {
		if status.Code(err) == codes.AlreadyExists {
			log.Debugf(ctx, "ignoring duplicate task ID %s: %s@%s", req.Task.Name, modulePath, version)
			enqueued = false
		} else {
			return false, fmt.Errorf("q.client.CreateTask(ctx, req): %v", err)
		}
	}
	return enqueued, nil
}

// Maximum timeout for HTTP tasks.
// See https://cloud.google.com/tasks/docs/creating-http-target-tasks.
const maxCloudTasksTimeout = 30 * time.Minute

func (q *GCP) newTaskRequest(modulePath, version, suffix string) *taskspb.CreateTaskRequest {
	taskID := newTaskID(modulePath, version)
	relativeURI := fmt.Sprintf("/fetch/%s/@v/%s", modulePath, version)
	task := &taskspb.Task{
		Name:             fmt.Sprintf("%s/tasks/%s", q.queueName, taskID),
		DispatchDeadline: ptypes.DurationProto(maxCloudTasksTimeout),
	}
	if q.queueService != "" {
		task.MessageType = &taskspb.Task_AppEngineHttpRequest{
			AppEngineHttpRequest: &taskspb.AppEngineHttpRequest{
				HttpMethod:  taskspb.HttpMethod_POST,
				RelativeUri: relativeURI,
				AppEngineRouting: &taskspb.AppEngineRouting{
					Service: q.queueService,
				},
			},
		}
	} else {
		task.MessageType = &taskspb.Task_HttpRequest{
			HttpRequest: &taskspb.HttpRequest{
				HttpMethod:          taskspb.HttpMethod_POST,
				Url:                 q.queueURL + relativeURI,
				AuthorizationHeader: q.token,
			},
		}
	}
	req := &taskspb.CreateTaskRequest{
		Parent: q.queueName,
		Task:   task,
	}
	// If suffix is non-empty, append it to the task name. This lets us force reprocessing
	// of tasks that would normally be de-duplicated.
	if suffix != "" {
		req.Task.Name += "-" + suffix
	}
	return req
}

// Create a task ID for the given module path and version.
// Task IDs can contain only letters ([A-Za-z]), numbers ([0-9]), hyphens (-), or underscores (_).
func newTaskID(modulePath, version string) string {
	mv := modulePath + "@" + version
	// Compute a hash to use as a prefix, so the task IDs are distributed uniformly.
	// See https://cloud.google.com/tasks/docs/reference/rpc/google.cloud.tasks.v2#task
	// under "Task De-duplication".
	hasher := fnv.New32()
	io.WriteString(hasher, mv)
	hash := hasher.Sum32() % math.MaxUint16
	// Escape the name so it contains only valid characters. Do our best to make it readable.
	var b strings.Builder
	for _, r := range mv {
		switch {
		case r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '-':
			b.WriteRune(r)
		case r == '_':
			b.WriteString("__")
		case r == '/':
			b.WriteString("_-")
		case r == '@':
			b.WriteString("_v")
		case r == '.':
			b.WriteString("_o")
		default:
			fmt.Fprintf(&b, "_%04x", r)
		}
	}
	return fmt.Sprintf("%04x-%s", hash, &b)
}

type moduleVersion struct {
	modulePath, version string
}

// InMemory is a Queue implementation that schedules in-process fetch
// operations. Unlike the GCP task queue, it will not automatically retry tasks
// on failure.
//
// This should only be used for local development.
type InMemory struct {
	queue       chan moduleVersion
	sem         chan struct{}
	experiments []string
}

type inMemoryProcessFunc func(context.Context, string, string) (int, error)

// NewInMemory creates a new InMemory that asynchronously fetches
// from proxyClient and stores in db. It uses workerCount parallelism to
// execute these fetches.
func NewInMemory(ctx context.Context, workerCount int, experiments []string, processFunc inMemoryProcessFunc) *InMemory {
	q := &InMemory{
		queue:       make(chan moduleVersion, 1000),
		sem:         make(chan struct{}, workerCount),
		experiments: experiments,
	}
	go func() {
		for v := range q.queue {
			select {
			case <-ctx.Done():
				return
			case q.sem <- struct{}{}:
			}

			// If a worker is available, make a request to the fetch service inside a
			// goroutine and wait for it to finish.
			go func(v moduleVersion) {
				defer func() { <-q.sem }()

				log.Infof(ctx, "Fetch requested: %q %q (workerCount = %d)", v.modulePath, v.version, cap(q.sem))

				fetchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
				fetchCtx = experiment.NewContext(fetchCtx, experiments...)
				defer cancel()

				if _, err := processFunc(fetchCtx, v.modulePath, v.version); err != nil {
					log.Error(fetchCtx, err)
				}
			}(v)
		}
	}()
	return q
}

// ScheduleFetch pushes a fetch task into the local queue to be processed
// asynchronously.
func (q *InMemory) ScheduleFetch(ctx context.Context, modulePath, version, suffix string) (bool, error) {
	q.queue <- moduleVersion{modulePath, version}
	return true, nil
}

// WaitForTesting waits for all queued requests to finish. It should only be
// used by test code.
func (q InMemory) WaitForTesting(ctx context.Context) {
	for i := 0; i < cap(q.sem); i++ {
		select {
		case <-ctx.Done():
			return
		case q.sem <- struct{}{}:
		}
	}
	close(q.queue)
}
<|endoftext|>


<commit_before>package redis

import "fmt"

func (c *Client) Hdel(key, field string, fields ...string) error {
	_, err := c.exec("HDEL", append([]string{key, field}, fields...)...)
	if err != nil {
		return err
	}
	// TODO(ttacon): should check resp, pretty sure it returns an int
	return nil
}

func (c *Client) Hexists(key, field string) (bool, error) {
	resp, err := c.exec("HEXISTS", key, field)
	if err != nil {
		return false, err
	}
	return c.boolResp(resp)
}

func (c *Client) Hget(key, field string) (string, error) {
	resp, err := c.exec("HGET", key, field)
	if err != nil {
		return "", err
	}
	return c.stringResp(resp)
}

func (c *Client) Hgetall(key string) ([]string, error) {
	resp, err := c.exec("HGETALL", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hincrby(key, field string, increment int) (int64, error) {
	resp, err := c.exec("HINCRBY", key, field, fmt.Sprintf("%d", increment))
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) Hincrbyfloat(key, field string, increment float64) error {
	// TODO(ttacon): use strconv.FormatFloat, also return float64
	_, err := c.exec("HINCRBYFLOAT", key, field, fmt.Sprintf("%f", increment))
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) Hkeys(key string) ([]string, error) {
	resp, err := c.exec("HKEYS", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hlen(key string) (int64, error) {
	resp, err := c.exec("HLEN", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) Hmget(key, field string, fields ...string) ([]string, error) {
	resp, err := c.exec("HMGET", append([]string{field}, fields...)...)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hmset(key string, values map[string]string) error {
	_, err := c.exec("HMSET", append([]string{key}, mapToStrSlice(values)...)...)
	if err != nil {
		return err
	}
	return nil
}

func mapToStrSlice(mp map[string]string) []string {
	var (
		vals = make([]string, len(mp)*2)
		i    = 0
	)

	for k, v := range mp {
		vals[i], vals[i+1] = k, v
		i += 2
	}
	return vals
}

func (c *Client) Hset(key, field, value string) error {
	// TODO(ttacon): allow int's on their own as well?
	_, err := c.exec("HSET", field, value)
	if err != nil {
		return err
	}
	// TODO(ttacon): check resp 0 vs 1
	return nil
}

func (c *Client) HsetNx(key, field string, value interface{}) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Hvals(key string) ([]string, error) {
	resp, err := c.exec("HVALS", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

// TODO(ttacon): set correct function signature
func (c *Client) Hscan(key string) error {
	// TODO(ttacon): ✔
	return nil
}
<commit_msg>return number of items returned from HDel<commit_after>package redis

import "fmt"

func (c *Client) Hdel(key, field string, fields ...string) (int, error) {
	resp, err := c.exec("HDEL", append([]string{key, field}, fields...)...)
	if err != nil {
		return 0, err
	}
	val, err := c.intResp(resp)
	return int(val), err
}

func (c *Client) Hexists(key, field string) (bool, error) {
	resp, err := c.exec("HEXISTS", key, field)
	if err != nil {
		return false, err
	}
	return c.boolResp(resp)
}

func (c *Client) Hget(key, field string) (string, error) {
	resp, err := c.exec("HGET", key, field)
	if err != nil {
		return "", err
	}
	return c.stringResp(resp)
}

func (c *Client) Hgetall(key string) ([]string, error) {
	resp, err := c.exec("HGETALL", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hincrby(key, field string, increment int) (int64, error) {
	resp, err := c.exec("HINCRBY", key, field, fmt.Sprintf("%d", increment))
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) Hincrbyfloat(key, field string, increment float64) error {
	// TODO(ttacon): use strconv.FormatFloat, also return float64
	_, err := c.exec("HINCRBYFLOAT", key, field, fmt.Sprintf("%f", increment))
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) Hkeys(key string) ([]string, error) {
	resp, err := c.exec("HKEYS", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hlen(key string) (int64, error) {
	resp, err := c.exec("HLEN", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) Hmget(key, field string, fields ...string) ([]string, error) {
	resp, err := c.exec("HMGET", append([]string{field}, fields...)...)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) Hmset(key string, values map[string]string) error {
	_, err := c.exec("HMSET", append([]string{key}, mapToStrSlice(values)...)...)
	if err != nil {
		return err
	}
	return nil
}

func mapToStrSlice(mp map[string]string) []string {
	var (
		vals = make([]string, len(mp)*2)
		i    = 0
	)

	for k, v := range mp {
		vals[i], vals[i+1] = k, v
		i += 2
	}
	return vals
}

func (c *Client) Hset(key, field, value string) error {
	// TODO(ttacon): allow int's on their own as well?
	_, err := c.exec("HSET", field, value)
	if err != nil {
		return err
	}
	// TODO(ttacon): check resp 0 vs 1
	return nil
}

func (c *Client) HsetNx(key, field string, value interface{}) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Hvals(key string) ([]string, error) {
	resp, err := c.exec("HVALS", key)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

// TODO(ttacon): set correct function signature
func (c *Client) Hscan(key string) error {
	// TODO(ttacon): ✔
	return nil
}
<|endoftext|>


<commit_before>// +build !windows

package main

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/sevlyar/go-daemon"
	"os"
	"strconv"
	"syscall"
	"path/filepath"
)

type User struct {
	Name           string
	HomeDir        string
	UserId         uint32
	DefaultGroupId uint32
}

// return true if it is child process
func daemonize() bool {

	daemonContext := &daemon.Context{}

	if *runAs != "" {
		userName := *runAs
		user, err := userLookup(userName)
		if err != nil {
			logrus.Fatalf("Can't lookup runas user '%v': %v", userName, err)
		}

		logrus.Infof("Parse runas '%v' as %v:%v", *runAs, user.UserId, user.DefaultGroupId)
		daemonContext.Credential = &syscall.Credential{
			Uid: user.UserId,
			Gid: user.DefaultGroupId,
		}
		daemonContext.WorkDir = user.HomeDir
	}

	if *workingDir != "" {
		daemonContext.WorkDir = *workingDir
	}
	logrus.Infof("Daemon working dir: %v", daemonContext.WorkDir)

	if *pidFilePath != "" {
		pidPath := *pidFilePath
		if !filepath.IsAbs(pidPath) && daemonContext.WorkDir != "" {
			pidPath = filepath.Join(daemonContext.WorkDir, pidPath)
		}
		daemonContext.PidFileName = pidPath
		logrus.Infof("Pidfile: %v", daemonContext.PidFileName)
	}

	child, err := daemonContext.Reborn()
	if err != nil {
		logrus.Fatalf("Can't start daemon process: %v", err)
	}

	if child == nil {
		logrus.Info("Start as daemon child")
		return true
	} else {
		logrus.Info("Start as daemons parent")
		return false
	}
}

// "os/user".Lookup need cgo and can't used when binary is cross-compiled.
// use own lookup
func userLookup(login string) (*User, error) {
	var isId bool = false
	_, err := parseUint32(login)
	if err == nil {
		isId = true
	}

	loginBytes := []byte(login)

	passwdFile, err := os.Open("/etc/passwd")
	if passwdFile != nil {
		defer passwdFile.Close()
	}
	if err != nil {
		return nil, err
	}

	splitBytes := []byte(":")
	scanner := bufio.NewScanner(passwdFile)
	var userLine []byte
	var userLineParts [][]byte
	for scanner.Scan() {
		line := scanner.Bytes()
		lineParts := bytes.SplitN(line, splitBytes, 7)
		if len(lineParts) < 6 {
			logrus.Warnf("Short passwd line '%s'", line)
			continue
		}

		if isId && bytes.Equal(lineParts[2], loginBytes) ||
			!isId && bytes.Equal(lineParts[0], loginBytes) {
			userLine = line
			userLineParts = lineParts
			break
		}
	}

	if scanner.Err() != nil {
		return nil, err
	}
	if userLineParts == nil {
		return nil, errors.New("User not found")
	}

	user := User{
		Name:    string(userLineParts[0]),
		HomeDir: string(userLineParts[5]),
	}

	user.UserId, err = parseUint32(string(userLineParts[2]))
	if err != nil {
		logrus.Errorf("Can't parse user id '%s' from passwd line '%s': %v", userLineParts[2], userLine, err)
		return nil, errors.New("Can't parse user id")
	}

	user.DefaultGroupId, err = parseUint32(string(userLineParts[3]))
	if err != nil {
		logrus.Errorf("Can't parse users group id '%s' from passwd line '%s': %v", userLineParts[3], userLine, err)
		return nil, errors.New("Can't parse group id")
	}

	return &user, nil
}

func parseUint32(s string) (uint32, error) {
	res, err := strconv.ParseUint(s, 10, 32)
	if err == nil {
		return uint32(res), nil
	} else {
		return 0, err
	}
}
<commit_msg>prevent unlock pid-file<commit_after>// +build !windows

package main

import (
	"bufio"
	"bytes"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/sevlyar/go-daemon"
	"os"
	"strconv"
	"syscall"
	"path/filepath"
)

type User struct {
	Name           string
	HomeDir        string
	UserId         uint32
	DefaultGroupId uint32
}

var (
	daemonContext *daemon.Context // need global var for prevent close (and unlock) pid-file
)
// return true if it is child process
func daemonize() bool {

	daemonContext = &daemon.Context{}

	if *runAs != "" {
		userName := *runAs
		user, err := userLookup(userName)
		if err != nil {
			logrus.Fatalf("Can't lookup runas user '%v': %v", userName, err)
		}

		logrus.Infof("Parse runas '%v' as %v:%v", *runAs, user.UserId, user.DefaultGroupId)
		daemonContext.Credential = &syscall.Credential{
			Uid: user.UserId,
			Gid: user.DefaultGroupId,
		}
		daemonContext.WorkDir = user.HomeDir
	}

	if *workingDir != "" {
		daemonContext.WorkDir = *workingDir
	}
	logrus.Infof("Daemon working dir: %v", daemonContext.WorkDir)

	if *pidFilePath != "" {
		pidPath := *pidFilePath
		if !filepath.IsAbs(pidPath) && daemonContext.WorkDir != "" {
			pidPath = filepath.Join(daemonContext.WorkDir, pidPath)
		}
		daemonContext.PidFileName = pidPath
		logrus.Infof("Pidfile: %v", daemonContext.PidFileName)
	}

	child, err := daemonContext.Reborn()
	if err != nil {
		logrus.Fatalf("Can't start daemon process: %v", err)
	}

	if child == nil {
		logrus.Info("Start as daemon child")
		return true
	} else {
		logrus.Info("Start as daemons parent")
		return false
	}
}

// "os/user".Lookup need cgo and can't used when binary is cross-compiled.
// use own lookup
func userLookup(login string) (*User, error) {
	var isId bool = false
	_, err := parseUint32(login)
	if err == nil {
		isId = true
	}

	loginBytes := []byte(login)

	passwdFile, err := os.Open("/etc/passwd")
	if passwdFile != nil {
		defer passwdFile.Close()
	}
	if err != nil {
		return nil, err
	}

	splitBytes := []byte(":")
	scanner := bufio.NewScanner(passwdFile)
	var userLine []byte
	var userLineParts [][]byte
	for scanner.Scan() {
		line := scanner.Bytes()
		lineParts := bytes.SplitN(line, splitBytes, 7)
		if len(lineParts) < 6 {
			logrus.Warnf("Short passwd line '%s'", line)
			continue
		}

		if isId && bytes.Equal(lineParts[2], loginBytes) ||
			!isId && bytes.Equal(lineParts[0], loginBytes) {
			userLine = line
			userLineParts = lineParts
			break
		}
	}

	if scanner.Err() != nil {
		return nil, err
	}
	if userLineParts == nil {
		return nil, errors.New("User not found")
	}

	user := User{
		Name:    string(userLineParts[0]),
		HomeDir: string(userLineParts[5]),
	}

	user.UserId, err = parseUint32(string(userLineParts[2]))
	if err != nil {
		logrus.Errorf("Can't parse user id '%s' from passwd line '%s': %v", userLineParts[2], userLine, err)
		return nil, errors.New("Can't parse user id")
	}

	user.DefaultGroupId, err = parseUint32(string(userLineParts[3]))
	if err != nil {
		logrus.Errorf("Can't parse users group id '%s' from passwd line '%s': %v", userLineParts[3], userLine, err)
		return nil, errors.New("Can't parse group id")
	}

	return &user, nil
}

func parseUint32(s string) (uint32, error) {
	res, err := strconv.ParseUint(s, 10, 32)
	if err == nil {
		return uint32(res), nil
	} else {
		return 0, err
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The go-winapi 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 winapi

const (
	HDF_SORTDOWN = 0x200
	HDF_SORTUP   = 0x400
)

const (
	HDI_FORMAT = 4
)

const (
	HDM_FIRST   = 0x1200
	HDM_GETITEM = HDM_FIRST + 11
	HDM_SETITEM = HDM_FIRST + 12
)

type HDITEM struct {
	Mask       uint32
	Cxy        int32
	PszText    *uint16
	Hbm        HBITMAP
	CchTextMax int32
	Fmt        int32
	LParam     uintptr
	IImage     int32
	IOrder     int32
	Type       uint32
	PvFilter   uintptr
}
<commit_msg>Add HDS_NOSIZING<commit_after>// Copyright 2012 The go-winapi 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 winapi

const (
	HDF_SORTDOWN = 0x200
	HDF_SORTUP   = 0x400
)

const (
	HDI_FORMAT = 4
)

const (
	HDM_FIRST   = 0x1200
	HDM_GETITEM = HDM_FIRST + 11
	HDM_SETITEM = HDM_FIRST + 12
)

const (
	HDS_NOSIZING = 0x0800
)

type HDITEM struct {
	Mask       uint32
	Cxy        int32
	PszText    *uint16
	Hbm        HBITMAP
	CchTextMax int32
	Fmt        int32
	LParam     uintptr
	IImage     int32
	IOrder     int32
	Type       uint32
	PvFilter   uintptr
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"path"
	"regexp"
	"time"

	"github.com/eiblog/utils/logd"
	"github.com/eiblog/utils/uuid"
)

const (
	SUCCESS = iota
	FAIL
)

// encrypt password
func EncryptPasswd(name, pass string) string {
	salt := "%$@w*)("
	h := sha256.New()
	io.WriteString(h, name)
	io.WriteString(h, salt)
	io.WriteString(h, pass)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func VerifyPasswd(origin, name, input string) bool {
	return origin == EncryptPasswd(name, input)
}

func RandUUIDv4() string {
	return uuid.NewV4().String()
}

func ReadDir(dir string, filter func(name string) bool) (files []string) {
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}
	for _, fi := range fis {
		if filter(fi.Name()) {
			continue
		}
		if fi.IsDir() {
			files = append(files, ReadDir(path.Join(dir, fi.Name()), filter)...)
			continue
		}
		files = append(files, path.Join(dir, fi.Name()))
	}
	return
}

func IgnoreHtmlTag(src string) string {
	//去除所有尖括号内的HTML代码
	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllString(src, "")

	//去除换行符
	re, _ = regexp.Compile("\\s{2,}")
	return re.ReplaceAllString(src, "")
}

func PickFirstImage(html string) string {
	re, _ := regexp.Compile(`data-src="(.*?)"`)
	sli := re.FindAllStringSubmatch(html, 1)
	if len(sli) > 0 && len(sli[0]) > 1 {
		return sli[0][1]
	}
	return ""
}

// 2016-10-22T07:03:01
const (
	JUST_NOW    = "几秒前"
	MINUTES_AGO = "%d分钟前"
	HOURS_AGO   = "%d小时前"
	DAYS_AGO    = "%d天前"
	MONTH_AGO   = "%d月前"
	YEARS_AGO   = "%d年前"
)

func ConvertStr(str string) string {
	t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
	if err != nil {
		logd.Error(err, str)
		return JUST_NOW
	}
	now := time.Now()
	year1, month1, day1 := t.Date()
	year2, month2, day2 := now.UTC().Date()
	if y := year2 - year1; y > 0 {
		return fmt.Sprintf(YEARS_AGO, y)
	}
	if m := month2 - month1; m > 0 {
		return fmt.Sprintf(MONTH_AGO, m)
	}
	if d := day2 - day1; d > 0 {
		return fmt.Sprintf(DAYS_AGO, d)
	}
	hour1, minute1, _ := t.Clock()
	hour2, minute2, _ := now.Clock()
	if h := hour2 - hour1; h > 0 {
		return fmt.Sprintf(HOURS_AGO, h)
	}
	if m := minute2 - minute1; m > 0 {
		return fmt.Sprintf(MINUTES_AGO, m)
	}
	return JUST_NOW
}
<commit_msg>fix comments duration<commit_after>package main

import (
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"path"
	"regexp"
	"time"

	"github.com/eiblog/utils/logd"
	"github.com/eiblog/utils/uuid"
)

const (
	SUCCESS = iota
	FAIL
)

// encrypt password
func EncryptPasswd(name, pass string) string {
	salt := "%$@w*)("
	h := sha256.New()
	io.WriteString(h, name)
	io.WriteString(h, salt)
	io.WriteString(h, pass)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func VerifyPasswd(origin, name, input string) bool {
	return origin == EncryptPasswd(name, input)
}

func RandUUIDv4() string {
	return uuid.NewV4().String()
}

func ReadDir(dir string, filter func(name string) bool) (files []string) {
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}
	for _, fi := range fis {
		if filter(fi.Name()) {
			continue
		}
		if fi.IsDir() {
			files = append(files, ReadDir(path.Join(dir, fi.Name()), filter)...)
			continue
		}
		files = append(files, path.Join(dir, fi.Name()))
	}
	return
}

func IgnoreHtmlTag(src string) string {
	//去除所有尖括号内的HTML代码
	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllString(src, "")

	//去除换行符
	re, _ = regexp.Compile("\\s{2,}")
	return re.ReplaceAllString(src, "")
}

func PickFirstImage(html string) string {
	re, _ := regexp.Compile(`data-src="(.*?)"`)
	sli := re.FindAllStringSubmatch(html, 1)
	if len(sli) > 0 && len(sli[0]) > 1 {
		return sli[0][1]
	}
	return ""
}

// 2016-10-22T07:03:01
const (
	JUST_NOW    = "几秒前"
	MINUTES_AGO = "%d分钟前"
	HOURS_AGO   = "%d小时前"
	DAYS_AGO    = "%d天前"
	MONTH_AGO   = "%d月前"
	YEARS_AGO   = "%d年前"
)

func ConvertStr(str string) string {
	t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
	if err != nil {
		logd.Error(err, str)
		return JUST_NOW
	}
	now := time.Now()
	y1, m1, d1 := t.Date()
	y2, m2, d2 := now.UTC().Date()
	h1, mi1, s1 := t.Clock()
	h2, mi2, s2 := now.Clock()
	if y := y2 - y1; y > 1 || m2-m1 >= 0 {
		return fmt.Sprintf(YEARS_AGO, y)
	} else if m := y*12 + int(m2-m1); m > 1 || d2-d1 >= 0 {
		return fmt.Sprintf(MONTH_AGO, m)
	} else if d := m*30 + d2 - d1; d > 1 || h2-h1 >= 0 {
		return fmt.Sprintf(DAYS_AGO, d)
	} else if h := d*24 + h2 - h1; h > 1 || mi2-mi1 >= 0 {
		return fmt.Sprintf(HOURS_AGO, h)
	} else if mi := h*60 + mi2 - mi1; mi > 1 || s2-s1 >= 0 {
		return fmt.Sprintf(MINUTES_AGO, m)
	}
	return JUST_NOW
}
<|endoftext|>


<commit_before>// tests for mschapv2 from https://tools.ietf.org/html/rfc2759#section-9.2
package mschap

import (
	"bytes"
	"fmt"
	"testing"
)

func TestUser2(t *testing.T) {
	user := []byte("User")
	expect := []byte{0x55, 0x73, 0x65, 0x72}

	if bytes.Compare(user, expect) != 0 {
		t.Fatal(fmt.Printf("User bytes wrong. expect=%d found=%d", expect, user))
	}
}

func TestNTPassword2(t *testing.T) {
	ntPass := ntPassword("clientPass")
	expect := []byte{
		0x63, 0x00, 0x6C, 0x00, 0x69,
		0x00, 0x65, 0x00, 0x6E, 0x00,
		0x74, 0x00, 0x50, 0x00, 0x61,
		0x00, 0x73, 0x00, 0x73, 0x00,
	}
	if bytes.Compare(ntPass, expect) != 0 {
		t.Fatal(fmt.Printf("NTPassword bytes wrong. expect=%d found=%d", expect, ntPass))
	}
}

func TestChallengeHash(t *testing.T) {
	userName := []byte("User")
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	expect := []byte{
		0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26,
	}

	hash := challengeHash(peerChallenge, authChallenge, userName)
	if len(hash) != 8 {
		t.Fatal(fmt.Printf("ChallengeHash did not return 8 octets, received=%v", hash))
	}
	if bytes.Compare(hash, expect) != 0 {
		t.Fatal(fmt.Printf("ChallengeHash bytes wrong. expect=%d found=%d", expect, hash))
	}
}

func TestEncryptMSCHAP2(t *testing.T) {
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	res, e := Encryptv2(authChallenge, peerChallenge, "User", "clientPass")
	if e != nil {
		t.Fatal(e)
	}

	expect := []byte{
		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF,
	}
	if bytes.Compare(res.ChallengeResponse, expect) != 0 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP2 bytes wrong. expect=%d found=%d", expect, res))
	}

	if len(res.AuthenticatorResponse) != 42 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP2 authRes not 42-octets, found=%d", res.AuthenticatorResponse))
	}
}

func TesthashNtPasswordHash2(t *testing.T) {
	hash := []byte{
		0x63, 0x00, 0x6C, 0x00, 0x69,
		0x00, 0x65, 0x00, 0x6E, 0x00,
		0x74, 0x00, 0x50, 0x00, 0x61,
		0x00, 0x73, 0x00, 0x73, 0x00,
	}
	expect := []byte{
		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F,
	}

	res := hashNtPasswordHash(hash)
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TesthashNtPasswordHash2 bytes wrong. expect=%d found=%d", expect, res))
	}
}

func TestAuthResponse2(t *testing.T) {
	ntResponse := []byte{
		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}

	expect := "S=407A5589115FD0D6209F510FE9C04566932CDA56"
	res := authResponse("clientPass", ntResponse, peerChallenge, authChallenge, "User")
	if res != expect {
		t.Fatal(fmt.Printf("TestAuthResponse2 res wrong. expect=%s found=%s", expect, res))
	}
}
<commit_msg>Bugfix. Printf format %d has arg of wrong type<commit_after>// tests for mschapv2 from https://tools.ietf.org/html/rfc2759#section-9.2
package mschap

import (
	"bytes"
	"fmt"
	"testing"
)

func TestUser2(t *testing.T) {
	user := []byte("User")
	expect := []byte{0x55, 0x73, 0x65, 0x72}

	if bytes.Compare(user, expect) != 0 {
		t.Fatal(fmt.Printf("User bytes wrong. expect=%d found=%d", expect, user))
	}
}

func TestNTPassword2(t *testing.T) {
	ntPass := ntPassword("clientPass")
	expect := []byte{
		0x63, 0x00, 0x6C, 0x00, 0x69,
		0x00, 0x65, 0x00, 0x6E, 0x00,
		0x74, 0x00, 0x50, 0x00, 0x61,
		0x00, 0x73, 0x00, 0x73, 0x00,
	}
	if bytes.Compare(ntPass, expect) != 0 {
		t.Fatal(fmt.Printf("NTPassword bytes wrong. expect=%d found=%d", expect, ntPass))
	}
}

func TestChallengeHash(t *testing.T) {
	userName := []byte("User")
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	expect := []byte{
		0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26,
	}

	hash := challengeHash(peerChallenge, authChallenge, userName)
	if len(hash) != 8 {
		t.Fatal(fmt.Printf("ChallengeHash did not return 8 octets, received=%v", hash))
	}
	if bytes.Compare(hash, expect) != 0 {
		t.Fatal(fmt.Printf("ChallengeHash bytes wrong. expect=%d found=%d", expect, hash))
	}
}

func TestEncryptMSCHAP2(t *testing.T) {
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	res, e := Encryptv2(authChallenge, peerChallenge, "User", "clientPass")
	if e != nil {
		t.Fatal(e)
	}

	expect := []byte{
		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF,
	}
	if bytes.Compare(res.ChallengeResponse, expect) != 0 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP2 bytes wrong. expect=%d found=%d", expect, res.ChallengeResponse))
	}

	if len(res.AuthenticatorResponse) != 42 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP2 authRes not 42-octets, found=%v", res.AuthenticatorResponse))
	}
}

func TesthashNtPasswordHash2(t *testing.T) {
	hash := []byte{
		0x63, 0x00, 0x6C, 0x00, 0x69,
		0x00, 0x65, 0x00, 0x6E, 0x00,
		0x74, 0x00, 0x50, 0x00, 0x61,
		0x00, 0x73, 0x00, 0x73, 0x00,
	}
	expect := []byte{
		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F,
	}

	res := hashNtPasswordHash(hash)
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TesthashNtPasswordHash2 bytes wrong. expect=%d found=%d", expect, res))
	}
}

func TestAuthResponse2(t *testing.T) {
	ntResponse := []byte{
		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF,
	}
	peerChallenge := []byte{
		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E,
	}
	authChallenge := []byte{
		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28,
	}

	expect := "S=407A5589115FD0D6209F510FE9C04566932CDA56"
	res := authResponse("clientPass", ntResponse, peerChallenge, authChallenge, "User")
	if res != expect {
		t.Fatal(fmt.Printf("TestAuthResponse2 res wrong. expect=%s found=%s", expect, res))
	}
}
<|endoftext|>


<commit_before>// Package rest represents a builder for RESTful API
// Copied from https://github.com/browny/goweb-scaffold with Viper and
// facebookgo packages removed as we do not use it now.
// For error handling, refer to:
// https://elithrar.github.io/article/http-handler-error-handling-revisited/
package rest

import (
	"net/http"

	"straas.io/base/logger"

	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
)

// MiddlewareFunc defines middleware for your restful API
type MiddlewareFunc func(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc)

// HandlerFunc handles http requests and returns Error on failure.
type HandlerFunc func(rw http.ResponseWriter, req *http.Request) *Error

// Error is self defined error type
type Error struct {
	Error  error
	Detail string
	Code   int
}

// New creates a new restful API builder
func New(log logger.Logger) Rest {
	return &restImpl{
		router:        httprouter.New(),
		middleware:    negroni.Classic(),
		log:           log,
		routerApplied: false,
	}
}

// Rest is the abstract interface to build restful API
// We will need to expand this interface if we need to use "Route Specific
// Middleware" in negroni or "Named Parameters" in httprouter.
type Rest interface {
	// GetHandler returns a http.Handler that can be passed to http.ListenAndServe
	GetHandler() http.Handler
	// Use registers a middleware function
	Use(fn MiddlewareFunc)
	// Route registers a route handler. When an error occurs, the handler should
	// just return an Error and let this package log and generate http error
	// response for you.
	Route(method, path string, handle HandlerFunc)
}

type restImpl struct {
	router        *httprouter.Router
	middleware    *negroni.Negroni
	log           logger.Logger
	routerApplied bool
}

func (r *restImpl) GetHandler() http.Handler {
	return r.middleware
}

func (r *restImpl) Use(fn MiddlewareFunc) {
	r.middleware.Use(negroni.HandlerFunc(fn))
}

func (r *restImpl) Route(method, path string, fn HandlerFunc) {
	r.router.HandlerFunc(method, path, wrapper(fn, r.log))
	if !r.routerApplied {
		r.middleware.UseHandler(r.router)
		r.routerApplied = true
	}
}

func wrapper(fn HandlerFunc, log logger.Logger) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {
		if restErr := fn(rw, req); restErr != nil {
			if restErr.Detail != "" {
				log.Errorf("error detail: %s", restErr.Detail)
			}
			http.Error(rw, restErr.Error.Error(), restErr.Code)
		}
	}
}
<commit_msg>use sync.Once.Do<commit_after>// Package rest represents a builder for RESTful API
// Copied from https://github.com/browny/goweb-scaffold with Viper and
// facebookgo packages removed as we do not use it now.
// For error handling, refer to:
// https://elithrar.github.io/article/http-handler-error-handling-revisited/
package rest

import (
	"net/http"
	"sync"

	"straas.io/base/logger"

	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
)

// MiddlewareFunc defines middleware for your restful API
type MiddlewareFunc func(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc)

// HandlerFunc handles http requests and returns Error on failure.
type HandlerFunc func(rw http.ResponseWriter, req *http.Request) *Error

// Error is self defined error type
type Error struct {
	Error  error
	Detail string
	Code   int
}

// New creates a new restful API builder
func New(log logger.Logger) Rest {
	return &restImpl{
		router:     httprouter.New(),
		middleware: negroni.Classic(),
		log:        log,
	}
}

// Rest is the abstract interface to build restful API
// We will need to expand this interface if we need to use "Route Specific
// Middleware" in negroni or "Named Parameters" in httprouter.
type Rest interface {
	// GetHandler returns a http.Handler that can be passed to http.ListenAndServe
	GetHandler() http.Handler
	// Use registers a middleware function
	Use(fn MiddlewareFunc)
	// Route registers a route handler. When an error occurs, the handler should
	// just return an Error and let this package log and generate http error
	// response for you.
	Route(method, path string, handle HandlerFunc)
}

type restImpl struct {
	router     *httprouter.Router
	middleware *negroni.Negroni
	log        logger.Logger
	once       sync.Once
}

func (r *restImpl) GetHandler() http.Handler {
	return r.middleware
}

func (r *restImpl) Use(fn MiddlewareFunc) {
	r.middleware.Use(negroni.HandlerFunc(fn))
}

func (r *restImpl) Route(method, path string, fn HandlerFunc) {
	r.router.HandlerFunc(method, path, wrapper(fn, r.log))
	r.once.Do(func() { r.middleware.UseHandler(r.router) })
}

func wrapper(fn HandlerFunc, log logger.Logger) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {
		if restErr := fn(rw, req); restErr != nil {
			if restErr.Detail != "" {
				log.Errorf("error detail: %s", restErr.Detail)
			}
			http.Error(rw, restErr.Error.Error(), restErr.Code)
		}
	}
}
<|endoftext|>


<commit_before>// +build windows

package netfs

import (
	"errors"
)

// Add exports a cifs share
func Add(host, path string) error {
	return errors.New("Adding an export is not yet supported on windows")
}

// Remove removes a cifs share
func Remove(host, path string) error {
	return errors.New("Removing an export is not yet supported on windows")
}

// Exists checks to see if the share already exists
func Exists(host, path string) bool {
	return false
}

// Mount mounts a cifs share on a guest machine
func Mount(host_path, mount_path string, context []string) error {
	return errors.New("Mounting a cifs share is not yet supported on windows")
}
<commit_msg>updated util/netfs/netfs_windows.go to fit new pattern<commit_after>// +build windows

package netfs

import (
	"errors"
)

// Add exports a cifs share
func Add(entry string) error {
	return errors.New("Adding an export is not yet supported on windows")
}

// Remove removes a cifs share
func Remove(entry string) error {
	return errors.New("Removing an export is not yet supported on windows")
}

// Exists checks to see if the share already exists
func Exists(entry string) bool {
	return false
}

// Mount mounts a cifs share on a guest machine
func Mount(host_path, mount_path string, context []string) error {
	return errors.New("Mounting a cifs share is not yet supported on windows")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"

	"github.com/miekg/dns"
)

var dnsServer string
var listen string
var hostsPath string
var blocked = []string{}

func init() {
	flag.StringVar(&dnsServer, "dns-server", "192.168.1.1:domain",
		"DNS server for proxy not blocked queries")
	flag.StringVar(&listen, "listen", ":domain", "Listen is pair 'ip:port'")
	flag.StringVar(&hostsPath, "hosts-path", "hosts", "Path to hosts file")
	flag.Parse()

	log.Printf("Start with: listen: %s; dns-server: %s; hosts-path: %s\n",
		listen, dnsServer, hostsPath)

	loadBlocked()
}

func loadBlocked() {
	file, openErr := os.Open(hostsPath)
	if nil != openErr {
		panic(openErr)
	}
	defer file.Close()
	buffer := bufio.NewReader(file)
	for {
		line, readErr := buffer.ReadString('\n')
		if readErr == io.EOF {
			break
		} else if nil != readErr {
			panic(readErr)
		}

		host := line[0 : len(line)-1]
		blocked = append(blocked, host)
	}
}

func isBlocked(requestMesage *dns.Msg) bool {
	if 1 != len(requestMesage.Question) {
		log.Printf("Can not process message with multiple questions")
		return false
	}

	question := requestMesage.Question[0]
	for _, name := range blocked {
		if dns.Fqdn(name) == question.Name {
			return true
		}
	}
	return false
}

func proxyRequest(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	responseMessage, exchangeErr := dns.Exchange(requestMessage, dnsServer)

	if nil == exchangeErr {
		log.Printf("Response message: %+v\n", responseMessage)
		writeResponse(writer, responseMessage)
	} else {
		log.Printf("Exchange error: %s\n", exchangeErr)

		errorMessage := new(dns.Msg)
		errorMessage.SetRcode(requestMessage, dns.RcodeServerFailure)
		log.Printf("Error message: %+v", errorMessage)

		writeResponse(writer, errorMessage)
	}
}

func blockedRequest(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	responseMessage := new(dns.Msg)
	responseMessage.SetRcode(requestMessage, dns.RcodeRefused)
	log.Printf("Block response: %+v\n", responseMessage)
	writeResponse(writer, responseMessage)
}

func handler(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	log.Printf("Query message: %+v\n", requestMessage)

	if isBlocked(requestMessage) {
		blockedRequest(writer, requestMessage)
	} else {
		proxyRequest(writer, requestMessage)
	}
}

func writeResponse(writer dns.ResponseWriter, message *dns.Msg) {
	if writeErr := writer.WriteMsg(message); nil == writeErr {
		log.Printf("Writer success\n")
	} else {
		log.Printf("Writer error: %s\n", writeErr)
	}
}

func main() {
	server := &dns.Server{
		Addr:    listen,
		Net:     "udp",
		Handler: dns.HandlerFunc(handler),
	}

	if serverErr := server.ListenAndServe(); nil != serverErr {
		log.Fatal(serverErr)
	}
}
<commit_msg>Log count of loaded domains<commit_after>package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"

	"github.com/miekg/dns"
)

var dnsServer string
var listen string
var hostsPath string
var blocked = []string{}

func init() {
	flag.StringVar(&dnsServer, "dns-server", "192.168.1.1:domain",
		"DNS server for proxy not blocked queries")
	flag.StringVar(&listen, "listen", ":domain", "Listen is pair 'ip:port'")
	flag.StringVar(&hostsPath, "hosts-path", "hosts", "Path to hosts file")
	flag.Parse()

	log.Printf("Start with: listen: %s; dns-server: %s; hosts-path: %s\n",
		listen, dnsServer, hostsPath)

	loadBlocked()
}

func loadBlocked() {
	file, openErr := os.Open(hostsPath)
	if nil != openErr {
		panic(openErr)
	}
	defer file.Close()
	buffer := bufio.NewReader(file)
	for {
		line, readErr := buffer.ReadString('\n')
		if readErr == io.EOF {
			break
		} else if nil != readErr {
			panic(readErr)
		}

		host := line[0 : len(line)-1]
		blocked = append(blocked, host)
	}
	log.Printf("Loaded %d domains", len(blocked))
}

func isBlocked(requestMesage *dns.Msg) bool {
	if 1 != len(requestMesage.Question) {
		log.Printf("Can not process message with multiple questions")
		return false
	}

	question := requestMesage.Question[0]
	for _, name := range blocked {
		if dns.Fqdn(name) == question.Name {
			return true
		}
	}
	return false
}

func proxyRequest(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	responseMessage, exchangeErr := dns.Exchange(requestMessage, dnsServer)

	if nil == exchangeErr {
		log.Printf("Response message: %+v\n", responseMessage)
		writeResponse(writer, responseMessage)
	} else {
		log.Printf("Exchange error: %s\n", exchangeErr)

		errorMessage := new(dns.Msg)
		errorMessage.SetRcode(requestMessage, dns.RcodeServerFailure)
		log.Printf("Error message: %+v", errorMessage)

		writeResponse(writer, errorMessage)
	}
}

func blockedRequest(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	responseMessage := new(dns.Msg)
	responseMessage.SetRcode(requestMessage, dns.RcodeRefused)
	log.Printf("Block response: %+v\n", responseMessage)
	writeResponse(writer, responseMessage)
}

func handler(writer dns.ResponseWriter, requestMessage *dns.Msg) {
	log.Printf("Query message: %+v\n", requestMessage)

	if isBlocked(requestMessage) {
		blockedRequest(writer, requestMessage)
	} else {
		proxyRequest(writer, requestMessage)
	}
}

func writeResponse(writer dns.ResponseWriter, message *dns.Msg) {
	if writeErr := writer.WriteMsg(message); nil == writeErr {
		log.Printf("Writer success\n")
	} else {
		log.Printf("Writer error: %s\n", writeErr)
	}
}

func main() {
	server := &dns.Server{
		Addr:    listen,
		Net:     "udp",
		Handler: dns.HandlerFunc(handler),
	}

	if serverErr := server.ListenAndServe(); nil != serverErr {
		log.Fatal(serverErr)
	}
}
<|endoftext|>


<commit_before>package metrics

import (
	"github.com/coredns/coredns/core/dnsserver"

	"github.com/mholt/caddy"
	"github.com/prometheus/client_golang/prometheus"
)

// MustRegister registers the prometheus Collectors when the metrics middleware is used.
func MustRegister(c *caddy.Controller, cs ...prometheus.Collector) {
	m := dnsserver.GetConfig(c).Handler("prometheus")
	if m == nil {
		return
	}
	x, ok := m.(*Metrics)
	if !ok {
		return
	}
	for _, c := range cs {
		x.MustRegister(c)
	}
}
<commit_msg>Rename middleware -> plugin in comment (#2818)<commit_after>package metrics

import (
	"github.com/coredns/coredns/core/dnsserver"

	"github.com/mholt/caddy"
	"github.com/prometheus/client_golang/prometheus"
)

// MustRegister registers the prometheus Collectors when the metrics plugin is used.
func MustRegister(c *caddy.Controller, cs ...prometheus.Collector) {
	m := dnsserver.GetConfig(c).Handler("prometheus")
	if m == nil {
		return
	}
	x, ok := m.(*Metrics)
	if !ok {
		return
	}
	for _, c := range cs {
		x.MustRegister(c)
	}
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// InitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type InitCommand struct {
	Meta
}

func (c *InitCommand) Help() string {
	helpText := `
Usage: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *InitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *InitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file.
# Create a job with ID and Name 'example'
job "example" {
	# Run the job in the global region, which is the default.
	# region = "global"

	# Specify the datacenters within the region this job can run in.
	datacenters = ["dc1"]

	# Service type jobs optimize for long-lived services. This is
	# the default but we can change to batch for short-lived tasks.
	# type = "service"

	# Priority controls our access to resources and scheduling priority.
	# This can be 1 to 100, inclusively, and defaults to 50.
	# priority = 50

	# Restrict our job to only linux. We can specify multiple
	# constraints as needed.
	constraint {
		attribute = "${attr.kernel.name}"
		value = "linux"
	}

	# Configure the job to do rolling updates
	update {
		# Stagger updates every 10 seconds
		stagger = "10s"

		# Update a single task at a time
		max_parallel = 1
	}

	# Create a 'cache' group. Each task in the group will be
	# scheduled onto the same machine.
	group "cache" {
		# Control the number of instances of this groups.
		# Defaults to 1
		# count = 1

		# Configure the restart policy for the task group. If not provided, a
		# default is used based on the job type.
		restart {
			# The number of attempts to run the job within the specified interval.
			attempts = 10
			interval = "5m"

			# A delay between a task failing and a restart occuring.
			delay = "25s"

			# Mode controls what happens when a task has restarted "attempts"
			# times within the interval. "delay" mode delays the next restart
			# till the next interval. "fail" mode does not restart the task if
			# "attempts" has been hit within the interval.
			mode = "delay"
		}

		# Define a task to run
		task "redis" {
			# Use Docker to run the task.
			driver = "docker"

			# Configure Docker driver with the image
			config {
				image = "redis:latest"
				port_map {
					db = 6379
				}
			}

			service {
				name = "${TASKGROUP}-redis"
				tags = ["global", "cache"]
				port = "db"
				check {
					name = "alive"
					type = "tcp"
					interval = "10s"
					timeout = "2s"
				}
			}

			# We must specify the resources required for
			# this task to ensure it runs on a machine with
			# enough capacity.
			resources {
				cpu = 500 # 500 Mhz
				memory = 256 # 256MB
				network {
					mbits = 10
					port "db" {
					}
				}
			}

			# Controls the timeout between signalling a task it will be killed
			# and killing the task. If not set a default is used.
			# kill_timeout = "20s"
		}
	}
}
`)
<commit_msg>Added the log config to init example<commit_after>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// InitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type InitCommand struct {
	Meta
}

func (c *InitCommand) Help() string {
	helpText := `
Usage: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *InitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *InitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file.
# Create a job with ID and Name 'example'
job "example" {
	# Run the job in the global region, which is the default.
	# region = "global"

	# Specify the datacenters within the region this job can run in.
	datacenters = ["dc1"]

	# Service type jobs optimize for long-lived services. This is
	# the default but we can change to batch for short-lived tasks.
	# type = "service"

	# Priority controls our access to resources and scheduling priority.
	# This can be 1 to 100, inclusively, and defaults to 50.
	# priority = 50

	# Restrict our job to only linux. We can specify multiple
	# constraints as needed.
	constraint {
		attribute = "${attr.kernel.name}"
		value = "linux"
	}

	# Configure the job to do rolling updates
	update {
		# Stagger updates every 10 seconds
		stagger = "10s"

		# Update a single task at a time
		max_parallel = 1
	}

	# Create a 'cache' group. Each task in the group will be
	# scheduled onto the same machine.
	group "cache" {
		# Control the number of instances of this groups.
		# Defaults to 1
		# count = 1

		# Configure the restart policy for the task group. If not provided, a
		# default is used based on the job type.
		restart {
			# The number of attempts to run the job within the specified interval.
			attempts = 10
			interval = "5m"

			# A delay between a task failing and a restart occuring.
			delay = "25s"

			# Mode controls what happens when a task has restarted "attempts"
			# times within the interval. "delay" mode delays the next restart
			# till the next interval. "fail" mode does not restart the task if
			# "attempts" has been hit within the interval.
			mode = "delay"
		}

		# Define a task to run
		task "redis" {
			# Use Docker to run the task.
			driver = "docker"

			# Configure Docker driver with the image
			config {
				image = "redis:latest"
				port_map {
					db = 6379
				}
			}

			service {
				name = "${TASKGROUP}-redis"
				tags = ["global", "cache"]
				port = "db"
				check {
					name = "alive"
					type = "tcp"
					interval = "10s"
					timeout = "2s"
				}
			}

			# We must specify the resources required for
			# this task to ensure it runs on a machine with
			# enough capacity.
			resources {
				cpu = 500 # 500 Mhz
				memory = 256 # 256MB
				network {
					mbits = 10
					port "db" {
					}
				}
			}

			# Specify configuration related to log rotation
			# logs {
			#	max_files = 10
			#	max_file_size = 15
			# }

			# Controls the timeout between signalling a task it will be killed
			# and killing the task. If not set a default is used.
			# kill_timeout = "20s"
		}
	}
}
`)
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/nerdalize/nerd/nerd/client"
)

//NerdAPIOpts configure how the platform endpoint is reached
type NerdAPIOpts struct {
	NerdAPIScheme string `long:"api-scheme" default:"https" default-mask:"https" env:"NERD_API_SCHEME" description:"protocol for endpoint communication"`

	NerdAPIHostname string `long:"api-hostname" default:"platform.nerdalize.net" default-mask:"platform.nerdalize.net" env:"NERD_API_HOST" description:"hostname of the compute platform"`

	NerdAPIBasePath string `long:"api-basepath" default:"" default-mask:"" env:"NERD_API_BASE_PATH" description:"basepath of the endpoint"`

	NerdAPIVersion string `long:"api-version" default:"v1" default-mask:"v1" env:"NERD_API_VERSION" description:"endpoint version"`

	NerdAPIFullURL string `long:"api-full-url" default:"" default-mask:"" env:"NERD_API_FULL_URL" description:"full endpoint url"`
}

//URL returns a fully qualitied url on the platform endpoint
func (opts *NerdAPIOpts) URL(path string) (loc *url.URL, err error) {
	loc, err = url.Parse(fmt.Sprintf(
		"%s://%s/%s/%s",
		opts.NerdAPIScheme,
		opts.NerdAPIHostname,
		opts.NerdAPIVersion,
		strings.TrimLeft(path, "/"),
	))
	return loc, err
}

//NerdAPIConfig returns a populated configuration struct for the Nerd API client.
func (opts *NerdAPIOpts) NerdAPIConfig() client.NerdAPIConfig {
	return client.NerdAPIConfig{
		Scheme:   opts.NerdAPIScheme,
		Host:     opts.NerdAPIHostname,
		BasePath: opts.NerdAPIBasePath,
		Version:  opts.NerdAPIVersion,
	}
}
<commit_msg>Remove full url option<commit_after>package command

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/nerdalize/nerd/nerd/client"
)

//NerdAPIOpts configure how the platform endpoint is reached
type NerdAPIOpts struct {
	NerdAPIScheme string `long:"api-scheme" default:"https" default-mask:"https" env:"NERD_API_SCHEME" description:"protocol for endpoint communication"`

	NerdAPIHostname string `long:"api-hostname" default:"platform.nerdalize.net" default-mask:"platform.nerdalize.net" env:"NERD_API_HOST" description:"hostname of the compute platform"`

	NerdAPIBasePath string `long:"api-basepath" default:"" default-mask:"" env:"NERD_API_BASE_PATH" description:"basepath of the endpoint"`

	NerdAPIVersion string `long:"api-version" default:"v1" default-mask:"v1" env:"NERD_API_VERSION" description:"endpoint version"`
}

//URL returns a fully qualitied url on the platform endpoint
func (opts *NerdAPIOpts) URL(path string) (loc *url.URL, err error) {
	loc, err = url.Parse(fmt.Sprintf(
		"%s://%s/%s/%s",
		opts.NerdAPIScheme,
		opts.NerdAPIHostname,
		opts.NerdAPIVersion,
		strings.TrimLeft(path, "/"),
	))
	return loc, err
}

//NerdAPIConfig returns a populated configuration struct for the Nerd API client.
func (opts *NerdAPIOpts) NerdAPIConfig() client.NerdAPIConfig {
	return client.NerdAPIConfig{
		Scheme:   opts.NerdAPIScheme,
		Host:     opts.NerdAPIHostname,
		BasePath: opts.NerdAPIBasePath,
		Version:  opts.NerdAPIVersion,
	}
}
<|endoftext|>


<commit_before>package wspacego

import (
	"fmt"
	. "github.com/r7kamura/gospel"
	"testing"
)

func TestCommnad(t *testing.T) {
	Describe(t, "Command Tests", func() {
		Context("コマンドだけ指定する", func() {
			It("インスタンスが作成できること", func() {
				cmd := "cmd"
				sut := NewCommand(cmd)
				Expect(sut).To(Exist)
			})
			It("コマンドだけ変数が書き換えられていること", func() {
				cmd := "cmd"
				sut := NewCommand(cmd)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, "")
				Expect(sut.param).To(Equal, 0)
			})
		})
		Context("サブコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd, subcmd := "cmd", "subcmd"
				Expect(NewSubCommand(cmd, subcmd)).To(Exist)
			})
			It("コマンドとサブコマンドが指定した値で書き換えられていること", func() {
				cmd, subcmd := "cmd", "subcmd"
				sut := NewSubCommand(cmd, subcmd)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, subcmd)
				Expect(sut.param).To(Equal, 0)
			})
		})
		Context("パラメータ付きのコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd := "cmd"
				param := 1
				sut := NewCommandWithParam(cmd, param)
				Expect(sut).To(Exist)
			})
			It("コマンドとパラメータが指定した値で書き換えられていること", func() {
				cmd := "cmd"
				param := 1
				sut := NewCommandWithParam(cmd, param)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, "")
				Expect(sut.param).To(Equal, 1)
			})
		})
		Context("パラメータ付きのサブコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(sut).To(Exist)
			})
			It("指定した値でメンバー変数が書き換えられていること", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, subcmd)
				Expect(sut.param).To(Equal, param)
			})
		})
		Context("文字列に変換する", func() {
			It("指定したフォーマットになっている", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(fmt.Sprint(sut)).To(Equal, "cmd subcmd 1")
			})
			It("パラメータに２を渡したとき指定したフォーマットになっている", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 2
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(fmt.Sprint(sut)).To(Equal, "cmd subcmd 2")
			})
		})
	})
}

func TestCommandList(t *testing.T) {
	Describe(t, "CommandList Tests", func() {
		Context("実行するコマンドの一覧を作成する", func() {
			It("インスタンスが作成できること", func() {
				sut := NewCommandList()
				Expect(sut).To(Exist)
			})
		})
		Context("リストにコマンドを追加する関数", func() {
			sut := NewCommandList()
			It("コマンドが追加できることを確認する", func() {
				sut.Add(NewCommand("test"))
				Expect(len(*sut)).To(Equal, 1)
			})
			It("コマンドが追加できることを確認する（2回目）", func() {
				sut.Add(NewCommand("test2"))
				Expect(len(*sut)).To(Equal, 2)
			})
		})
		Context("コマンドをすべて削除する関数", func() {
			sut := NewCommandList()
			sut.Add(NewCommand("test"))
			Expect(len(*sut)).To(Equal, 1)
			It("コマンドがすべて削除される", func() {
				sut.Clear()
				Expect(len(*sut)).To(Equal, 0)
			})
			It("コマンドがすべて削除される", func() {
				sut.Add(NewCommand("test"))
				sut.Add(NewCommand("test2"))
				sut.Clear()
				Expect(len(*sut)).To(Equal, 0)
			})
		})
		Context("コマンドを行番号で取得する関数", func() {
			sut := NewCommandList()
			sut.Add(NewCommand("test"))
			sut.Add(NewCommand("test2"))
			It("指定した１行目のコマンドを取得できること", func() {
				actual := sut.Get(1)
				Expect(actual).To(Exist)
				Expect(actual.cmd).To(Equal, "test")
			})
			It("指定した２行目のコマンドを取得できること", func() {
				actual := sut.Get(2)
				Expect(actual).To(Exist)
				Expect(actual.cmd).To(Equal, "test2")
			})
		})
	})
}
<commit_msg>存在しない行番号３が指定されたときにnilが返ってくることを確認するテストを追加<commit_after>package wspacego

import (
	"fmt"
	. "github.com/r7kamura/gospel"
	"testing"
)

func TestCommnad(t *testing.T) {
	Describe(t, "Command Tests", func() {
		Context("コマンドだけ指定する", func() {
			It("インスタンスが作成できること", func() {
				cmd := "cmd"
				sut := NewCommand(cmd)
				Expect(sut).To(Exist)
			})
			It("コマンドだけ変数が書き換えられていること", func() {
				cmd := "cmd"
				sut := NewCommand(cmd)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, "")
				Expect(sut.param).To(Equal, 0)
			})
		})
		Context("サブコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd, subcmd := "cmd", "subcmd"
				Expect(NewSubCommand(cmd, subcmd)).To(Exist)
			})
			It("コマンドとサブコマンドが指定した値で書き換えられていること", func() {
				cmd, subcmd := "cmd", "subcmd"
				sut := NewSubCommand(cmd, subcmd)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, subcmd)
				Expect(sut.param).To(Equal, 0)
			})
		})
		Context("パラメータ付きのコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd := "cmd"
				param := 1
				sut := NewCommandWithParam(cmd, param)
				Expect(sut).To(Exist)
			})
			It("コマンドとパラメータが指定した値で書き換えられていること", func() {
				cmd := "cmd"
				param := 1
				sut := NewCommandWithParam(cmd, param)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, "")
				Expect(sut.param).To(Equal, 1)
			})
		})
		Context("パラメータ付きのサブコマンドを作成する", func() {
			It("インスタンスが作成できること", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(sut).To(Exist)
			})
			It("指定した値でメンバー変数が書き換えられていること", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(sut.cmd).To(Equal, cmd)
				Expect(sut.subcmd).To(Equal, subcmd)
				Expect(sut.param).To(Equal, param)
			})
		})
		Context("文字列に変換する", func() {
			It("指定したフォーマットになっている", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 1
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(fmt.Sprint(sut)).To(Equal, "cmd subcmd 1")
			})
			It("パラメータに２を渡したとき指定したフォーマットになっている", func() {
				cmd, subcmd := "cmd", "subcmd"
				param := 2
				sut := NewSubCommandWithParam(cmd, subcmd, param)
				Expect(fmt.Sprint(sut)).To(Equal, "cmd subcmd 2")
			})
		})
	})
}

func TestCommandList(t *testing.T) {
	Describe(t, "CommandList Tests", func() {
		Context("実行するコマンドの一覧を作成する", func() {
			It("インスタンスが作成できること", func() {
				sut := NewCommandList()
				Expect(sut).To(Exist)
			})
		})
		Context("リストにコマンドを追加する関数", func() {
			sut := NewCommandList()
			It("コマンドが追加できることを確認する", func() {
				sut.Add(NewCommand("test"))
				Expect(len(*sut)).To(Equal, 1)
			})
			It("コマンドが追加できることを確認する（2回目）", func() {
				sut.Add(NewCommand("test2"))
				Expect(len(*sut)).To(Equal, 2)
			})
		})
		Context("コマンドをすべて削除する関数", func() {
			sut := NewCommandList()
			sut.Add(NewCommand("test"))
			Expect(len(*sut)).To(Equal, 1)
			It("コマンドがすべて削除される", func() {
				sut.Clear()
				Expect(len(*sut)).To(Equal, 0)
			})
			It("コマンドがすべて削除される", func() {
				sut.Add(NewCommand("test"))
				sut.Add(NewCommand("test2"))
				sut.Clear()
				Expect(len(*sut)).To(Equal, 0)
			})
		})
		Context("コマンドを行番号で取得する関数", func() {
			sut := NewCommandList()
			sut.Add(NewCommand("test"))
			sut.Add(NewCommand("test2"))
			It("指定した１行目のコマンドを取得できること", func() {
				actual := sut.Get(1)
				Expect(actual).To(Exist)
				Expect(actual.cmd).To(Equal, "test")
			})
			It("指定した２行目のコマンドを取得できること", func() {
				actual := sut.Get(2)
				Expect(actual).To(Exist)
				Expect(actual.cmd).To(Equal, "test2")
			})
			It("指定した3行目のコマンドが存在しないときにnilが返ってくること", func() {
				actual := sut.Get(3)
				Expect(actual).To(NotExist)
			})
		})
	})
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/zetamatta/go-box"

	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

func cmd_box(ctx context.Context, cmd *shell.Cmd) (int, error) {
	data, err := ioutil.ReadAll(cmd.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		return 1, err
	}
	list := strings.Split(string(data), "\n")
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	result := box.Choice(
		list,
		readline.Console)
	fmt.Fprintln(cmd.Stdout, result)
	return 0, nil
}
<commit_msg>Fix: panic occurs when `box` Enter & Ctrl-C pressed.<commit_after>package commands

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/zetamatta/go-box"

	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

func cmd_box(ctx context.Context, cmd *shell.Cmd) (int, error) {
	data, err := ioutil.ReadAll(cmd.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		return 1, err
	}
	list := strings.Split(string(data), "\n")
	switch len(list) {
	case 0:
		return 0, nil
	case 1:
		fmt.Fprintln(cmd.Stdout, strings.TrimSpace(list[0]))
		return 0, nil
	}
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	result := box.Choice(
		list,
		readline.Console)
	fmt.Fprintln(cmd.Stdout, result)
	return 0, nil
}
<|endoftext|>