<commit_before>package cmd

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	"github.com/aws/aws-sdk-go/service/ecs"

	"github.com/docker/distribution/reference"
	"github.com/oklog/ulid"
	"github.com/spf13/cobra"
)

var ECRRegex *regexp.Regexp = func() *regexp.Regexp {
	regex, _ := regexp.Compile(`^[0-9]+\.dkr\.ecr\.(us|ca|eu|ap|sa)-(east|west|central|northeast|southeast|south)-[12]\.amazonaws\.com$`)
	return regex
}()

type deployCmd struct {
	cluster     string
	serviceName string
	revision    int
	images      imageOptions
	backend     string
	slackNotify string
}

func NewDeployCommand(out, errOut io.Writer) *cobra.Command {
	f := &deployCmd{}
	cmd := &cobra.Command{
		Use:   "deploy [options]",
		Short: "",
		RunE: func(cmd *cobra.Command, args []string) error {
			log := NewLogger(f.cluster, f.serviceName, f.slackNotify, out)
			err := f.execute(cmd, args, log)
			if err != nil {
				log.fail(fmt.Sprintf("failed to deploy. cluster: %s, serviceName: %s\n", f.cluster, f.serviceName))
				return err
			}
			return nil
		},
	}
	cmd.Flags().StringVar(&f.cluster, "cluster", "", "ECS Cluster Name")
	cmd.Flags().StringVar(&f.serviceName, "service-name", "", "ECS Service Name")
	cmd.Flags().IntVar(&f.revision, "revision", 0, "revision of ECS task definition")
	cmd.Flags().Var(&f.images, "image", "base image of ECR image")
	cmd.Flags().StringVar(&f.backend, "backend", "SSM", "Backend type of history manager")
	cmd.Flags().StringVar(&f.slackNotify, "slack-notify", "", "slack webhook URL")

	return cmd
}

func (f *deployCmd) execute(_ *cobra.Command, args []string, l *logger) error {
	if f.cluster == "" {
		return errors.New("--cluster is required")
	}

	if f.serviceName == "" {
		return errors.New("--service-name is required")
	}

	region := getAWSRegion()
	if region == "" {
		return errors.New("AWS region is not found. please set a AWS_DEFAULT_REGION or AWS_REGION")
	}

	sess, err := session.NewSession()
	if err != nil {
		return err
	}

	client := ecs.New(sess, &aws.Config{
		Region: aws.String(region),
	})

	ecrClient := ecr.New(sess, &aws.Config{
		Region: aws.String(region),
	})

	service, err := describeService(client, f.cluster, f.serviceName)
	if err != nil {
		return err
	}

	if len(service.Deployments) > 1 {
		return errors.New(fmt.Sprintf("%s is currently deployed", f.serviceName))
	}

	var uniqueID string
	{
		entropy := rand.New(rand.NewSource(time.Now().UnixNano()))
		uniqueID = ulid.MustNew(ulid.Now(), entropy).String()
	}

	var taskDef *ecs.TaskDefinition
	var registerdTaskDef *ecs.TaskDefinition
	{
		taskDefArn := *service.TaskDefinition
		taskDefArn, err = specifyRevision(f.revision, taskDefArn)
		if err != nil {
			return err
		}

		taskDef, err = describeTaskDefinition(client, taskDefArn)
		if err != nil {
			return err
		}

		newTaskDef, err := createNewTaskDefinition(uniqueID, taskDef)
		if err != nil {
			return err
		}

		for _, v := range taskDef.ContainerDefinitions {
			img, err := parseDockerImage(*v.Image)
			if err != nil {
				return err
			}

			opt := f.images.Get(img.RepositoryName)
			if opt == nil {
				return errors.New(fmt.Sprintf("can not found a image option %s", img.RepositoryName))
			}

			err = tagDockerImage(ecrClient, img.RepositoryName, opt.Tag, uniqueID)
			if err != nil {
				return err
			}
		}

		registerdTaskDef, err = registerTaskDefinition(client, newTaskDef)
		if err != nil {
			return err
		}
	}

	pusher, err := NewStatePusher(f.backend, f.cluster, f.serviceName)
	if err != nil {
		return err
	}
	err = pusher.PushState(
		int(*registerdTaskDef.Revision),
		fmt.Sprintf("deploy: %d -> %d", *taskDef.Revision, *registerdTaskDef.Revision),
	)
	if err != nil {
		return err
	}

	l.log(fmt.Sprintf("task definition registerd successfully: revision %d -> %d\n", *taskDef.Revision, *registerdTaskDef.Revision))

	err = updateService(client, service, registerdTaskDef)
	if err != nil {
		return err
	}

	l.log(fmt.Sprintf("service updating\n"))

	err = waitUpdateService(client, f.cluster, f.serviceName, l)
	if err != nil {
		return err
	}

	err = pusher.UpdateState(int(*registerdTaskDef.Revision))
	if err != nil {
		return err
	}

	l.success(fmt.Sprintf("service updated successfully\n"))

	return nil
}

func describeService(client *ecs.ECS, cluster, serviceName string) (*ecs.Service, error) {
	params := &ecs.DescribeServicesInput{
		Services: []*string{aws.String(serviceName)},
		Cluster:  aws.String(cluster),
	}

	res, err := client.DescribeServices(params)
	if err != nil {
		return nil, err
	}

	if len(res.Services) == 0 {
		return nil, errors.New("service is not found")
	}

	return res.Services[0], nil
}

func describeTaskDefinition(client *ecs.ECS, arn string) (*ecs.TaskDefinition, error) {
	params := &ecs.DescribeTaskDefinitionInput{
		TaskDefinition: aws.String(arn),
	}

	res, err := client.DescribeTaskDefinition(params)
	if err != nil {
		return nil, err
	}

	return res.TaskDefinition, nil
}

func createNewTaskDefinition(id string, taskDef *ecs.TaskDefinition) (*ecs.TaskDefinition, error) {
	newTaskDef := *taskDef // shallow copy
	var containers []*ecs.ContainerDefinition
	for _, vp := range taskDef.ContainerDefinitions {
		v := *vp // shallow copy
		img, err := parseDockerImage(*v.Image)
		if err != nil {
			return nil, err
		}

		if isECRHosted(img) {
			v.Image = aws.String(fmt.Sprintf("%s:%s", img.Name, id))
			containers = append(containers, &v)
		}
	}
	newTaskDef.ContainerDefinitions = containers

	return &newTaskDef, nil
}

type dockerImage struct {
	Name           string
	Tag            string
	RepositoryName string
	HostName       string
}

func parseDockerImage(image string) (*dockerImage, error) {
	ref, err := reference.Parse(image)
	if err != nil {
		return nil, err
	}

	hostName, repoName := reference.SplitHostname(ref.(reference.Named))
	return &dockerImage{
		Name:           ref.(reference.Named).Name(),
		Tag:            ref.(reference.Tagged).Tag(),
		RepositoryName: repoName,
		HostName:       hostName,
	}, nil
}

func isECRHosted(image *dockerImage) bool {
	return ECRRegex.MatchString(image.HostName)
}

func registerTaskDefinition(client *ecs.ECS, taskDef *ecs.TaskDefinition) (*ecs.TaskDefinition, error) {
	params := &ecs.RegisterTaskDefinitionInput{
		ContainerDefinitions: taskDef.ContainerDefinitions,
		Family:               taskDef.Family,
		NetworkMode:          taskDef.NetworkMode,
		PlacementConstraints: taskDef.PlacementConstraints,
		TaskRoleArn:          taskDef.TaskRoleArn,
		Volumes:              taskDef.Volumes,
	}

	res, err := client.RegisterTaskDefinition(params)
	if err != nil {
		return nil, err
	}

	return res.TaskDefinition, nil
}

func updateService(client *ecs.ECS, service *ecs.Service, taskDef *ecs.TaskDefinition) error {
	params := &ecs.UpdateServiceInput{
		Cluster:                 service.ClusterArn,
		DeploymentConfiguration: service.DeploymentConfiguration,
		DesiredCount:            service.DesiredCount,
		Service:                 service.ServiceName,
		TaskDefinition:          taskDef.TaskDefinitionArn,
	}

	_, err := client.UpdateService(params)
	if err != nil {
		return err
	}

	return nil
}

func waitUpdateService(client *ecs.ECS, cluster, serviceName string, l *logger) error {
	start := time.Now()
	t := time.NewTicker(10 * time.Second)
	for {
		select {
		case <-t.C:
			s, err := describeService(client, cluster, serviceName)
			if err != nil {
				return err
			}

			elapsed := time.Now().Sub(start)
			l.log(fmt.Sprintf("still service updating... [%s]\n", (elapsed/time.Second)*time.Second))

			if len(s.Deployments) == 1 && *s.RunningCount == *s.DesiredCount {
				return nil
			}
		}
	}
}

func specifyRevision(revision int, arn string) (string, error) {
	if revision <= 0 {
		return arn, nil
	}

	re, err := regexp.Compile(`(.*):[1-9][0-9]*$`)
	if err != nil {
		return "", err
	}

	return re.ReplaceAllString(arn, fmt.Sprintf("${1}:%d", revision)), nil
}

func tagDockerImage(ecrClient *ecr.ECR, repoName string, fromTag string, toTag string) error {
	params := &ecr.BatchGetImageInput{
		ImageIds:       []*ecr.ImageIdentifier{{ImageTag: aws.String(fromTag)}},
		RepositoryName: aws.String(repoName),

		AcceptedMediaTypes: []*string{
			aws.String("application/vnd.docker.distribution.manifest.v1+json"),
			aws.String("application/vnd.docker.distribution.manifest.v2+json"),
			aws.String("application/vnd.oci.image.manifest.v1+json"),
		},
	}
	img, err := ecrClient.BatchGetImage(params)
	if err != nil {
		return err
	}

	putParams := &ecr.PutImageInput{
		ImageManifest:  img.Images[0].ImageManifest,
		RepositoryName: aws.String(repoName),
		ImageTag:       aws.String(toTag),
	}
	_, err = ecrClient.PutImage(putParams)
	if err != nil {
		return err
	}

	return nil
}

func getAWSRegion() string {
	if os.Getenv("AWS_REGION") != "" {
		return os.Getenv("AWS_REGION")
	}

	if os.Getenv("AWS_DEFAULT_REGION") != "" {
		return os.Getenv("AWS_DEFAULT_REGION")
	}

	return ""
}

//
// imageOptions
//

type imageOption struct {
	RepositoryName string
	Tag            string
}

type imageOptions struct {
	Value []*imageOption
}

func (t *imageOptions) String() string {
	return fmt.Sprintf("String: %v", t.Value)
}

func (t *imageOptions) Set(v string) error {
	r, _ := regexp.Compile(`^([a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*):([\w][\w.-]{0,127})$`)
	matches := r.FindStringSubmatch(v)
	if len(matches) == 0 {
		return errors.New(fmt.Sprintf("invalid format %s", v))
	}

	opt := &imageOption{
		RepositoryName: matches[1],
		Tag:            matches[2],
	}

	t.Value = append(t.Value, opt)

	return nil
}

func (t *imageOptions) Type() string {
	return "image"
}

func (t *imageOptions) Get(repoName string) *imageOption {
	for _, v := range t.Value {
		if v.RepositoryName == repoName {
			return v
		}
	}
	return nil
}
<commit_msg>check image option<commit_after>package cmd

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"regexp"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	"github.com/aws/aws-sdk-go/service/ecs"

	"github.com/docker/distribution/reference"
	"github.com/oklog/ulid"
	"github.com/spf13/cobra"
)

var ECRRegex *regexp.Regexp = func() *regexp.Regexp {
	regex, _ := regexp.Compile(`^[0-9]+\.dkr\.ecr\.(us|ca|eu|ap|sa)-(east|west|central|northeast|southeast|south)-[12]\.amazonaws\.com$`)
	return regex
}()

type deployCmd struct {
	cluster     string
	serviceName string
	revision    int
	images      imageOptions
	backend     string
	slackNotify string
}

func NewDeployCommand(out, errOut io.Writer) *cobra.Command {
	f := &deployCmd{}
	cmd := &cobra.Command{
		Use:   "deploy [options]",
		Short: "",
		RunE: func(cmd *cobra.Command, args []string) error {
			log := NewLogger(f.cluster, f.serviceName, f.slackNotify, out)
			err := f.execute(cmd, args, log)
			if err != nil {
				log.fail(fmt.Sprintf("failed to deploy. cluster: %s, serviceName: %s\n", f.cluster, f.serviceName))
				return err
			}
			return nil
		},
	}
	cmd.Flags().StringVar(&f.cluster, "cluster", "", "ECS Cluster Name")
	cmd.Flags().StringVar(&f.serviceName, "service-name", "", "ECS Service Name")
	cmd.Flags().IntVar(&f.revision, "revision", 0, "revision of ECS task definition")
	cmd.Flags().Var(&f.images, "image", "base image of ECR image")
	cmd.Flags().StringVar(&f.backend, "backend", "SSM", "Backend type of history manager")
	cmd.Flags().StringVar(&f.slackNotify, "slack-notify", "", "slack webhook URL")

	return cmd
}

func (f *deployCmd) execute(_ *cobra.Command, args []string, l *logger) error {
	if f.cluster == "" {
		return errors.New("--cluster is required")
	}

	if f.serviceName == "" {
		return errors.New("--service-name is required")
	}

	if len(f.images.Value) == 0 {
		return errors.New("--image is required")
	}

	region := getAWSRegion()
	if region == "" {
		return errors.New("AWS region is not found. please set a AWS_DEFAULT_REGION or AWS_REGION")
	}

	sess, err := session.NewSession()
	if err != nil {
		return err
	}

	client := ecs.New(sess, &aws.Config{
		Region: aws.String(region),
	})

	ecrClient := ecr.New(sess, &aws.Config{
		Region: aws.String(region),
	})

	service, err := describeService(client, f.cluster, f.serviceName)
	if err != nil {
		return err
	}

	if len(service.Deployments) > 1 {
		return errors.New(fmt.Sprintf("%s is currently deployed", f.serviceName))
	}

	var uniqueID string
	{
		entropy := rand.New(rand.NewSource(time.Now().UnixNano()))
		uniqueID = ulid.MustNew(ulid.Now(), entropy).String()
	}

	var taskDef *ecs.TaskDefinition
	var registerdTaskDef *ecs.TaskDefinition
	{
		taskDefArn := *service.TaskDefinition
		taskDefArn, err = specifyRevision(f.revision, taskDefArn)
		if err != nil {
			return err
		}

		taskDef, err = describeTaskDefinition(client, taskDefArn)
		if err != nil {
			return err
		}

		newTaskDef, err := createNewTaskDefinition(uniqueID, taskDef)
		if err != nil {
			return err
		}

		for _, v := range taskDef.ContainerDefinitions {
			img, err := parseDockerImage(*v.Image)
			if err != nil {
				return err
			}

			opt := f.images.Get(img.RepositoryName)
			if opt == nil {
				return errors.New(fmt.Sprintf("can not found a image option %s", img.RepositoryName))
			}

			err = tagDockerImage(ecrClient, img.RepositoryName, opt.Tag, uniqueID)
			if err != nil {
				return err
			}
		}

		registerdTaskDef, err = registerTaskDefinition(client, newTaskDef)
		if err != nil {
			return err
		}
	}

	pusher, err := NewStatePusher(f.backend, f.cluster, f.serviceName)
	if err != nil {
		return err
	}
	err = pusher.PushState(
		int(*registerdTaskDef.Revision),
		fmt.Sprintf("deploy: %d -> %d", *taskDef.Revision, *registerdTaskDef.Revision),
	)
	if err != nil {
		return err
	}

	l.log(fmt.Sprintf("task definition registerd successfully: revision %d -> %d\n", *taskDef.Revision, *registerdTaskDef.Revision))

	err = updateService(client, service, registerdTaskDef)
	if err != nil {
		return err
	}

	l.log(fmt.Sprintf("service updating\n"))

	err = waitUpdateService(client, f.cluster, f.serviceName, l)
	if err != nil {
		return err
	}

	err = pusher.UpdateState(int(*registerdTaskDef.Revision))
	if err != nil {
		return err
	}

	l.success(fmt.Sprintf("service updated successfully\n"))

	return nil
}

func describeService(client *ecs.ECS, cluster, serviceName string) (*ecs.Service, error) {
	params := &ecs.DescribeServicesInput{
		Services: []*string{aws.String(serviceName)},
		Cluster:  aws.String(cluster),
	}

	res, err := client.DescribeServices(params)
	if err != nil {
		return nil, err
	}

	if len(res.Services) == 0 {
		return nil, errors.New("service is not found")
	}

	return res.Services[0], nil
}

func describeTaskDefinition(client *ecs.ECS, arn string) (*ecs.TaskDefinition, error) {
	params := &ecs.DescribeTaskDefinitionInput{
		TaskDefinition: aws.String(arn),
	}

	res, err := client.DescribeTaskDefinition(params)
	if err != nil {
		return nil, err
	}

	return res.TaskDefinition, nil
}

func createNewTaskDefinition(id string, taskDef *ecs.TaskDefinition) (*ecs.TaskDefinition, error) {
	newTaskDef := *taskDef // shallow copy
	var containers []*ecs.ContainerDefinition
	for _, vp := range taskDef.ContainerDefinitions {
		v := *vp // shallow copy
		img, err := parseDockerImage(*v.Image)
		if err != nil {
			return nil, err
		}

		if isECRHosted(img) {
			v.Image = aws.String(fmt.Sprintf("%s:%s", img.Name, id))
			containers = append(containers, &v)
		}
	}
	newTaskDef.ContainerDefinitions = containers

	return &newTaskDef, nil
}

type dockerImage struct {
	Name           string
	Tag            string
	RepositoryName string
	HostName       string
}

func parseDockerImage(image string) (*dockerImage, error) {
	ref, err := reference.Parse(image)
	if err != nil {
		return nil, err
	}

	hostName, repoName := reference.SplitHostname(ref.(reference.Named))
	return &dockerImage{
		Name:           ref.(reference.Named).Name(),
		Tag:            ref.(reference.Tagged).Tag(),
		RepositoryName: repoName,
		HostName:       hostName,
	}, nil
}

func isECRHosted(image *dockerImage) bool {
	return ECRRegex.MatchString(image.HostName)
}

func registerTaskDefinition(client *ecs.ECS, taskDef *ecs.TaskDefinition) (*ecs.TaskDefinition, error) {
	params := &ecs.RegisterTaskDefinitionInput{
		ContainerDefinitions: taskDef.ContainerDefinitions,
		Family:               taskDef.Family,
		NetworkMode:          taskDef.NetworkMode,
		PlacementConstraints: taskDef.PlacementConstraints,
		TaskRoleArn:          taskDef.TaskRoleArn,
		Volumes:              taskDef.Volumes,
	}

	res, err := client.RegisterTaskDefinition(params)
	if err != nil {
		return nil, err
	}

	return res.TaskDefinition, nil
}

func updateService(client *ecs.ECS, service *ecs.Service, taskDef *ecs.TaskDefinition) error {
	params := &ecs.UpdateServiceInput{
		Cluster:                 service.ClusterArn,
		DeploymentConfiguration: service.DeploymentConfiguration,
		DesiredCount:            service.DesiredCount,
		Service:                 service.ServiceName,
		TaskDefinition:          taskDef.TaskDefinitionArn,
	}

	_, err := client.UpdateService(params)
	if err != nil {
		return err
	}

	return nil
}

func waitUpdateService(client *ecs.ECS, cluster, serviceName string, l *logger) error {
	start := time.Now()
	t := time.NewTicker(10 * time.Second)
	for {
		select {
		case <-t.C:
			s, err := describeService(client, cluster, serviceName)
			if err != nil {
				return err
			}

			elapsed := time.Now().Sub(start)
			l.log(fmt.Sprintf("still service updating... [%s]\n", (elapsed/time.Second)*time.Second))

			if len(s.Deployments) == 1 && *s.RunningCount == *s.DesiredCount {
				return nil
			}
		}
	}
}

func specifyRevision(revision int, arn string) (string, error) {
	if revision <= 0 {
		return arn, nil
	}

	re, err := regexp.Compile(`(.*):[1-9][0-9]*$`)
	if err != nil {
		return "", err
	}

	return re.ReplaceAllString(arn, fmt.Sprintf("${1}:%d", revision)), nil
}

func tagDockerImage(ecrClient *ecr.ECR, repoName string, fromTag string, toTag string) error {
	params := &ecr.BatchGetImageInput{
		ImageIds:       []*ecr.ImageIdentifier{{ImageTag: aws.String(fromTag)}},
		RepositoryName: aws.String(repoName),

		AcceptedMediaTypes: []*string{
			aws.String("application/vnd.docker.distribution.manifest.v1+json"),
			aws.String("application/vnd.docker.distribution.manifest.v2+json"),
			aws.String("application/vnd.oci.image.manifest.v1+json"),
		},
	}
	img, err := ecrClient.BatchGetImage(params)
	if err != nil {
		return err
	}

	putParams := &ecr.PutImageInput{
		ImageManifest:  img.Images[0].ImageManifest,
		RepositoryName: aws.String(repoName),
		ImageTag:       aws.String(toTag),
	}
	_, err = ecrClient.PutImage(putParams)
	if err != nil {
		return err
	}

	return nil
}

func getAWSRegion() string {
	if os.Getenv("AWS_REGION") != "" {
		return os.Getenv("AWS_REGION")
	}

	if os.Getenv("AWS_DEFAULT_REGION") != "" {
		return os.Getenv("AWS_DEFAULT_REGION")
	}

	return ""
}

//
// imageOptions
//

type imageOption struct {
	RepositoryName string
	Tag            string
}

type imageOptions struct {
	Value []*imageOption
}

func (t *imageOptions) String() string {
	return fmt.Sprintf("String: %v", t.Value)
}

func (t *imageOptions) Set(v string) error {
	r, _ := regexp.Compile(`^([a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*):([\w][\w.-]{0,127})$`)
	matches := r.FindStringSubmatch(v)
	if len(matches) == 0 {
		return errors.New(fmt.Sprintf("invalid format %s", v))
	}

	opt := &imageOption{
		RepositoryName: matches[1],
		Tag:            matches[2],
	}

	t.Value = append(t.Value, opt)

	return nil
}

func (t *imageOptions) Type() string {
	return "image"
}

func (t *imageOptions) Get(repoName string) *imageOption {
	for _, v := range t.Value {
		if v.RepositoryName == repoName {
			return v
		}
	}
	return nil
}
<|endoftext|>


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

package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/errors"
)

var log = NewLogger()
var trimStrings []string

// Level type
type Level int8

// Enumerated level types
const (
	Error Level = iota + 1
	Fatal
)

func (level Level) String() string {
	var lvlStr string
	switch level {
	case Error:
		lvlStr = "ERROR"
	case Fatal:
		lvlStr = "FATAL"
	}
	return lvlStr
}

type logEntry struct {
	Level   string   `json:"level"`
	Message string   `json:"message"`
	Time    string   `json:"time"`
	Cause   string   `json:"cause"`
	Trace   []string `json:"trace"`
}

// Logger - for console messages
type Logger struct {
	quiet bool
	json  bool
}

// NewLogger - to create a new Logger object
func NewLogger() *Logger {
	return &Logger{}
}

// EnableQuiet - turns quiet option on.
func (log *Logger) EnableQuiet() {
	log.quiet = true
}

// EnableJSON - outputs logs in json format.
func (log *Logger) EnableJSON() {
	log.json = true
	log.quiet = true
}

// Println - wrapper to console.Println() with quiet flag.
func (log *Logger) Println(args ...interface{}) {
	if !log.quiet {
		console.Println(args...)
	}
}

// Printf - wrapper to console.Printf() with quiet flag.
func (log *Logger) Printf(format string, args ...interface{}) {
	if !log.quiet {
		console.Printf(format, args...)
	}
}

func init() {
	var goPathList []string
	// Add all possible GOPATH paths into trimStrings
	// Split GOPATH depending on the OS type
	if runtime.GOOS == "windows" {
		goPathList = strings.Split(GOPATH, ";")
	} else {
		// All other types of OSs
		goPathList = strings.Split(GOPATH, ":")
	}

	// Add trim string "{GOROOT}/src/" into trimStrings
	trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}

	// Add all possible path from GOPATH=path1:path2...:pathN
	// as "{path#}/src/" into trimStrings
	for _, goPathString := range goPathList {
		trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
	}
	// Add "github.com/minio/minio" as the last to cover
	// paths like "{GOROOT}/src/github.com/minio/minio"
	// and "{GOPATH}/src/github.com/minio/minio"
	trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
}

func trimTrace(f string) string {
	for _, trimString := range trimStrings {
		f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
	}
	return filepath.FromSlash(f)
}

// getTrace method - creates and returns stack trace
func getTrace(traceLevel int) []string {
	var trace []string
	pc, file, lineNumber, ok := runtime.Caller(traceLevel)

	for ok {
		// Clean up the common prefixes
		file = trimTrace(file)
		// Get the function name
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		// Skip duplicate traces that start with file name, "<autogenerated>"
		// and also skip traces with function name that starts with "runtime."
		if !strings.HasPrefix(file, "<autogenerated>") &&
			!strings.HasPrefix(funcName, "runtime.") {
			// Form and append a line of stack trace into a
			// collection, 'trace', to build full stack trace
			trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))
		}
		traceLevel++
		// Read stack trace information from PC
		pc, file, lineNumber, ok = runtime.Caller(traceLevel)
	}
	return trace
}

func logIf(level Level, err error, msg string,
	data ...interface{}) {

	isErrIgnored := func(err error) (ok bool) {
		err = errors.Cause(err)
		switch err.(type) {
		case BucketNotFound, BucketNotEmpty, BucketExists:
			ok = true
		case ObjectNotFound, ObjectExistsAsDirectory:
			ok = true
		case BucketPolicyNotFound, InvalidUploadID:
			ok = true
		}
		return ok
	}

	if err == nil || isErrIgnored(err) {
		return
	}
	cause := strings.Title(err.Error())
	// Get full stack trace
	trace := getTrace(3)
	// Get time
	timeOfError := UTCNow().Format(time.RFC3339Nano)
	// Output the formatted log message at console
	var output string
	message := fmt.Sprintf(msg, data...)
	if log.json {
		logJSON, err := json.Marshal(&logEntry{
			Level:   level.String(),
			Message: message,
			Time:    timeOfError,
			Cause:   cause,
			Trace:   trace,
		})
		if err != nil {
			panic("json marshal of logEntry failed: " + err.Error())
		}
		output = string(logJSON)
	} else {
		// Add a sequence number and formatting for each stack trace
		// No formatting is required for the first entry
		trace[0] = "1: " + trace[0]
		for i, element := range trace[1:] {
			trace[i+1] = fmt.Sprintf("%8v: %s", i+2, element)
		}
		errMsg := fmt.Sprintf("[%s] [%s] %s (%s)",
			timeOfError, level.String(), message, cause)

		output = fmt.Sprintf("\nTrace: %s\n%s",
			strings.Join(trace, "\n"),
			colorRed(colorBold(errMsg)))
	}
	fmt.Println(output)

	if level == Fatal {
		os.Exit(1)
	}
}

func errorIf(err error, msg string, data ...interface{}) {
	logIf(Error, err, msg, data...)
}

func fatalIf(err error, msg string, data ...interface{}) {
	logIf(Fatal, err, msg, data...)
}
<commit_msg>Removes capitalization of error causes (#5468)<commit_after>/*
 * Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/errors"
)

var log = NewLogger()
var trimStrings []string

// Level type
type Level int8

// Enumerated level types
const (
	Error Level = iota + 1
	Fatal
)

func (level Level) String() string {
	var lvlStr string
	switch level {
	case Error:
		lvlStr = "ERROR"
	case Fatal:
		lvlStr = "FATAL"
	}
	return lvlStr
}

type logEntry struct {
	Level   string   `json:"level"`
	Message string   `json:"message"`
	Time    string   `json:"time"`
	Cause   string   `json:"cause"`
	Trace   []string `json:"trace"`
}

// Logger - for console messages
type Logger struct {
	quiet bool
	json  bool
}

// NewLogger - to create a new Logger object
func NewLogger() *Logger {
	return &Logger{}
}

// EnableQuiet - turns quiet option on.
func (log *Logger) EnableQuiet() {
	log.quiet = true
}

// EnableJSON - outputs logs in json format.
func (log *Logger) EnableJSON() {
	log.json = true
	log.quiet = true
}

// Println - wrapper to console.Println() with quiet flag.
func (log *Logger) Println(args ...interface{}) {
	if !log.quiet {
		console.Println(args...)
	}
}

// Printf - wrapper to console.Printf() with quiet flag.
func (log *Logger) Printf(format string, args ...interface{}) {
	if !log.quiet {
		console.Printf(format, args...)
	}
}

func init() {
	var goPathList []string
	// Add all possible GOPATH paths into trimStrings
	// Split GOPATH depending on the OS type
	if runtime.GOOS == "windows" {
		goPathList = strings.Split(GOPATH, ";")
	} else {
		// All other types of OSs
		goPathList = strings.Split(GOPATH, ":")
	}

	// Add trim string "{GOROOT}/src/" into trimStrings
	trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}

	// Add all possible path from GOPATH=path1:path2...:pathN
	// as "{path#}/src/" into trimStrings
	for _, goPathString := range goPathList {
		trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
	}
	// Add "github.com/minio/minio" as the last to cover
	// paths like "{GOROOT}/src/github.com/minio/minio"
	// and "{GOPATH}/src/github.com/minio/minio"
	trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
}

func trimTrace(f string) string {
	for _, trimString := range trimStrings {
		f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
	}
	return filepath.FromSlash(f)
}

// getTrace method - creates and returns stack trace
func getTrace(traceLevel int) []string {
	var trace []string
	pc, file, lineNumber, ok := runtime.Caller(traceLevel)

	for ok {
		// Clean up the common prefixes
		file = trimTrace(file)
		// Get the function name
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		// Skip duplicate traces that start with file name, "<autogenerated>"
		// and also skip traces with function name that starts with "runtime."
		if !strings.HasPrefix(file, "<autogenerated>") &&
			!strings.HasPrefix(funcName, "runtime.") {
			// Form and append a line of stack trace into a
			// collection, 'trace', to build full stack trace
			trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))
		}
		traceLevel++
		// Read stack trace information from PC
		pc, file, lineNumber, ok = runtime.Caller(traceLevel)
	}
	return trace
}

func logIf(level Level, err error, msg string,
	data ...interface{}) {

	isErrIgnored := func(err error) (ok bool) {
		err = errors.Cause(err)
		switch err.(type) {
		case BucketNotFound, BucketNotEmpty, BucketExists:
			ok = true
		case ObjectNotFound, ObjectExistsAsDirectory:
			ok = true
		case BucketPolicyNotFound, InvalidUploadID:
			ok = true
		}
		return ok
	}

	if err == nil || isErrIgnored(err) {
		return
	}
	// Get the cause for the Error
	cause := err.Error()
	// Get full stack trace
	trace := getTrace(3)
	// Get time
	timeOfError := UTCNow().Format(time.RFC3339Nano)
	// Output the formatted log message at console
	var output string
	message := fmt.Sprintf(msg, data...)
	if log.json {
		logJSON, err := json.Marshal(&logEntry{
			Level:   level.String(),
			Message: message,
			Time:    timeOfError,
			Cause:   cause,
			Trace:   trace,
		})
		if err != nil {
			panic("json marshal of logEntry failed: " + err.Error())
		}
		output = string(logJSON)
	} else {
		// Add a sequence number and formatting for each stack trace
		// No formatting is required for the first entry
		trace[0] = "1: " + trace[0]
		for i, element := range trace[1:] {
			trace[i+1] = fmt.Sprintf("%8v: %s", i+2, element)
		}
		errMsg := fmt.Sprintf("[%s] [%s] %s (%s)",
			timeOfError, level.String(), message, cause)

		output = fmt.Sprintf("\nTrace: %s\n%s",
			strings.Join(trace, "\n"),
			colorRed(colorBold(errMsg)))
	}
	fmt.Println(output)

	if level == Fatal {
		os.Exit(1)
	}
}

func errorIf(err error, msg string, data ...interface{}) {
	logIf(Error, err, msg, data...)
}

func fatalIf(err error, msg string, data ...interface{}) {
	logIf(Fatal, err, msg, data...)
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
	"github.com/tsaikd/KDGoLib/cliutil/cobrather"
	"github.com/tsaikd/gobuilder/cmd/modBuild"
	"github.com/tsaikd/gobuilder/cmd/modCheckError"
	"github.com/tsaikd/gobuilder/cmd/modDep"
	"github.com/tsaikd/gobuilder/cmd/modGet"
	"github.com/tsaikd/gobuilder/cmd/modRestore"
)

// Module info
var Module = &cobrather.Module{
	Use:   "gobuilder",
	Short: "Go application builder, run action: restore -> get -> build",
	Commands: []*cobrather.Module{
		modDep.Module,
		modCheckError.Module,
		modRestore.Module,
		modGet.Module,
		modBuild.Module,
		cobrather.VersionModule,
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		runFuncs := []func(cmd *cobra.Command, args []string) error{}

		cmdModules := []*cobrather.Module{
			modRestore.Module,
			modGet.Module,
			modBuild.Module,
		}
		depModules := cobrather.ListDeps(cobrather.OIncludeDepInCommand, cmdModules...)
		preRun := cobrather.GenRunE(depModules...)
		runFuncs = append(runFuncs, preRun)

		run := cobrather.GenRunE(cmdModules...)
		runFuncs = append(runFuncs, run)

		postRun := cobrather.GenPostRunE(cmdModules...)
		runFuncs = append(runFuncs, postRun)

		depPostRun := cobrather.GenPostRunE(depModules...)
		runFuncs = append(runFuncs, depPostRun)

		for _, fn := range runFuncs {
			if err := fn(cmd, args); err != nil {
				return err
			}
		}

		return nil
	},
}
<commit_msg>add option check for default action<commit_after>package cmd

import (
	"github.com/spf13/cobra"
	"github.com/tsaikd/KDGoLib/cliutil/cobrather"
	"github.com/tsaikd/gobuilder/cmd/modBuild"
	"github.com/tsaikd/gobuilder/cmd/modCheckError"
	"github.com/tsaikd/gobuilder/cmd/modDep"
	"github.com/tsaikd/gobuilder/cmd/modGet"
	"github.com/tsaikd/gobuilder/cmd/modRestore"
)

// command line flags
var (
	FlagCheck = &cobrather.BoolFlag{
		Name:      "check",
		ShortHand: "c",
		Default:   false,
		Usage:     "Run check actions before build actions: checkerror",
	}
)

// Module info
var Module = &cobrather.Module{
	Use:   "gobuilder",
	Short: "Go application builder, run action: restore -> get -> build",
	Commands: []*cobrather.Module{
		modDep.Module,
		modCheckError.Module,
		modRestore.Module,
		modGet.Module,
		modBuild.Module,
		cobrather.VersionModule,
	},
	Flags: []cobrather.Flag{
		FlagCheck,
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		runFuncs := []func(cmd *cobra.Command, args []string) error{}

		cmdModules := []*cobrather.Module{}
		if FlagCheck.Bool() {
			cmdModules = append(cmdModules,
				modCheckError.Module,
			)
		}
		cmdModules = append(cmdModules,
			modRestore.Module,
			modGet.Module,
			modBuild.Module,
		)

		depModules := cobrather.ListDeps(cobrather.OIncludeDepInCommand, cmdModules...)
		preRun := cobrather.GenRunE(depModules...)
		runFuncs = append(runFuncs, preRun)

		run := cobrather.GenRunE(cmdModules...)
		runFuncs = append(runFuncs, run)

		postRun := cobrather.GenPostRunE(cmdModules...)
		runFuncs = append(runFuncs, postRun)

		depPostRun := cobrather.GenPostRunE(depModules...)
		runFuncs = append(runFuncs, depPostRun)

		for _, fn := range runFuncs {
			if err := fn(cmd, args); err != nil {
				return err
			}
		}

		return nil
	},
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/zquestz/s/providers"
)

const (
	appName = "s"
	version = "0.1.8"
)

// Flag variables
var displayVersion bool
var verbose bool
var provider string
var listProviders bool
var binary string

// Main command for Cobra.
var SearchCmd = &cobra.Command{
	Use:   "s <query>",
	Short: "Web search from the terminal",
	Long:  `Web search from the terminal.`,
	Run: func(cmd *cobra.Command, args []string) {
		err := performCommand(cmd, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[Error] %s\n", err)
			os.Exit(1)
		}
	},
}

func init() {
	prepareFlags()
}

func prepareFlags() {
	SearchCmd.PersistentFlags().BoolVarP(
		&displayVersion, "version", "", false, "display version")
	SearchCmd.PersistentFlags().BoolVarP(
		&verbose, "verbose", "v", false, "display url when opening")
	SearchCmd.PersistentFlags().StringVarP(
		&provider, "provider", "p", "google", "set search provider")
	SearchCmd.PersistentFlags().BoolVarP(
		&listProviders, "list-providers", "l", false, "list supported providers")
	SearchCmd.PersistentFlags().StringVarP(
		&binary, "binary", "b", "", "binary to launch search uri")
}

// Where all the work happens.
func performCommand(cmd *cobra.Command, args []string) error {
	if displayVersion {
		fmt.Printf("%s %s\n", appName, version)
		return nil
	}

	if listProviders {
		fmt.Printf(providers.DisplayProviders())
		return nil
	}

	query := strings.Join(args, " ")

	if query != "" {
		err := providers.Search(binary, provider, query, verbose)
		if err != nil {
			return err
		}
	} else {
		// Don't return an error, help screen is more appropriate.
		cmd.Help()
	}

	return nil
}
<commit_msg>Add support for getting search query from stdin<commit_after>package cmd

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

	"github.com/spf13/cobra"
	"github.com/zquestz/s/providers"
)

const (
	appName = "s"
	version = "0.1.8"
)

// Flag variables
var displayVersion bool
var verbose bool
var provider string
var listProviders bool
var binary string

// Main command for Cobra.
var SearchCmd = &cobra.Command{
	Use:   "s <query>",
	Short: "Web search from the terminal",
	Long:  `Web search from the terminal.`,
	Run: func(cmd *cobra.Command, args []string) {
		err := performCommand(cmd, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[Error] %s\n", err)
			os.Exit(1)
		}
	},
}

func init() {
	prepareFlags()
}

func prepareFlags() {
	SearchCmd.PersistentFlags().BoolVarP(
		&displayVersion, "version", "", false, "display version")
	SearchCmd.PersistentFlags().BoolVarP(
		&verbose, "verbose", "v", false, "display url when opening")
	SearchCmd.PersistentFlags().StringVarP(
		&provider, "provider", "p", "google", "set search provider")
	SearchCmd.PersistentFlags().BoolVarP(
		&listProviders, "list-providers", "l", false, "list supported providers")
	SearchCmd.PersistentFlags().StringVarP(
		&binary, "binary", "b", "", "binary to launch search uri")
}

// Where all the work happens.
func performCommand(cmd *cobra.Command, args []string) error {
	if displayVersion {
		fmt.Printf("%s %s\n", appName, version)
		return nil
	}

	if listProviders {
		fmt.Printf(providers.DisplayProviders())
		return nil
	}

	query := strings.Join(args, " ")

	st, err := os.Stdin.Stat()
	if err == nil && st.Mode()&os.ModeNamedPipe != 0 {
		bytes, _ := ioutil.ReadAll(os.Stdin)
		query = strings.TrimSpace(query + " " + string(bytes))
	}
	if query != "" {
		err := providers.Search(binary, provider, query, verbose)
		if err != nil {
			return err
		}
	} else {
		// Don't return an error, help screen is more appropriate.
		cmd.Help()
	}

	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os/exec"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/wataru0225/sreq/config"
	"github.com/wataru0225/sreq/snippet"
)

var pagenation int
var argument string

type Qiita struct {
	Title string `json: "title"`
	Url   string `json: "url"`
}

var searchCmd = &cobra.Command{
	Use:   "search",
	Short: "Call Api of Qiita",
	Long:  "Call Api of Qiita",
	Run: func(cmd *cobra.Command, args []string) {
		pagenation = 1
		argument = strings.Join(args, ",")
		execute()
	},
}

func init() {
	RootCmd.AddCommand(searchCmd)
}

func execute() {
	resp, err := http.Get("http://qiita.com/api/v2/items?page=" + strconv.Itoa(pagenation) + "&per_page=10&query=" + argument)
	if err == nil {
		defer resp.Body.Close()
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
			rendering(b)
		}
	}
}

func rendering(b []byte) {
	var contents []Qiita
	json.Unmarshal(b, &contents)
	for i, c := range contents {
		fmt.Print(color.YellowString(strconv.Itoa(i) + " -> "))
		fmt.Println(c.Title)
	}
	if len(contents) == 10 {
		fmt.Println(color.YellowString("n -> ") + "next page")
	}
	fmt.Print("SELECT > ")
	scan(contents)
}

func scan(content []Qiita) {
	var num string
	_, err := fmt.Scanf("%s", &num)
	if err == nil {
		if num == "n" {
			pagenation++
			execute()
		} else {
			numb, _ := strconv.Atoi(num)
			writeKeyword()
			url := writeHistory(content[numb])
			exec.Command("open", url).Run()
		}
	} else {
		fmt.Println(err)
	}
}

func writeHistory(content Qiita) string {
	var snippets snippet.Snippets
	file := config.HistoryFile()
	snippets.Load(file)
	url := content.Url
	newSnippet := snippet.SnippetInfo{
		SearchKeyword: argument,
		Url:           url,
		Title:         content.Title,
	}
	snippets.Snippets = append(snippets.Snippets, newSnippet)
	if err := snippets.Save(file); err != nil {
		fmt.Errorf("Failed. %v", err)
	}
	return url
}

func writeKeyword() {
	var snippets snippet.Snippets
	file := config.KeywordFile()
	snippets.Load(file)
	newSnippet := snippet.SnippetInfo{
		SearchKeyword: argument,
	}
	snippets.Snippets = append(snippets.Snippets, newSnippet)
	if err := snippets.Save(file); err != nil {
		fmt.Errorf("Failed. %v", err)
	}
}
<commit_msg>Add show description when searching<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os/exec"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/wataru0225/sreq/config"
	"github.com/wataru0225/sreq/snippet"
)

var pagenation int
var argument string

type Qiita struct {
	Title string `json: "title"`
	Url   string `json: "url"`
	Body  string `json: "body"`
}

var searchCmd = &cobra.Command{
	Use:   "search",
	Short: "Call Api of Qiita",
	Long:  "Call Api of Qiita",
	Run: func(cmd *cobra.Command, args []string) {
		pagenation = 1
		argument = strings.Join(args, ",")
		execute()
	},
}

func init() {
	RootCmd.AddCommand(searchCmd)
}

func execute() {
	resp, err := http.Get("http://qiita.com/api/v2/items?page=" + strconv.Itoa(pagenation) + "&per_page=10&query=" + argument)
	if err == nil {
		defer resp.Body.Close()
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
			rendering(b)
		}
	}
}

func rendering(b []byte) {
	var contents []Qiita
	json.Unmarshal(b, &contents)
	for i, c := range contents {
		fmt.Print(color.YellowString(strconv.Itoa(i) + " -> "))
		fmt.Println(c.Title)
		if count := len(c.Body); count > 256 {
			fmt.Println(color.GreenString(strings.Replace(c.Body, "\n", "", -1)[0:256]))
		} else {
			fmt.Println(color.GreenString(strings.Replace(c.Body, "\n", "", -1)))
		}
		fmt.Print("\n")
	}
	if len(contents) == 10 {
		fmt.Println(color.YellowString("n -> ") + "next page")
	}
	fmt.Print("SELECT > ")
	scan(contents)
}

func scan(content []Qiita) {
	var num string
	_, err := fmt.Scanf("%s", &num)
	if err == nil {
		if num == "n" {
			pagenation++
			execute()
		} else {
			numb, _ := strconv.Atoi(num)
			writeKeyword()
			url := writeHistory(content[numb])
			exec.Command("open", url).Run()
		}
	} else {
		fmt.Println(err)
	}
}

func writeHistory(content Qiita) string {
	var snippets snippet.Snippets
	file := config.HistoryFile()
	snippets.Load(file)
	url := content.Url
	newSnippet := snippet.SnippetInfo{
		SearchKeyword: argument,
		Url:           url,
		Title:         content.Title,
	}
	snippets.Snippets = append(snippets.Snippets, newSnippet)
	if err := snippets.Save(file); err != nil {
		fmt.Errorf("Failed. %v", err)
	}
	return url
}

func writeKeyword() {
	var snippets snippet.Snippets
	file := config.KeywordFile()
	snippets.Load(file)
	newSnippet := snippet.SnippetInfo{
		SearchKeyword: argument,
	}
	snippets.Snippets = append(snippets.Snippets, newSnippet)
	if err := snippets.Save(file); err != nil {
		fmt.Errorf("Failed. %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"fmt"
	"os"
	"text/tabwriter"
	"text/template"

	"github.com/hjma29/ovcli/oneview"
	"github.com/spf13/cobra"
)

var showICPortCmd = &cobra.Command{
	Use:   "port",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: showICPort,
}

var showSFPCmd = &cobra.Command{
	Use:   "sfp",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: showSFP,
}

const (
	icShowFormat = "" +
		"Name\tModel\tLogical Interconnect\n" +
		//"----\t-----\n" +
		"{{range .}}" +
		"{{.Name}}\t{{.ProductName}}\t{{.LIName}}\n" +
		"{{end}}"

	//portshowFormat will loop map["IC name"]*{IC struct{[]sliceof{port struct} }}
	portShowFormat = "" +
		"{{range .}}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tConnectorType\tPortStatus\tPortType\tNeighbor\tNeighbor Port\n" +
		"{{range .Ports}}" +
		//"{{if eq .PortType porttype}}" +
		"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\n" +
		//"{{end}}" +
		"{{end}}" +
		"\n" +
		"{{end}}"

	//sfpShowFormat is to display icsfpMap, which  is mapping between each module and its own port mapping table, such as map["module 1, top frame"]*map[d1]struct{for d1}
	sfpShowFormat = "" +
		"{{range .}}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tVendorName\tVendorPartNumber\tVendorRevision\tSpeed\n" +
		"{{range .SFPList}}" +
		"{{.PortName}}\t{{.VendorName}}\t{{.VendorPartNumber}}\t{{.VendorRevision}}\t{{.Speed}}\n" +
		//"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\n" +
		"{{end}}" +
		"\n" +
		"{{end}}"

	uplinkShowFormat = "" +
		"{{range .}}" +
		"{{if ne .ProductName \"Synergy 20Gb Interconnect Link Module\" }}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tConnectorType\tPortStatus\tPortType\tNeighbor\tNeighbor Port\tTransceiver\n" +
		"{{range .Ports}}" +
		"{{if or (eq .PortType \"Uplink\") (eq .PortType \"Stacking\") }}" +
		//"{{if eq .PortType Uplink }}" +
		"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\t{{.TransceiverPN}}\n" +
		"{{end}}" +
		"{{end}}" +
		"\n" +
		"{{end}}" +
		"{{end}}"
)

func NewShowICCmd(c *oneview.CLIOVClient) *cobra.Command {

	var cmd = &cobra.Command{
		Use:   "interconnect",
		Short: "show Interconnect",
		Long:  `show Interconnect`,
		Run: func(cmd *cobra.Command, args []string) {

			c := verifyClient(c)

			icList := c.GetIC()

			tw := tabwriter.NewWriter(c.Out, 5, 1, 3, ' ', 0)
			defer tw.Flush()

			t := template.Must(template.New("").Parse(icShowFormat))
			t.Execute(tw, icList)
		},
	}

	cmd.Flags().StringVarP(&porttype, "type", "t", "all", "Port Type:uplink,downlink,interconnect,all")

	cmd.AddCommand(showICPortCmd)

	return cmd

}

func showICPort(cmd *cobra.Command, args []string) {
	var showdata interface{}
	var showformat string

	switch porttype {

	case "uplink":
		icPortMap := oneview.GetICPort()
		showdata = icPortMap
		showformat = uplinkShowFormat
		// uplinkPortMap := oneview.GetICPortUplinkShow()
		// showdata = uplinkPortMap
	case "downlink":
	case "interconnect":
	case "all":
		icList := oneview.GetICPort()
		showdata = icList
		showformat = portShowFormat
	default:
		fmt.Println("invalid port type option")

	}

	tw := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	defer tw.Flush()

	// fmap := template.FuncMap{
	// 	"filterPort": filterPort
	// }

	t := template.Must(template.New("").Parse(showformat))
	t.Execute(tw, showdata)
}

//func filterPort()

func showSFP(cmd *cobra.Command, args []string) {
	icList := oneview.GetSFP()

	//fmt.Println(modTransMap)

	tw := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	defer tw.Flush()

	t := template.Must(template.New("").Parse(sfpShowFormat))
	t.Execute(tw, icList)

}

func init() {

	showICPortCmd.AddCommand(showSFPCmd)

}
<commit_msg>quick add<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"fmt"
	"os"
	"text/tabwriter"
	"text/template"

	"github.com/hjma29/ovcli/oneview"
	"github.com/spf13/cobra"
)

var showICPortCmd = &cobra.Command{
	Use:   "port",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: showICPort,
}

var showSFPCmd = &cobra.Command{
	Use:   "sfp",
	Short: "A brief description of your command",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: showSFP,
}

const (
	icShowFormat = "" +
		"Name\tModel\tLogical Interconnect\n" +
		//"----\t-----\n" +
		"{{range .}}" +
		"{{.Name}}\t{{.ProductName}}\t{{.LIName}}\n" +
		"{{end}}"

	//portshowFormat will loop map["IC name"]*{IC struct{[]sliceof{port struct} }}
	portShowFormat = "" +
		"{{range .}}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tConnectorType\tPortStatus\tPortType\tNeighbor\tNeighbor Port\n" +
		"{{range .Ports}}" +
		//"{{if eq .PortType porttype}}" +
		"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\n" +
		//"{{end}}" +
		"{{end}}" +
		"\n" +
		"{{end}}"

	//sfpShowFormat is to display icsfpMap, which  is mapping between each module and its own port mapping table, such as map["module 1, top frame"]*map[d1]struct{for d1}
	sfpShowFormat = "" +
		"{{range .}}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tVendorName\tVendorPartNumber\tVendorRevision\tSpeed\n" +
		"{{range .SFPList}}" +
		"{{.PortName}}\t{{.VendorName}}\t{{.VendorPartNumber}}\t{{.VendorRevision}}\t{{.Speed}}\n" +
		//"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\n" +
		"{{end}}" +
		"\n" +
		"{{end}}"

	uplinkShowFormat = "" +
		"{{range .}}" +
		"{{if ne .ProductName \"Synergy 20Gb Interconnect Link Module\" }}" +
		"-------------\n" +
		"Interconnect: {{.Name}} ({{.ProductName}})\n" +
		"-------------\n" +
		"PortName\tConnectorType\tPortStatus\tPortType\tNeighbor\tNeighbor Port\tTransceiver\n" +
		"{{range .Ports}}" +
		"{{if or (eq .PortType \"Uplink\") (eq .PortType \"Stacking\") }}" +
		//"{{if eq .PortType Uplink }}" +
		"{{.PortName}}\t{{.ConnectorType}}\t{{.PortStatus}}\t{{.PortType}}\t{{.Neighbor.RemoteSystemName}}\t{{.Neighbor.RemotePortID}}\t{{.TransceiverPN}}\n" +
		"{{end}}" +
		"{{end}}" +
		"\n" +
		"{{end}}" +
		"{{end}}"
)

func NewShowICCmd(c *oneview.CLIOVClient) *cobra.Command {

	var cmd = &cobra.Command{
		Use:   "interconnect",
		Short: "show Interconnect",
		Long:  `show Interconnect`,
		Run: func(cmd *cobra.Command, args []string) {

			c := verifyClient(c)

			icList := c.GetIC()

			tw := tabwriter.NewWriter(c.Out, 5, 1, 3, ' ', 0)
			defer tw.Flush()

			t := template.Must(template.New("").Parse(icShowFormat))
			t.Execute(tw, icList)
		},
	}

	cmd.AddCommand(showICPortCmd)

	return cmd

}

func showICPort(cmd *cobra.Command, args []string) {
	var showdata interface{}
	var showformat string

	switch porttype {

	case "uplink":
		icPortMap := oneview.GetICPort()
		showdata = icPortMap
		showformat = uplinkShowFormat
		// uplinkPortMap := oneview.GetICPortUplinkShow()
		// showdata = uplinkPortMap
	case "downlink":
	case "interconnect":
	case "all":
		icList := oneview.GetICPort()
		showdata = icList
		showformat = portShowFormat
	default:
		fmt.Println("invalid port type option")

	}

	tw := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	defer tw.Flush()

	// fmap := template.FuncMap{
	// 	"filterPort": filterPort
	// }

	t := template.Must(template.New("").Parse(showformat))
	t.Execute(tw, showdata)
}

//func filterPort()

func showSFP(cmd *cobra.Command, args []string) {
	icList := oneview.GetSFP()

	//fmt.Println(modTransMap)

	tw := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	defer tw.Flush()

	t := template.Must(template.New("").Parse(sfpShowFormat))
	t.Execute(tw, icList)

}

func init() {

	showICPortCmd.AddCommand(showSFPCmd)

	showICPortCmd.Flags().StringVarP(&porttype, "type", "t", "all", "Port Type:uplink,downlink,interconnect,all")

}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// submitCmd lets people upload a solution to the website.
var submitCmd = &cobra.Command{
	Use:     "submit",
	Aliases: []string{"s"},
	Short:   "Submit your solution to an exercise.",
	Long: `Submit your solution to an Exercism exercise.

	Call the command with the list of files you want to submit.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfiguration()

		usrCfg := viper.New()
		usrCfg.AddConfigPath(cfg.Dir)
		usrCfg.SetConfigName("user")
		usrCfg.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = usrCfg.ReadInConfig()
		cfg.UserViperConfig = usrCfg

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("cli")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()

		return runSubmit(cfg, cmd.Flags(), args)
	},
}

func runSubmit(cfg config.Configuration, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig

	if usrCfg.GetString("token") == "" {
		tokenURL := config.InferSiteURL(usrCfg.GetString("apibaseurl")) + "/my/settings"
		return fmt.Errorf(msgWelcomePleaseConfigure, tokenURL, BinaryName)
	}

	if usrCfg.GetString("workspace") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	for i, arg := range args {
		var err error
		arg, err = filepath.Abs(arg)
		if err != nil {
			return err
		}

		info, err := os.Lstat(arg)
		if err != nil {
			if os.IsNotExist(err) {
				msg := `

    The file you are trying to submit cannot be found.

        %s

		`
				return fmt.Errorf(msg, arg)
			}
			return err
		}
		if info.IsDir() {
			msg := `

    You are submitting a directory, which is not currently supported.

        %s

			`
			return fmt.Errorf(msg, arg)
		}

		src, err := filepath.EvalSymlinks(arg)
		if err != nil {
			return err
		}
		args[i] = src
	}

	ws, err := workspace.New(usrCfg.GetString("workspace"))
	if err != nil {
		return err
	}

	var exerciseDir string
	for _, arg := range args {
		dir, err := ws.SolutionDir(arg)
		if err != nil {
			if workspace.IsMissingMetadata(err) {
				return errors.New(msgMissingMetadata)
			}
			return err
		}
		if exerciseDir != "" && dir != exerciseDir {
			msg := `

    You are submitting files belonging to different solutions.
    Please submit the files for one solution at a time.

		`
			return errors.New(msg)
		}
		exerciseDir = dir
	}

	dirs, err := ws.Locate(exerciseDir)
	if err != nil {
		return err
	}

	sx, err := workspace.NewSolutions(dirs)
	if err != nil {
		return err
	}
	if len(sx) > 1 {
		msg := `

    You are submitting files belonging to different solutions.
    Please submit the files for one solution at a time.

		`
		return errors.New(msg)
	}
	solution := sx[0]

	if !solution.IsRequester {
		// TODO: add test
		msg := `

    The solution you are submitting is not connected to your account.
    Please re-download the exercise to make sure it has the data it needs.

        %s download --exercise=%s --track=%s

		`
		return fmt.Errorf(msg, BinaryName, solution.Exercise, solution.Track)
	}

	paths := make([]string, 0, len(args))
	for _, file := range args {
		// Don't submit empty files
		info, err := os.Stat(file)
		if err != nil {
			return err
		}
		if info.Size() == 0 {

			msg := `

    WARNING: Skipping empty file
             %s

		`
			fmt.Fprintf(Err, msg, file)
			continue
		}
		paths = append(paths, file)
	}

	if len(paths) == 0 {
		msg := `

    No files found to submit.

		`
		return errors.New(msg)
	}

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	for _, path := range paths {
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		dirname := fmt.Sprintf("%s%s%s", string(os.PathSeparator), solution.Exercise, string(os.PathSeparator))
		pieces := strings.Split(path, dirname)
		filename := fmt.Sprintf("%s%s", string(os.PathSeparator), pieces[len(pieces)-1])

		part, err := writer.CreateFormFile("files[]", filename)
		if err != nil {
			return err
		}
		_, err = io.Copy(part, file)
		if err != nil {
			return err
		}
	}

	err = writer.Close()
	if err != nil {
		return err
	}

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), solution.ID)
	req, err := client.NewRequest("PATCH", url, body)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	bb := &bytes.Buffer{}
	_, err = bb.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	msg := `

    Your solution has been submitted successfully.
    %s
`
	suffix := "View it at:\n\n    "
	if solution.AutoApprove {
		suffix = "You can complete the exercise and unlock the next core exercise at:\n"
	}
	fmt.Fprintf(Err, msg, suffix)
	fmt.Fprintf(Out, "    %s\n\n", solution.URL)
	return nil
}

func initSubmitCmd() {
	setupSubmitFlags(submitCmd.Flags())
}

func setupSubmitFlags(flags *pflag.FlagSet) {
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise ID")
	flags.StringSliceP("files", "f", make([]string, 0), "files to submit")
}

func init() {
	RootCmd.AddCommand(submitCmd)
}
<commit_msg>Delete unnecessary flags in submit command (#654)<commit_after>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// submitCmd lets people upload a solution to the website.
var submitCmd = &cobra.Command{
	Use:     "submit",
	Aliases: []string{"s"},
	Short:   "Submit your solution to an exercise.",
	Long: `Submit your solution to an Exercism exercise.

	Call the command with the list of files you want to submit.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfiguration()

		usrCfg := viper.New()
		usrCfg.AddConfigPath(cfg.Dir)
		usrCfg.SetConfigName("user")
		usrCfg.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = usrCfg.ReadInConfig()
		cfg.UserViperConfig = usrCfg

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("cli")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()

		return runSubmit(cfg, cmd.Flags(), args)
	},
}

func runSubmit(cfg config.Configuration, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig

	if usrCfg.GetString("token") == "" {
		tokenURL := config.InferSiteURL(usrCfg.GetString("apibaseurl")) + "/my/settings"
		return fmt.Errorf(msgWelcomePleaseConfigure, tokenURL, BinaryName)
	}

	if usrCfg.GetString("workspace") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	for i, arg := range args {
		var err error
		arg, err = filepath.Abs(arg)
		if err != nil {
			return err
		}

		info, err := os.Lstat(arg)
		if err != nil {
			if os.IsNotExist(err) {
				msg := `

    The file you are trying to submit cannot be found.

        %s

		`
				return fmt.Errorf(msg, arg)
			}
			return err
		}
		if info.IsDir() {
			msg := `

    You are submitting a directory, which is not currently supported.

        %s

			`
			return fmt.Errorf(msg, arg)
		}

		src, err := filepath.EvalSymlinks(arg)
		if err != nil {
			return err
		}
		args[i] = src
	}

	ws, err := workspace.New(usrCfg.GetString("workspace"))
	if err != nil {
		return err
	}

	var exerciseDir string
	for _, arg := range args {
		dir, err := ws.SolutionDir(arg)
		if err != nil {
			if workspace.IsMissingMetadata(err) {
				return errors.New(msgMissingMetadata)
			}
			return err
		}
		if exerciseDir != "" && dir != exerciseDir {
			msg := `

    You are submitting files belonging to different solutions.
    Please submit the files for one solution at a time.

		`
			return errors.New(msg)
		}
		exerciseDir = dir
	}

	dirs, err := ws.Locate(exerciseDir)
	if err != nil {
		return err
	}

	sx, err := workspace.NewSolutions(dirs)
	if err != nil {
		return err
	}
	if len(sx) > 1 {
		msg := `

    You are submitting files belonging to different solutions.
    Please submit the files for one solution at a time.

		`
		return errors.New(msg)
	}
	solution := sx[0]

	if !solution.IsRequester {
		// TODO: add test
		msg := `

    The solution you are submitting is not connected to your account.
    Please re-download the exercise to make sure it has the data it needs.

        %s download --exercise=%s --track=%s

		`
		return fmt.Errorf(msg, BinaryName, solution.Exercise, solution.Track)
	}

	paths := make([]string, 0, len(args))
	for _, file := range args {
		// Don't submit empty files
		info, err := os.Stat(file)
		if err != nil {
			return err
		}
		if info.Size() == 0 {

			msg := `

    WARNING: Skipping empty file
             %s

		`
			fmt.Fprintf(Err, msg, file)
			continue
		}
		paths = append(paths, file)
	}

	if len(paths) == 0 {
		msg := `

    No files found to submit.

		`
		return errors.New(msg)
	}

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	for _, path := range paths {
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		dirname := fmt.Sprintf("%s%s%s", string(os.PathSeparator), solution.Exercise, string(os.PathSeparator))
		pieces := strings.Split(path, dirname)
		filename := fmt.Sprintf("%s%s", string(os.PathSeparator), pieces[len(pieces)-1])

		part, err := writer.CreateFormFile("files[]", filename)
		if err != nil {
			return err
		}
		_, err = io.Copy(part, file)
		if err != nil {
			return err
		}
	}

	err = writer.Close()
	if err != nil {
		return err
	}

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), solution.ID)
	req, err := client.NewRequest("PATCH", url, body)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	bb := &bytes.Buffer{}
	_, err = bb.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	msg := `

    Your solution has been submitted successfully.
    %s
`
	suffix := "View it at:\n\n    "
	if solution.AutoApprove {
		suffix = "You can complete the exercise and unlock the next core exercise at:\n"
	}
	fmt.Fprintf(Err, msg, suffix)
	fmt.Fprintf(Out, "    %s\n\n", solution.URL)
	return nil
}

func init() {
	RootCmd.AddCommand(submitCmd)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

// `direnv stdlib`
var CmdStdlib = &Cmd{
	Name:    "stdlib",
	Desc:    "Outputs the stdlib that is available in the .envrc",
	Private: true,
	Fn: func(env Env, args []string) (err error) {
		var config *Config
		if config, err = LoadConfig(env); err != nil {
			return
		}

		fmt.Printf(STDLIB, config.SelfPath)
		return
	},
}

const STDLIB = `
# These are the commands available in an .envrc context
set -e
DIRENV_PATH="%s"

# Determines if "something" is availabe as a command
#
# Usage: has something
has() {
	type "$1" &>/dev/null
}

# Usage: expand_path ./rel/path [RELATIVE_TO]
# RELATIVE_TO is $PWD by default
expand_path() {
	"$DIRENV_PATH" expand_path "$@"
}

# Loads a .env in the current environment
#
# Usage: dotenv
dotenv() {
	eval "$("$DIRENV_PATH" dotenv "$@")"
}

# Usage: user_rel_path /Users/you/some_path => ~/some_path
user_rel_path() {
	local path="${1#-}"

	if [ -z "$path" ]; then return; fi

	if [ -n "$HOME" ]; then
		local rel_path="${path#$HOME}"
		if [ "$rel_path" != "$path" ]; then
			path="~${rel_path}"
		fi
	fi

	echo $path
}

# Usage: find_up FILENAME
find_up() {
	(
		cd "$(pwd -P 2>/dev/null)"
		while true; do
			if [ -f "$1" ]; then
				echo $PWD/$1
				return 0
			fi
			if [ "$PWD" = "/" ] || [ "$PWD" = "//" ]; then
				return 1
			fi
			cd ..
		done
	)
}

# Inherit another .envrc
#
# Usage: source_env <FILE_OR_DIR_PATH>
source_env() {
	local rcfile="$1"
	local rcpath="${1/#\~/$HOME}"
	if ! [ -f "$rcpath" ]; then
		rcfile="$rcfile/.envrc"
		rcpath="$rcpath/.envrc"
	fi
	echo "direnv: loading $rcfile"
	pushd "$(dirname "$rcpath")" > /dev/null
	. "./$(basename "$rcpath")"
	popd > /dev/null
}

# Inherits the first .envrc (or given FILENAME) it finds in the path
#
# Usage: source_up [FILENAME]
source_up() {
	local file="$1"
	if [ -z "$file" ]; then
		file=".envrc"
	fi
	local path="$(cd .. && find_up "$file")"
	if [ -n "$path" ]; then
		source_env "$(user_rel_path "$path")"
	fi
}

# Safer PATH handling
#
# Usage: PATH_add PATH
# Example: PATH_add bin
PATH_add() {
	export PATH="$(expand_path "$1"):$PATH"
}

# Safer path handling
#
# Usage: path_add VARNAME PATH
# Example: path_add LD_LIBRARY_PATH ./lib
path_add() {
	local old_paths="${!1}"
	local path="$(expand_path "$2")"

	if [ -z "$old_paths" ]; then
		old_paths="$path"
	else
		old_paths="$path:$old_paths"
	fi

	export $1="$old_paths"
}

#
# Usage: load_prefix PATH
load_prefix() {
	local path="$(expand_path "$1")"
	path_add CPATH "$path/include"
	path_add LD_LIBRARY_PATH "$path/lib"
	path_add LIBRARY_PATH "$path/lib"
	path_add MANPATH "$path/man"
	path_add MANPATH "$path/share/man"
	path_add PATH "$path/bin"
	path_add PKG_CONFIG_PATH "$path/lib/pkgconfig"
}

# Pre-programmed project layout. Add your own in your ~/.direnvrc.
#
# Usage: layout TYPE
layout() {
	eval "layout_$1"
}

# Usage: layout ruby
layout_ruby() {
	# TODO: ruby_version should be the ABI version
	local ruby_version="$(ruby -e"puts (defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby') + '-' + RUBY_VERSION")"

	export GEM_HOME="$PWD/.direnv/${ruby_version}"
	export BUNDLE_BIN="$PWD/.direnv/bin"

	PATH_add ".direnv/${ruby_version}/bin"
	PATH_add ".direnv/bin"
}

# Usage: layout python
layout_python() {
	if ! [ -d .direnv/virtualenv ]; then
		virtualenv --no-site-packages --distribute .direnv/virtualenv
		virtualenv --relocatable .direnv/virtualenv
	fi
	source .direnv/virtualenv/bin/activate
}

# Usage: layout node
layout_node() {
	PATH_add node_modules/.bin
}

# Put the project's root in the GOPATH.
#
# Usage: layout go
layout_go() {
	path_add GOPATH "$PWD"
}

# Intended to load external dependencies into the environment.
#
# Usage: use PROGRAM_NAME VERSION
# Example: use ruby 1.9.3
use() {
	local cmd="$1"
	echo "Using $@"
	shift
	use_$cmd "$@"
}

# Usage: use rbenv
use_rbenv() {
	eval "$(rbenv init -)"
}

# Sources rvm on first call. Should work like the rvm command-line.
rvm() {
	unset rvm
	if [ -n "${rvm_scripts_path:-}" ]; then
		source "${rvm_scripts_path}/rvm"
	elif [ -n "${rvm_path:-}" ]; then
		source "${rvm_path}/scripts/rvm"
	else
		source "$HOME/.rvm/scripts/rvm"
	fi
	rvm "$@"
}

## Load the global ~/.direnvrc if present
if [ -f "$HOME/.direnvrc" ]; then
	source_env "~/.direnvrc" >&2
fi
`
<commit_msg>More stdlib doc<commit_after>package main

import (
	"fmt"
)

// `direnv stdlib`
var CmdStdlib = &Cmd{
	Name:    "stdlib",
	Desc:    "Outputs the stdlib that is available in the .envrc",
	Private: true,
	Fn: func(env Env, args []string) (err error) {
		var config *Config
		if config, err = LoadConfig(env); err != nil {
			return
		}

		fmt.Printf(STDLIB, config.SelfPath)
		return
	},
}

const STDLIB = `
# These are the commands available in an .envrc context
set -e
DIRENV_PATH="%s"

# Determines if "something" is availabe as a command
#
# Usage: has something
has() {
	type "$1" &>/dev/null
}

# Usage: expand_path ./rel/path [RELATIVE_TO]
# RELATIVE_TO is $PWD by default
expand_path() {
	"$DIRENV_PATH" expand_path "$@"
}

# Loads a .env in the current environment
#
# Usage: dotenv
dotenv() {
	eval "$("$DIRENV_PATH" dotenv "$@")"
}

# Usage: user_rel_path /Users/you/some_path => ~/some_path
user_rel_path() {
	local path="${1#-}"

	if [ -z "$path" ]; then return; fi

	if [ -n "$HOME" ]; then
		local rel_path="${path#$HOME}"
		if [ "$rel_path" != "$path" ]; then
			path="~${rel_path}"
		fi
	fi

	echo $path
}

# Usage: find_up FILENAME
find_up() {
	(
		cd "$(pwd -P 2>/dev/null)"
		while true; do
			if [ -f "$1" ]; then
				echo $PWD/$1
				return 0
			fi
			if [ "$PWD" = "/" ] || [ "$PWD" = "//" ]; then
				return 1
			fi
			cd ..
		done
	)
}

# Inherit another .envrc
#
# Usage: source_env <FILE_OR_DIR_PATH>
source_env() {
	local rcfile="$1"
	local rcpath="${1/#\~/$HOME}"
	if ! [ -f "$rcpath" ]; then
		rcfile="$rcfile/.envrc"
		rcpath="$rcpath/.envrc"
	fi
	echo "direnv: loading $rcfile"
	pushd "$(dirname "$rcpath")" > /dev/null
	. "./$(basename "$rcpath")"
	popd > /dev/null
}

# Inherits the first .envrc (or given FILENAME) it finds in the path
#
# Usage: source_up [FILENAME]
source_up() {
	local file="$1"
	if [ -z "$file" ]; then
		file=".envrc"
	fi
	local path="$(cd .. && find_up "$file")"
	if [ -n "$path" ]; then
		source_env "$(user_rel_path "$path")"
	fi
}

# Safer PATH handling
#
# Usage: PATH_add PATH
# Example: PATH_add bin
PATH_add() {
	export PATH="$(expand_path "$1"):$PATH"
}

# Safer path handling
#
# Usage: path_add VARNAME PATH
# Example: path_add LD_LIBRARY_PATH ./lib
path_add() {
	local old_paths="${!1}"
	local path="$(expand_path "$2")"

	if [ -z "$old_paths" ]; then
		old_paths="$path"
	else
		old_paths="$path:$old_paths"
	fi

	export $1="$old_paths"
}

# Sets some common variables for the given PREFIX_PATH.
#
# This is useful if you installed something in the PREFIX_PATH using
# $(./configure --prefix=PREFIX_PATH && make install) and want to use it in the
# project.
#
# Usage: load_prefix PREFIX_PATH
load_prefix() {
	local path="$(expand_path "$1")"
	path_add CPATH "$path/include"
	path_add LD_LIBRARY_PATH "$path/lib"
	path_add LIBRARY_PATH "$path/lib"
	path_add MANPATH "$path/man"
	path_add MANPATH "$path/share/man"
	path_add PATH "$path/bin"
	path_add PKG_CONFIG_PATH "$path/lib/pkgconfig"
}

# Pre-programmed project layout.
#
# It's possible to add your own layout or override the existing ones in your
# ~/.direnvrc file.
#
# Usage: layout TYPE
layout() {
	eval "layout_$1"
}

# This layout sets the $GEM_HOME into the project's directory to allow
# independent gem sets.
#
# It also allows bundler to provide bin-wrappers so you don't have to type
# bundle exec all the time.
#
# Usage: layout ruby
layout_ruby() {
	# TODO: ruby_version should be the ABI version
	local ruby_version="$(ruby -e"puts (defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby') + '-' + RUBY_VERSION")"

	export GEM_HOME="$PWD/.direnv/${ruby_version}"
	export BUNDLE_BIN="$PWD/.direnv/bin"

	PATH_add ".direnv/${ruby_version}/bin"
	PATH_add ".direnv/bin"
}

# This layout loads a per-project virtualenv.
#
# Usage: layout python
layout_python() {
	if ! [ -d .direnv/virtualenv ]; then
		virtualenv --no-site-packages --distribute .direnv/virtualenv
		virtualenv --relocatable .direnv/virtualenv
	fi
	source .direnv/virtualenv/bin/activate
}

# Adds the node_modules/.bin on the PATH to make the default tools accessible.
#
# Usage: layout node
layout_node() {
	PATH_add node_modules/.bin
}

# Put the project's root in the GOPATH.
#
# Usage: layout go
layout_go() {
	path_add GOPATH "$PWD"
}

# Intended to load external dependencies into the environment.
#
# Usage: use PROGRAM_NAME VERSION
# Example: use ruby 1.9.3
use() {
	local cmd="$1"
	echo "Using $@"
	shift
	use_$cmd "$@"
}

# Loads rbenv which makes the rbenv wrappers avaible on the $PATH.
#
# Usage: use rbenv
use_rbenv() {
	eval "$(rbenv init -)"
}

# Should work like the rvm command-line.
rvm() {
	unset rvm
	if [ -n "${rvm_scripts_path:-}" ]; then
		source "${rvm_scripts_path}/rvm"
	elif [ -n "${rvm_path:-}" ]; then
		source "${rvm_path}/scripts/rvm"
	else
		source "$HOME/.rvm/scripts/rvm"
	fi
	rvm "$@"
}

## Load the global ~/.direnvrc if present
if [ -f "$HOME/.direnvrc" ]; then
	source_env "~/.direnvrc" >&2
fi
`
<|endoftext|>


<commit_before>package coal

import (
	"context"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/256dpi/lungo"
	"github.com/256dpi/xo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readconcern"
	"go.mongodb.org/mongo-driver/mongo/writeconcern"
)

// MustConnect will call Connect and panic on errors.
func MustConnect(uri string, reporter func(error)) *Store {
	// connect store
	store, err := Connect(uri, reporter)
	if err != nil {
		panic(err)
	}

	return store
}

// Connect will connect to the specified database and return a new store. The
// read and write concern is set to majority by default.
//
// In summary, queries may return data that has bas been committed but may not
// be the most recent committed data. Also, long running cursors on indexed
// fields may return duplicate or missing documents due to the documents moving
// within the index. For operations involving multiple documents a transaction
// must be used to ensure atomicity, consistency and isolation.
func Connect(uri string, reporter func(error)) (*Store, error) {
	// parse url
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return nil, xo.W(err)
	}

	// get default db
	defaultDB := strings.Trim(parsedURL.Path, "/")

	// prepare options
	opts := options.Client().ApplyURI(uri)
	opts.SetReadConcern(readconcern.Linearizable())
	opts.SetWriteConcern(writeconcern.New(writeconcern.WMajority()))

	// create client
	client, err := lungo.Connect(nil, opts)
	if err != nil {
		return nil, xo.W(err)
	}

	// ping server
	err = client.Ping(nil, nil)
	if err != nil {
		return nil, xo.W(err)
	}

	return NewStore(client, defaultDB, nil, reporter), nil
}

// MustOpen will call Open and panic on errors.
func MustOpen(store lungo.Store, defaultDB string, reporter func(error)) *Store {
	// open store
	s, err := Open(store, defaultDB, reporter)
	if err != nil {
		panic(err)
	}

	return s
}

// Open will open the database using the provided lungo store. If the store is
// missing an in-memory store will be created.
func Open(store lungo.Store, defaultDB string, reporter func(error)) (*Store, error) {
	// set default memory store
	if store == nil {
		store = lungo.NewMemoryStore()
	}

	// create client
	client, engine, err := lungo.Open(nil, lungo.Options{
		Store:          store,
		ExpireInterval: time.Minute,
		ExpireErrors:   reporter,
	})
	if err != nil {
		return nil, xo.W(err)
	}

	return NewStore(client, defaultDB, engine, reporter), nil
}

// NewStore creates a store that uses the specified client, default database and
// engine. The engine may be nil if no lungo database is used.
func NewStore(client lungo.IClient, defaultDB string, engine *lungo.Engine, reporter func(error)) *Store {
	return &Store{
		client:   client,
		defDB:    defaultDB,
		engine:   engine,
		reporter: reporter,
	}
}

// A Store manages the usage of a database client.
type Store struct {
	client   lungo.IClient
	defDB    string
	engine   *lungo.Engine
	reporter func(error)
	colls    sync.Map
	managers sync.Map
}

// Client returns the client used by this store.
func (s *Store) Client() lungo.IClient {
	return s.client
}

// DB returns the database used by this store.
func (s *Store) DB() lungo.IDatabase {
	return s.client.Database(s.defDB)
}

// C will return the collection for the specified model. The collection is just
// a thin wrapper around the driver collection API to integrate tracing. Since
// it does not perform any checks, it is recommended to use the manager to
// perform safe CRUD operations.
func (s *Store) C(model Model) *Collection {
	// get meta
	meta := GetMeta(model)

	// check cache
	val, ok := s.colls.Load(meta)
	if ok {
		return val.(*Collection)
	}

	// create collection
	coll := &Collection{
		coll: s.DB().Collection(meta.Collection),
	}

	// cache collection
	s.colls.Store(meta, coll)

	return coll
}

// M will return the manager for the specified model. The manager will translate
// query and update documents as well as perform extensive checks before running
// operations to ensure they are as safe as possible.
func (s *Store) M(model Model) *Manager {
	// get meta
	meta := GetMeta(model)

	// check cache
	val, ok := s.managers.Load(meta)
	if ok {
		return val.(*Manager)
	}

	// create manager
	manager := &Manager{
		meta:  meta,
		coll:  s.C(model),
		trans: NewTranslator(model),
	}

	// cache collection
	s.managers.Store(meta, manager)

	return manager
}

// T will create a transaction around the specified callback. If the callback
// returns no error the transaction will be committed. If T itself does not
// return an error the transaction has been committed. The created context must
// be used with all operations that should be included in the transaction. A
// read only transaction will always abort the transaction when done.
//
// A transaction has the effect that the read concern is upgraded to "snapshot"
// which results in isolated and linearizable reads and writes of the data that
// has been committed prior to the start of the transaction:
//
// - Writes that conflict with other transactional writes will return an error.
//   Non-transactional writes will wait until the transaction has completed.
// - Reads are not guaranteed to be stable, another transaction may delete or
//   modify the document an also commit concurrently. Therefore, documents that
//   must "survive" the transaction and cause transactional writes to abort,
//   must be locked by changing a field to a new value.
func (s *Store) T(ctx context.Context, readOnly bool, fn func(tc context.Context) error) error {
	// ensure context
	if ctx == nil {
		ctx = context.Background()
	}

	// check if transaction already exists
	if HasTransaction(ctx) {
		return fn(ctx)
	}

	// trace
	ctx, span := xo.Trace(ctx, "coal/Store.T")
	defer span.End()

	// prepare options
	opts := options.Session().
		SetCausalConsistency(true).
		SetDefaultReadConcern(readconcern.Snapshot())

	// start transaction
	return xo.W(s.client.UseSessionWithOptions(ctx, opts, func(sc lungo.ISessionContext) error {
		// start transaction
		err := sc.StartTransaction()
		if err != nil {
			return xo.W(err)
		}

		// call function
		err = fn(context.WithValue(sc, hasTransaction, s))
		if err != nil {
			_ = sc.AbortTransaction(sc)
			return xo.W(err)
		}

		// abort ort commit transaction
		if readOnly {
			err = sc.AbortTransaction(sc)
		} else {
			err = sc.CommitTransaction(sc)
		}
		if err != nil {
			return xo.W(err)
		}

		return nil
	}))
}

// Close will close the store and its associated client.
func (s *Store) Close() error {
	// disconnect client
	err := s.client.Disconnect(nil)
	if err != nil {
		return xo.W(err)
	}

	// close engine
	if s.engine != nil {
		s.engine.Close()
	}

	return nil
}

type contextKey struct{}

var hasTransaction = contextKey{}

// GetTransaction will return whether the context carries a transaction and the
// store used to create the transaction.
func GetTransaction(ctx context.Context) (bool, *Store) {
	// check context
	if ctx == nil {
		return false, nil
	}

	// get value
	val, ok := ctx.Value(hasTransaction).(*Store)
	if !ok {
		return false, nil
	}

	return true, val
}

// HasTransaction will return whether the context carries a transaction.
func HasTransaction(ctx context.Context) bool {
	ok, _ := GetTransaction(ctx)
	return ok
}
<commit_msg>allow passing in client options<commit_after>package coal

import (
	"context"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/256dpi/lungo"
	"github.com/256dpi/xo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readconcern"
	"go.mongodb.org/mongo-driver/mongo/writeconcern"
)

// MustConnect will call Connect and panic on errors.
func MustConnect(uri string, reporter func(error), opts ...*options.ClientOptions) *Store {
	// connect store
	store, err := Connect(uri, reporter, opts...)
	if err != nil {
		panic(err)
	}

	return store
}

// Connect will connect to the specified database and return a new store. The
// read and write concern is set to majority by default.
//
// In summary, queries may return data that has bas been committed but may not
// be the most recent committed data. Also, long running cursors on indexed
// fields may return duplicate or missing documents due to the documents moving
// within the index. For operations involving multiple documents a transaction
// must be used to ensure atomicity, consistency and isolation.
func Connect(uri string, reporter func(error), opts ...*options.ClientOptions) (*Store, error) {
	// parse url
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return nil, xo.W(err)
	}

	// get default db
	defaultDB := strings.Trim(parsedURL.Path, "/")

	// prepare options
	opt := options.MergeClientOptions(opts...)
	opt.ApplyURI(uri)
	opt.SetReadConcern(readconcern.Linearizable())
	opt.SetWriteConcern(writeconcern.New(writeconcern.WMajority()))

	// create client
	client, err := lungo.Connect(nil, opt)
	if err != nil {
		return nil, xo.W(err)
	}

	// ping server
	err = client.Ping(nil, nil)
	if err != nil {
		return nil, xo.W(err)
	}

	return NewStore(client, defaultDB, nil, reporter), nil
}

// MustOpen will call Open and panic on errors.
func MustOpen(store lungo.Store, defaultDB string, reporter func(error)) *Store {
	// open store
	s, err := Open(store, defaultDB, reporter)
	if err != nil {
		panic(err)
	}

	return s
}

// Open will open the database using the provided lungo store. If the store is
// missing an in-memory store will be created.
func Open(store lungo.Store, defaultDB string, reporter func(error)) (*Store, error) {
	// set default memory store
	if store == nil {
		store = lungo.NewMemoryStore()
	}

	// create client
	client, engine, err := lungo.Open(nil, lungo.Options{
		Store:          store,
		ExpireInterval: time.Minute,
		ExpireErrors:   reporter,
	})
	if err != nil {
		return nil, xo.W(err)
	}

	return NewStore(client, defaultDB, engine, reporter), nil
}

// NewStore creates a store that uses the specified client, default database and
// engine. The engine may be nil if no lungo database is used.
func NewStore(client lungo.IClient, defaultDB string, engine *lungo.Engine, reporter func(error)) *Store {
	return &Store{
		client:   client,
		defDB:    defaultDB,
		engine:   engine,
		reporter: reporter,
	}
}

// A Store manages the usage of a database client.
type Store struct {
	client   lungo.IClient
	defDB    string
	engine   *lungo.Engine
	reporter func(error)
	colls    sync.Map
	managers sync.Map
}

// Client returns the client used by this store.
func (s *Store) Client() lungo.IClient {
	return s.client
}

// DB returns the database used by this store.
func (s *Store) DB() lungo.IDatabase {
	return s.client.Database(s.defDB)
}

// C will return the collection for the specified model. The collection is just
// a thin wrapper around the driver collection API to integrate tracing. Since
// it does not perform any checks, it is recommended to use the manager to
// perform safe CRUD operations.
func (s *Store) C(model Model) *Collection {
	// get meta
	meta := GetMeta(model)

	// check cache
	val, ok := s.colls.Load(meta)
	if ok {
		return val.(*Collection)
	}

	// create collection
	coll := &Collection{
		coll: s.DB().Collection(meta.Collection),
	}

	// cache collection
	s.colls.Store(meta, coll)

	return coll
}

// M will return the manager for the specified model. The manager will translate
// query and update documents as well as perform extensive checks before running
// operations to ensure they are as safe as possible.
func (s *Store) M(model Model) *Manager {
	// get meta
	meta := GetMeta(model)

	// check cache
	val, ok := s.managers.Load(meta)
	if ok {
		return val.(*Manager)
	}

	// create manager
	manager := &Manager{
		meta:  meta,
		coll:  s.C(model),
		trans: NewTranslator(model),
	}

	// cache collection
	s.managers.Store(meta, manager)

	return manager
}

// T will create a transaction around the specified callback. If the callback
// returns no error the transaction will be committed. If T itself does not
// return an error the transaction has been committed. The created context must
// be used with all operations that should be included in the transaction. A
// read only transaction will always abort the transaction when done.
//
// A transaction has the effect that the read concern is upgraded to "snapshot"
// which results in isolated and linearizable reads and writes of the data that
// has been committed prior to the start of the transaction:
//
// - Writes that conflict with other transactional writes will return an error.
//   Non-transactional writes will wait until the transaction has completed.
// - Reads are not guaranteed to be stable, another transaction may delete or
//   modify the document an also commit concurrently. Therefore, documents that
//   must "survive" the transaction and cause transactional writes to abort,
//   must be locked by changing a field to a new value.
func (s *Store) T(ctx context.Context, readOnly bool, fn func(tc context.Context) error) error {
	// ensure context
	if ctx == nil {
		ctx = context.Background()
	}

	// check if transaction already exists
	if HasTransaction(ctx) {
		return fn(ctx)
	}

	// trace
	ctx, span := xo.Trace(ctx, "coal/Store.T")
	defer span.End()

	// prepare options
	opts := options.Session().
		SetCausalConsistency(true).
		SetDefaultReadConcern(readconcern.Snapshot())

	// start transaction
	return xo.W(s.client.UseSessionWithOptions(ctx, opts, func(sc lungo.ISessionContext) error {
		// start transaction
		err := sc.StartTransaction()
		if err != nil {
			return xo.W(err)
		}

		// call function
		err = fn(context.WithValue(sc, hasTransaction, s))
		if err != nil {
			_ = sc.AbortTransaction(sc)
			return xo.W(err)
		}

		// abort ort commit transaction
		if readOnly {
			err = sc.AbortTransaction(sc)
		} else {
			err = sc.CommitTransaction(sc)
		}
		if err != nil {
			return xo.W(err)
		}

		return nil
	}))
}

// Close will close the store and its associated client.
func (s *Store) Close() error {
	// disconnect client
	err := s.client.Disconnect(nil)
	if err != nil {
		return xo.W(err)
	}

	// close engine
	if s.engine != nil {
		s.engine.Close()
	}

	return nil
}

type contextKey struct{}

var hasTransaction = contextKey{}

// GetTransaction will return whether the context carries a transaction and the
// store used to create the transaction.
func GetTransaction(ctx context.Context) (bool, *Store) {
	// check context
	if ctx == nil {
		return false, nil
	}

	// get value
	val, ok := ctx.Value(hasTransaction).(*Store)
	if !ok {
		return false, nil
	}

	return true, val
}

// HasTransaction will return whether the context carries a transaction.
func HasTransaction(ctx context.Context) bool {
	ok, _ := GetTransaction(ctx)
	return ok
}
<|endoftext|>


<commit_before>package worker

import (
	"errors"
	"fmt"
	"math/rand"
	"time"

	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . WorkerProvider

type WorkerProvider interface {
	Workers() ([]Worker, error)
	GetWorker(string) (Worker, bool, error)
	FindContainerInfoForIdentifier(Identifier) (db.ContainerInfo, bool, error)
	GetContainerInfo(string) (db.ContainerInfo, bool, error)
	ReapContainer(string) error
}

var (
	ErrNoWorkers     = errors.New("no workers")
	ErrMissingWorker = errors.New("worker for container is missing")
)

type NoCompatibleWorkersError struct {
	Spec    WorkerSpec
	Workers []Worker
}

func (err NoCompatibleWorkersError) Error() string {
	availableWorkers := ""
	for _, worker := range err.Workers {
		availableWorkers += "\n  - " + worker.Description()
	}

	return fmt.Sprintf(
		"no workers satisfying: %s\n\navailable workers: %s",
		err.Spec.Description(),
		availableWorkers,
	)
}

type pool struct {
	provider WorkerProvider

	rand *rand.Rand
}

func NewPool(provider WorkerProvider) Client {
	return &pool{
		provider: provider,
		rand:     rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

func (pool *pool) Satisfying(spec WorkerSpec) (Worker, error) {
	workers, err := pool.provider.Workers()
	if err != nil {
		return nil, err
	}

	if len(workers) == 0 {
		return nil, ErrNoWorkers
	}

	compatibleWorkers := []Worker{}
	for _, worker := range workers {
		satisfyingWorker, err := worker.Satisfying(spec)
		if err == nil {
			compatibleWorkers = append(compatibleWorkers, satisfyingWorker)
		}
	}

	if len(compatibleWorkers) == 0 {
		return nil, NoCompatibleWorkersError{
			Spec:    spec,
			Workers: workers,
		}
	}

	randomWorker := compatibleWorkers[pool.rand.Intn(len(compatibleWorkers))]

	return randomWorker, nil
}

func (pool *pool) CreateContainer(logger lager.Logger, id Identifier, spec ContainerSpec) (Container, error) {
	worker, err := pool.Satisfying(spec.WorkerSpec())
	if err != nil {
		return nil, err
	}

	container, err := worker.CreateContainer(logger, id, spec)
	if err != nil {
		return nil, err
	}

	return container, nil
}

func (pool *pool) FindContainerForIdentifier(logger lager.Logger, id Identifier) (Container, bool, error) {
	containerInfo, found, err := pool.provider.FindContainerInfoForIdentifier(id)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, found, nil
	}

	worker, found, err := pool.provider.GetWorker(containerInfo.WorkerName)
	if err != nil {
		return nil, found, err
	}

	if !found {
		logger.Info("found-container-for-missing-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		return nil, false, ErrMissingWorker
	}

	container, found, err := worker.LookupContainer(logger, containerInfo.Handle)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("found-container-in-db-but-not-on-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		err := pool.provider.ReapContainer(containerInfo.Handle)
		if err != nil {
			return nil, false, err
		}

		return nil, false, err
	}

	return container, true, nil
}

func (pool *pool) LookupContainer(logger lager.Logger, handle string) (Container, bool, error) {
	logger.Info("looking-up-container", lager.Data{"handle": handle})

	containerInfo, found, err := pool.provider.GetContainerInfo(handle)
	if err != nil {
		return nil, false, err
	}
	if !found {
		return nil, false, nil
	}

	worker, found, err := pool.provider.GetWorker(containerInfo.WorkerName)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("found-container-for-missing-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		return nil, false, ErrMissingWorker
	}

	container, found, err := worker.LookupContainer(logger, handle)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("found-container-in-db-but-not-on-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		err := pool.provider.ReapContainer(handle)
		if err != nil {
			return nil, false, err
		}

		return nil, false, nil
	}

	return container, true, nil
}

func (pool *pool) Name() string {
	return "pool"
}

type byActiveContainers []Worker

func (cs byActiveContainers) Len() int { return len(cs) }

func (cs byActiveContainers) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }

func (cs byActiveContainers) Less(i, j int) bool {
	return cs[i].ActiveContainers() < cs[j].ActiveContainers()
}
<commit_msg>clearer log lines<commit_after>package worker

import (
	"errors"
	"fmt"
	"math/rand"
	"time"

	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . WorkerProvider

type WorkerProvider interface {
	Workers() ([]Worker, error)
	GetWorker(string) (Worker, bool, error)
	FindContainerInfoForIdentifier(Identifier) (db.ContainerInfo, bool, error)
	GetContainerInfo(string) (db.ContainerInfo, bool, error)
	ReapContainer(string) error
}

var (
	ErrNoWorkers     = errors.New("no workers")
	ErrMissingWorker = errors.New("worker for container is missing")
)

type NoCompatibleWorkersError struct {
	Spec    WorkerSpec
	Workers []Worker
}

func (err NoCompatibleWorkersError) Error() string {
	availableWorkers := ""
	for _, worker := range err.Workers {
		availableWorkers += "\n  - " + worker.Description()
	}

	return fmt.Sprintf(
		"no workers satisfying: %s\n\navailable workers: %s",
		err.Spec.Description(),
		availableWorkers,
	)
}

type pool struct {
	provider WorkerProvider

	rand *rand.Rand
}

func NewPool(provider WorkerProvider) Client {
	return &pool{
		provider: provider,
		rand:     rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

func (pool *pool) Satisfying(spec WorkerSpec) (Worker, error) {
	workers, err := pool.provider.Workers()
	if err != nil {
		return nil, err
	}

	if len(workers) == 0 {
		return nil, ErrNoWorkers
	}

	compatibleWorkers := []Worker{}
	for _, worker := range workers {
		satisfyingWorker, err := worker.Satisfying(spec)
		if err == nil {
			compatibleWorkers = append(compatibleWorkers, satisfyingWorker)
		}
	}

	if len(compatibleWorkers) == 0 {
		return nil, NoCompatibleWorkersError{
			Spec:    spec,
			Workers: workers,
		}
	}

	randomWorker := compatibleWorkers[pool.rand.Intn(len(compatibleWorkers))]

	return randomWorker, nil
}

func (pool *pool) CreateContainer(logger lager.Logger, id Identifier, spec ContainerSpec) (Container, error) {
	worker, err := pool.Satisfying(spec.WorkerSpec())
	if err != nil {
		return nil, err
	}

	container, err := worker.CreateContainer(logger, id, spec)
	if err != nil {
		return nil, err
	}

	return container, nil
}

func (pool *pool) FindContainerForIdentifier(logger lager.Logger, id Identifier) (Container, bool, error) {
	containerInfo, found, err := pool.provider.FindContainerInfoForIdentifier(id)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, found, nil
	}

	worker, found, err := pool.provider.GetWorker(containerInfo.WorkerName)
	if err != nil {
		return nil, found, err
	}

	if !found {
		logger.Info("found-container-for-missing-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		return nil, false, ErrMissingWorker
	}

	container, found, err := worker.LookupContainer(logger, containerInfo.Handle)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("reaping-container-not-found-on-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		err := pool.provider.ReapContainer(containerInfo.Handle)
		if err != nil {
			return nil, false, err
		}

		return nil, false, err
	}

	return container, true, nil
}

func (pool *pool) LookupContainer(logger lager.Logger, handle string) (Container, bool, error) {
	logger.Info("looking-up-container", lager.Data{"handle": handle})

	containerInfo, found, err := pool.provider.GetContainerInfo(handle)
	if err != nil {
		return nil, false, err
	}
	if !found {
		return nil, false, nil
	}

	worker, found, err := pool.provider.GetWorker(containerInfo.WorkerName)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("found-container-for-missing-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		return nil, false, ErrMissingWorker
	}

	container, found, err := worker.LookupContainer(logger, handle)
	if err != nil {
		return nil, false, err
	}

	if !found {
		logger.Info("reaping-container-not-found-on-worker", lager.Data{
			"container-handle": containerInfo.Handle,
			"worker-name":      containerInfo.WorkerName,
		})

		err := pool.provider.ReapContainer(handle)
		if err != nil {
			return nil, false, err
		}

		return nil, false, nil
	}

	return container, true, nil
}

func (pool *pool) Name() string {
	return "pool"
}

type byActiveContainers []Worker

func (cs byActiveContainers) Len() int { return len(cs) }

func (cs byActiveContainers) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }

func (cs byActiveContainers) Less(i, j int) bool {
	return cs[i].ActiveContainers() < cs[j].ActiveContainers()
}
<|endoftext|>