<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/azure"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/certs"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/config"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"
	proxy "github.com/cloudfoundry/socks5-proxy"

	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	azurecloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/azure"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	vspherecloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/vsphere"
	awsterraform "github.com/cloudfoundry/bosh-bootloader/terraform/aws"
	azureterraform "github.com/cloudfoundry/bosh-bootloader/terraform/azure"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
	vsphereterraform "github.com/cloudfoundry/bosh-bootloader/terraform/vsphere"
)

var Version = "dev"

func main() {
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)
	stateBootstrap := storage.NewStateBootstrap(stderrLogger, Version)

	globals, _, err := config.ParseArgs(os.Args)
	log.SetFlags(0)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	stateStore := storage.NewStore(globals.StateDir)
	stateMigrator := storage.NewMigrator(stateStore)
	newConfig := config.NewConfig(stateBootstrap, stateMigrator, stderrLogger)

	appConfig, err := newConfig.Bootstrap(os.Args)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	needsIAASCreds := config.NeedsIAASCreds(appConfig.Command) && !appConfig.ShowCommandHelp
	if needsIAASCreds {
		err = config.ValidateIAAS(appConfig.State)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
	}

	// Utilities
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	stateValidator := application.NewStateValidator(appConfig.Global.StateDir)
	certificateValidator := certs.NewValidator()
	lbArgsHandler := commands.NewLBArgsHandler(certificateValidator)

	// Terraform
	terraformOutputBuffer := bytes.NewBuffer([]byte{})
	terraformCmd := terraform.NewCmd(os.Stderr, terraformOutputBuffer)
	terraformExecutor := terraform.NewExecutor(terraformCmd, stateStore, appConfig.Global.Debug)

	var (
		networkClient            helpers.NetworkClient
		networkDeletionValidator commands.NetworkDeletionValidator

		gcpClient                 gcp.Client
		availabilityZoneRetriever aws.AvailabilityZoneRetriever
	)
	if appConfig.State.IAAS == "aws" && needsIAASCreds {
		awsClient := aws.NewClient(appConfig.State.AWS, logger)

		availabilityZoneRetriever = awsClient
		networkDeletionValidator = awsClient
		networkClient = awsClient
	} else if appConfig.State.IAAS == "gcp" && needsIAASCreds {
		gcpClient, err = gcp.NewClient(appConfig.State.GCP, "")
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}

		networkDeletionValidator = gcpClient
		networkClient = gcpClient

		gcpZonerHack := config.NewGCPZonerHack(gcpClient)
		stateWithZones, err := gcpZonerHack.SetZones(appConfig.State)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
		appConfig.State = stateWithZones
	} else if appConfig.State.IAAS == "azure" && needsIAASCreds {
		azureClient, err := azure.NewClient(appConfig.State.Azure)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}

		networkDeletionValidator = azureClient
		networkClient = azureClient
	}

	var (
		inputGenerator    terraform.InputGenerator
		templateGenerator terraform.TemplateGenerator
	)
	switch appConfig.State.IAAS {
	case "aws":
		templateGenerator = awsterraform.NewTemplateGenerator()
		inputGenerator = awsterraform.NewInputGenerator(availabilityZoneRetriever)
	case "azure":
		templateGenerator = azureterraform.NewTemplateGenerator()
		inputGenerator = azureterraform.NewInputGenerator()
	case "gcp":
		templateGenerator = gcpterraform.NewTemplateGenerator()
		inputGenerator = gcpterraform.NewInputGenerator()
	case "vsphere":
		templateGenerator = vsphereterraform.NewTemplateGenerator()
		inputGenerator = vsphereterraform.NewInputGenerator()
	}

	terraformManager := terraform.NewManager(terraformExecutor, templateGenerator, inputGenerator, terraformOutputBuffer, logger)

	// BOSH
	hostKeyGetter := proxy.NewHostKeyGetter()
	socks5Proxy := proxy.NewSocks5Proxy(hostKeyGetter)
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.ReadFile, json.Unmarshal, json.Marshal, ioutil.WriteFile)
	sshKeyGetter := bosh.NewSSHKeyGetter(stateStore)
	boshManager := bosh.NewManager(boshExecutor, logger, socks5Proxy, stateStore, sshKeyGetter)
	boshClientProvider := bosh.NewClientProvider(socks5Proxy, sshKeyGetter)
	environmentValidator := application.NewEnvironmentValidator(boshClientProvider)

	var cloudConfigOpsGenerator cloudconfig.OpsGenerator
	switch appConfig.State.IAAS {
	case "aws":
		cloudConfigOpsGenerator = awscloudconfig.NewOpsGenerator(terraformManager, availabilityZoneRetriever)
	case "azure":
		cloudConfigOpsGenerator = azurecloudconfig.NewOpsGenerator(terraformManager)
	case "gcp":
		cloudConfigOpsGenerator = gcpcloudconfig.NewOpsGenerator(terraformManager)
	case "vsphere":
		cloudConfigOpsGenerator = vspherecloudconfig.NewOpsGenerator(terraformManager)
	}
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, stateStore, cloudConfigOpsGenerator, boshClientProvider, terraformManager, sshKeyGetter)

	// Subcommands
	var lbsCmd commands.LBsCmd

	switch appConfig.State.IAAS {
	case "aws":
		lbsCmd = commands.NewAWSLBs(terraformManager, logger)
	case "gcp":
		lbsCmd = commands.NewGCPLBs(terraformManager, logger)
	}

	// Commands
	var envIDManager helpers.EnvIDManager
	if appConfig.State.IAAS != "" {
		envIDManager = helpers.NewEnvIDManager(envIDGenerator, networkClient)
	}
	plan := commands.NewPlan(boshManager, cloudConfigManager, stateStore, envIDManager, terraformManager, lbArgsHandler, stderrLogger)
	up := commands.NewUp(plan, boshManager, cloudConfigManager, stateStore, terraformManager)
	usage := commands.NewUsage(logger)

	commandSet := application.CommandSet{}
	commandSet["help"] = usage
	commandSet["version"] = commands.NewVersion(Version, logger)
	commandSet["up"] = up
	commandSet["plan"] = plan
	sshKeyDeleter := bosh.NewSSHKeyDeleter(stateStore)
	commandSet["rotate"] = commands.NewRotate(stateValidator, sshKeyDeleter, up)
	commandSet["destroy"] = commands.NewDestroy(plan, logger, os.Stdin, boshManager, stateStore, stateValidator, terraformManager, networkDeletionValidator)
	commandSet["down"] = commandSet["destroy"]
	commandSet["create-lbs"] = commands.NewCreateLBs(logger, stateValidator, boshManager, lbArgsHandler, cloudConfigManager, terraformManager, stateStore, environmentValidator)
	commandSet["update-lbs"] = commandSet["create-lbs"]
	commandSet["delete-lbs"] = commands.NewDeleteLBs(logger, stateValidator, boshManager, cloudConfigManager, stateStore, environmentValidator, terraformManager)
	commandSet["lbs"] = commands.NewLBs(lbsCmd, stateValidator)
	commandSet["jumpbox-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.JumpboxAddressPropertyName)
	commandSet["director-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorAddressPropertyName)
	commandSet["director-username"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorUsernamePropertyName)
	commandSet["director-password"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorPasswordPropertyName)
	commandSet["director-ca-cert"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorCACertPropertyName)
	commandSet["ssh-key"] = commands.NewSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["director-ssh-key"] = commands.NewDirectorSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["env-id"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.EnvIDPropertyName)
	commandSet["latest-error"] = commands.NewLatestError(logger, stateValidator)
	commandSet["print-env"] = commands.NewPrintEnv(logger, stateValidator, sshKeyGetter, terraformManager)
	commandSet["cloud-config"] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)
	commandSet["jumpbox-deployment-vars"] = commands.NewJumpboxDeploymentVars(logger, boshManager, stateValidator, terraformManager)
	commandSet["bosh-deployment-vars"] = commands.NewBOSHDeploymentVars(logger, boshManager, stateValidator, terraformManager)

	app := application.New(commandSet, appConfig, usage)

	err = app.Run()
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}
}
<commit_msg>Test which versions the pipeline uses.<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/azure"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/certs"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/config"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"
	proxy "github.com/cloudfoundry/socks5-proxy"

	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	azurecloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/azure"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	vspherecloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/vsphere"
	awsterraform "github.com/cloudfoundry/bosh-bootloader/terraform/aws"
	azureterraform "github.com/cloudfoundry/bosh-bootloader/terraform/azure"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
	vsphereterraform "github.com/cloudfoundry/bosh-bootloader/terraform/vsphere"
)

var Version = "dev"

func main() {
	fmt.Println("pipeline version test")
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)
	stateBootstrap := storage.NewStateBootstrap(stderrLogger, Version)

	globals, _, err := config.ParseArgs(os.Args)
	log.SetFlags(0)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	stateStore := storage.NewStore(globals.StateDir)
	stateMigrator := storage.NewMigrator(stateStore)
	newConfig := config.NewConfig(stateBootstrap, stateMigrator, stderrLogger)

	appConfig, err := newConfig.Bootstrap(os.Args)
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}

	needsIAASCreds := config.NeedsIAASCreds(appConfig.Command) && !appConfig.ShowCommandHelp
	if needsIAASCreds {
		err = config.ValidateIAAS(appConfig.State)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
	}

	// Utilities
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	stateValidator := application.NewStateValidator(appConfig.Global.StateDir)
	certificateValidator := certs.NewValidator()
	lbArgsHandler := commands.NewLBArgsHandler(certificateValidator)

	// Terraform
	terraformOutputBuffer := bytes.NewBuffer([]byte{})
	terraformCmd := terraform.NewCmd(os.Stderr, terraformOutputBuffer)
	terraformExecutor := terraform.NewExecutor(terraformCmd, stateStore, appConfig.Global.Debug)

	var (
		networkClient            helpers.NetworkClient
		networkDeletionValidator commands.NetworkDeletionValidator

		gcpClient                 gcp.Client
		availabilityZoneRetriever aws.AvailabilityZoneRetriever
	)
	if appConfig.State.IAAS == "aws" && needsIAASCreds {
		awsClient := aws.NewClient(appConfig.State.AWS, logger)

		availabilityZoneRetriever = awsClient
		networkDeletionValidator = awsClient
		networkClient = awsClient
	} else if appConfig.State.IAAS == "gcp" && needsIAASCreds {
		gcpClient, err = gcp.NewClient(appConfig.State.GCP, "")
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}

		networkDeletionValidator = gcpClient
		networkClient = gcpClient

		gcpZonerHack := config.NewGCPZonerHack(gcpClient)
		stateWithZones, err := gcpZonerHack.SetZones(appConfig.State)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}
		appConfig.State = stateWithZones
	} else if appConfig.State.IAAS == "azure" && needsIAASCreds {
		azureClient, err := azure.NewClient(appConfig.State.Azure)
		if err != nil {
			log.Fatalf("\n\n%s\n", err)
		}

		networkDeletionValidator = azureClient
		networkClient = azureClient
	}

	var (
		inputGenerator    terraform.InputGenerator
		templateGenerator terraform.TemplateGenerator
	)
	switch appConfig.State.IAAS {
	case "aws":
		templateGenerator = awsterraform.NewTemplateGenerator()
		inputGenerator = awsterraform.NewInputGenerator(availabilityZoneRetriever)
	case "azure":
		templateGenerator = azureterraform.NewTemplateGenerator()
		inputGenerator = azureterraform.NewInputGenerator()
	case "gcp":
		templateGenerator = gcpterraform.NewTemplateGenerator()
		inputGenerator = gcpterraform.NewInputGenerator()
	case "vsphere":
		templateGenerator = vsphereterraform.NewTemplateGenerator()
		inputGenerator = vsphereterraform.NewInputGenerator()
	}

	terraformManager := terraform.NewManager(terraformExecutor, templateGenerator, inputGenerator, terraformOutputBuffer, logger)

	// BOSH
	hostKeyGetter := proxy.NewHostKeyGetter()
	socks5Proxy := proxy.NewSocks5Proxy(hostKeyGetter)
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.ReadFile, json.Unmarshal, json.Marshal, ioutil.WriteFile)
	sshKeyGetter := bosh.NewSSHKeyGetter(stateStore)
	boshManager := bosh.NewManager(boshExecutor, logger, socks5Proxy, stateStore, sshKeyGetter)
	boshClientProvider := bosh.NewClientProvider(socks5Proxy, sshKeyGetter)
	environmentValidator := application.NewEnvironmentValidator(boshClientProvider)

	var cloudConfigOpsGenerator cloudconfig.OpsGenerator
	switch appConfig.State.IAAS {
	case "aws":
		cloudConfigOpsGenerator = awscloudconfig.NewOpsGenerator(terraformManager, availabilityZoneRetriever)
	case "azure":
		cloudConfigOpsGenerator = azurecloudconfig.NewOpsGenerator(terraformManager)
	case "gcp":
		cloudConfigOpsGenerator = gcpcloudconfig.NewOpsGenerator(terraformManager)
	case "vsphere":
		cloudConfigOpsGenerator = vspherecloudconfig.NewOpsGenerator(terraformManager)
	}
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, stateStore, cloudConfigOpsGenerator, boshClientProvider, terraformManager, sshKeyGetter)

	// Subcommands
	var lbsCmd commands.LBsCmd

	switch appConfig.State.IAAS {
	case "aws":
		lbsCmd = commands.NewAWSLBs(terraformManager, logger)
	case "gcp":
		lbsCmd = commands.NewGCPLBs(terraformManager, logger)
	}

	// Commands
	var envIDManager helpers.EnvIDManager
	if appConfig.State.IAAS != "" {
		envIDManager = helpers.NewEnvIDManager(envIDGenerator, networkClient)
	}
	plan := commands.NewPlan(boshManager, cloudConfigManager, stateStore, envIDManager, terraformManager, lbArgsHandler, stderrLogger)
	up := commands.NewUp(plan, boshManager, cloudConfigManager, stateStore, terraformManager)
	usage := commands.NewUsage(logger)

	commandSet := application.CommandSet{}
	commandSet["help"] = usage
	commandSet["version"] = commands.NewVersion(Version, logger)
	commandSet["up"] = up
	commandSet["plan"] = plan
	sshKeyDeleter := bosh.NewSSHKeyDeleter(stateStore)
	commandSet["rotate"] = commands.NewRotate(stateValidator, sshKeyDeleter, up)
	commandSet["destroy"] = commands.NewDestroy(plan, logger, os.Stdin, boshManager, stateStore, stateValidator, terraformManager, networkDeletionValidator)
	commandSet["down"] = commandSet["destroy"]
	commandSet["create-lbs"] = commands.NewCreateLBs(logger, stateValidator, boshManager, lbArgsHandler, cloudConfigManager, terraformManager, stateStore, environmentValidator)
	commandSet["update-lbs"] = commandSet["create-lbs"]
	commandSet["delete-lbs"] = commands.NewDeleteLBs(logger, stateValidator, boshManager, cloudConfigManager, stateStore, environmentValidator, terraformManager)
	commandSet["lbs"] = commands.NewLBs(lbsCmd, stateValidator)
	commandSet["jumpbox-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.JumpboxAddressPropertyName)
	commandSet["director-address"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorAddressPropertyName)
	commandSet["director-username"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorUsernamePropertyName)
	commandSet["director-password"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorPasswordPropertyName)
	commandSet["director-ca-cert"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.DirectorCACertPropertyName)
	commandSet["ssh-key"] = commands.NewSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["director-ssh-key"] = commands.NewDirectorSSHKey(logger, stateValidator, sshKeyGetter)
	commandSet["env-id"] = commands.NewStateQuery(logger, stateValidator, terraformManager, commands.EnvIDPropertyName)
	commandSet["latest-error"] = commands.NewLatestError(logger, stateValidator)
	commandSet["print-env"] = commands.NewPrintEnv(logger, stateValidator, sshKeyGetter, terraformManager)
	commandSet["cloud-config"] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)
	commandSet["jumpbox-deployment-vars"] = commands.NewJumpboxDeploymentVars(logger, boshManager, stateValidator, terraformManager)
	commandSet["bosh-deployment-vars"] = commands.NewBOSHDeploymentVars(logger, boshManager, stateValidator, terraformManager)

	app := application.New(commandSet, appConfig, usage)

	err = app.Run()
	if err != nil {
		log.Fatalf("\n\n%s\n", err)
	}
}
<|endoftext|>


<commit_before>package helpers

import (
    "errors"
    helperModels "github.com/SpectraLogic/ds3_go_sdk/helpers/models"
)

// A queue that manages descriptions of blobs
// Used to track blobs that are waiting to be transferred
type BlobDescriptionQueue interface {
    Push(description *helperModels.BlobDescription)
    Pop() (*helperModels.BlobDescription, error)
    Size() int
}

// Implements BlobDescriptionQueue using a slice
// NOT thread safe
type blobDescriptionQueueImpl struct {
    queue []*helperModels.BlobDescription
}

func NewBlobDescriptionQueue() BlobDescriptionQueue {
    queue :=make([]*helperModels.BlobDescription, 0)
    return &blobDescriptionQueueImpl{queue:queue}
}

func (queue *blobDescriptionQueueImpl) Push(description *helperModels.BlobDescription) {
    queue.queue = append(queue.queue, description)
}

func (queue *blobDescriptionQueueImpl) Pop() (*helperModels.BlobDescription, error) {
    if queue.Size() == 0 {
        return nil, errors.New("Cannot perform Pop() from blobDescriptionQueueImpl as queue is empty")
    }
    descriptor := queue.queue[0]
    queue.queue = queue.queue[1:]
    return descriptor, nil
}

func (queue *blobDescriptionQueueImpl) Size() int {
    return len(queue.queue)
}
<commit_msg>OTHER: fixing race condition queueing things for processing. This only happens in streaming strategy combined with very small blob size on the BP bucket. There existed the possibility that a blob would be added to the queue twice. This explicitly prevents the double queuing of blobs. (#101) (#104)<commit_after>package helpers

import (
    "errors"
    helperModels "github.com/SpectraLogic/ds3_go_sdk/helpers/models"
    "reflect"
)

// A queue that manages descriptions of blobs
// Used to track blobs that are waiting to be transferred
type BlobDescriptionQueue interface {
    Push(description *helperModels.BlobDescription)
    Pop() (*helperModels.BlobDescription, error)
    Size() int
}

// Implements BlobDescriptionQueue using a slice
// NOT thread safe
type blobDescriptionQueueImpl struct {
    queue []*helperModels.BlobDescription
}

func NewBlobDescriptionQueue() BlobDescriptionQueue {
    queue :=make([]*helperModels.BlobDescription, 0)
    return &blobDescriptionQueueImpl{queue:queue}
}

func (queue *blobDescriptionQueueImpl) Push(description *helperModels.BlobDescription) {
    // verify that this blob isn't already in the queue before adding it
    for _, existingBlob := range queue.queue {
        if reflect.DeepEqual(*existingBlob, *description) {
            return
        }
    }
    queue.queue = append(queue.queue, description)
}

func (queue *blobDescriptionQueueImpl) Pop() (*helperModels.BlobDescription, error) {
    if queue.Size() == 0 {
        return nil, errors.New("Cannot perform Pop() from blobDescriptionQueueImpl as queue is empty")
    }
    descriptor := queue.queue[0]
    queue.queue = queue.queue[1:]
    return descriptor, nil
}

func (queue *blobDescriptionQueueImpl) Size() int {
    return len(queue.queue)
}
<|endoftext|>


<commit_before>package sparta

import (
	"fmt"
	"strings"

	_ "github.com/aws/aws-lambda-go/lambda"        // Force dep to resolve
	_ "github.com/aws/aws-lambda-go/lambdacontext" // Force dep to resolve
)

////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////

const (
	// ProperName is the DRY name definition
	ProperName = "Sparta"
)
const (
	// SpartaVersion defines the current Sparta release
	SpartaVersion = "2.0.0"
	// GoLambdaVersion is the Go version runtime used for the lambda function
	GoLambdaVersion = "go1.x"
	// LambdaBinaryTag is the build tag name used when building the binary
	LambdaBinaryTag = "lambdabinary"
)

var (
	// SpartaBinaryName is binary name that exposes the Go lambda function
	SpartaBinaryName = fmt.Sprintf("%s.lambda.amd64", ProperName)
)

const (
	// Custom Resource typename used to create new cloudFormationUserDefinedFunctionCustomResource
	cloudFormationLambda = "Custom::SpartaLambdaCustomResource"
	// divider length is the length of a divider in the text
	// based CLI output
	dividerLength = 48
)
const (
	// envVarLogLevel is the provision time debug value
	// carried into the execution environment
	envVarLogLevel = "SPARTA_LOG_LEVEL"
	// spartaEnvVarFunctionName is the name of this function in the
	// map. It's the function that will be registered to run
	// envVarFunctionName = "SPARTA_FUNC_NAME"
	// envVarDiscoveryInformation is the name of the discovery information
	// published into the environment
	envVarDiscoveryInformation = "SPARTA_DISCOVERY_INFO"
)

var (
	// internal logging header
	headerDivider = strings.Repeat("═", dividerLength)
)

// AWS Principal ARNs from http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
// See also
// http://docs.aws.amazon.com/general/latest/gr/rande.html
// for region specific principal names
const (
	// @enum AWSPrincipal
	APIGatewayPrincipal = "apigateway.amazonaws.com"
	// @enum AWSPrincipal
	CloudWatchEventsPrincipal = "events.amazonaws.com"
	// @enum AWSPrincipal
	SESPrincipal = "ses.amazonaws.com"
	// @enum AWSPrincipal
	SNSPrincipal = "sns.amazonaws.com"
	// @enum AWSPrincipal
	EC2Principal = "ec2.amazonaws.com"
	// @enum AWSPrincipal
	LambdaPrincipal = "lambda.amazonaws.com"
)

type contextKey int

const (
	// ContextKeyLogger is the request-independent *logrus.Logger
	// instance common to all requests
	ContextKeyLogger contextKey = iota
	// ContextKeyRequestLogger is the *logrus.Entry instance
	// that is annotated with request-identifying
	// information extracted from the AWS context object
	ContextKeyRequestLogger
	// ContextKeyLambdaContext is the *sparta.LambdaContext
	// pointer in the request
	// DEPRECATED
	ContextKeyLambdaContext
	// ContextKeyLambdaError is the possible error that was returned
	// from the lambda function
	ContextKeyLambdaError
	// ContextKeyLambdaResponse is the possible response that
	// was returned from the lambda function
	ContextKeyLambdaResponse
)
<commit_msg>Add ContextKeyAWSSession constant<commit_after>package sparta

import (
	"fmt"
	"strings"

	_ "github.com/aws/aws-lambda-go/lambda"        // Force dep to resolve
	_ "github.com/aws/aws-lambda-go/lambdacontext" // Force dep to resolve
)

////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////

const (
	// ProperName is the DRY name definition
	ProperName = "Sparta"
)
const (
	// SpartaVersion defines the current Sparta release
	SpartaVersion = "2.0.0"
	// GoLambdaVersion is the Go version runtime used for the lambda function
	GoLambdaVersion = "go1.x"
	// LambdaBinaryTag is the build tag name used when building the binary
	LambdaBinaryTag = "lambdabinary"
)

var (
	// SpartaBinaryName is binary name that exposes the Go lambda function
	SpartaBinaryName = fmt.Sprintf("%s.lambda.amd64", ProperName)
)

const (
	// Custom Resource typename used to create new cloudFormationUserDefinedFunctionCustomResource
	cloudFormationLambda = "Custom::SpartaLambdaCustomResource"
	// divider length is the length of a divider in the text
	// based CLI output
	dividerLength = 48
)
const (
	// envVarLogLevel is the provision time debug value
	// carried into the execution environment
	envVarLogLevel = "SPARTA_LOG_LEVEL"
	// spartaEnvVarFunctionName is the name of this function in the
	// map. It's the function that will be registered to run
	// envVarFunctionName = "SPARTA_FUNC_NAME"
	// envVarDiscoveryInformation is the name of the discovery information
	// published into the environment
	envVarDiscoveryInformation = "SPARTA_DISCOVERY_INFO"
)

var (
	// internal logging header
	headerDivider = strings.Repeat("═", dividerLength)
)

// AWS Principal ARNs from http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
// See also
// http://docs.aws.amazon.com/general/latest/gr/rande.html
// for region specific principal names
const (
	// @enum AWSPrincipal
	APIGatewayPrincipal = "apigateway.amazonaws.com"
	// @enum AWSPrincipal
	CloudWatchEventsPrincipal = "events.amazonaws.com"
	// @enum AWSPrincipal
	SESPrincipal = "ses.amazonaws.com"
	// @enum AWSPrincipal
	SNSPrincipal = "sns.amazonaws.com"
	// @enum AWSPrincipal
	EC2Principal = "ec2.amazonaws.com"
	// @enum AWSPrincipal
	LambdaPrincipal = "lambda.amazonaws.com"
)

type contextKey int

const (
	// ContextKeyLogger is the request-independent *logrus.Logger
	// instance common to all requests
	ContextKeyLogger contextKey = iota
	// ContextKeyRequestLogger is the *logrus.Entry instance
	// that is annotated with request-identifying
	// information extracted from the AWS context object
	ContextKeyRequestLogger
	// ContextKeyLambdaContext is the *sparta.LambdaContext
	// pointer in the request
	// DEPRECATED
	ContextKeyLambdaContext
	// ContextKeyLambdaError is the possible error that was returned
	// from the lambda function
	ContextKeyLambdaError
	// ContextKeyLambdaResponse is the possible response that
	// was returned from the lambda function
	ContextKeyLambdaResponse
	// ContextKeyAWSSession is the aws Session instance for this
	// request
	ContextKeyAWSSession
)
<|endoftext|>


<commit_before>// Copyright 2012 Twitter, 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 twurlrc

import (
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
)

// Represents OAuth credentials to make requests on behalf of a user.
type Credentials struct {
	Token          string
	Username       string
	ConsumerKey    string
	ConsumerSecret string
	Secret         string
}

// Returns a path to the default twurlrc location.
func GetDefaultPath() string {
	return os.ExpandEnv("$HOME/.twurlrc")
}

// Represents a parsed ~/.twurlrc formatted file.
type Twurlrc struct {
	data map[string]interface{}
}

// Given the contents of a .twurlrc file, return a parsed data structure.
func Parse(text string) (*Twurlrc, error) {
	t := new(Twurlrc)
	t.data = make(map[string]interface{})
	err := goyaml.Unmarshal([]uint8(text), t.data)
	if err != nil {
		return nil, err
	}
	return t, nil
}

// Given a path to a twurlrc file, return a parsed data structure.
func Load(path string) (*Twurlrc, error) {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return Parse(string(text))
}

// Returns credentials for the given user profile and consumer key.
func (t *Twurlrc) GetCredentials(profile string, key string) *Credentials {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	keyMap := profileMap[profile].(map[interface{}]interface{})
	data := keyMap[key].(map[interface{}]interface{})
	return &Credentials{
		Token:          data["token"].(string),
		Username:       data["username"].(string),
		ConsumerKey:    data["consumer_key"].(string),
		ConsumerSecret: data["consumer_secret"].(string),
		Secret:         data["secret"].(string),
	}
}

// Returns the default credentials, as specified in the ~/.twurlrc file.
func (t *Twurlrc) GetDefaultCredentials() *Credentials {
	configMap := t.data["configuration"].(map[interface{}]interface{})
	parts := configMap["default_profile"].([]interface{})
	return t.GetCredentials(parts[0].(string), parts[1].(string))
}

// Returns a list of consumer keys authorized with the given profile.
func (t *Twurlrc) GetKeys(profile string) []string {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	keyMap := profileMap[profile].(map[interface{}]interface{})
	keys := make([]string, len(keyMap))
	i := 0
	for key, _ := range keyMap {
		keys[i] = key.(string)
		i++
	}
	return keys
}

// Returns a list of profiles listed in the ~/.twurlrc file.
func (t *Twurlrc) GetProfiles() []string {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	profiles := make([]string, len(profileMap))
	i := 0
	for key, _ := range profileMap {
		profiles[i] = key.(string)
		i++
	}
	return profiles
}
<commit_msg>Add a package description<commit_after>// Copyright 2012 Twitter, 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.

// A library for reading the Twitter configuration files written by Twurl.
package twurlrc

import (
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
)

// Represents OAuth credentials to make requests on behalf of a user.
type Credentials struct {
	Token          string
	Username       string
	ConsumerKey    string
	ConsumerSecret string
	Secret         string
}

// Returns a path to the default twurlrc location.
func GetDefaultPath() string {
	return os.ExpandEnv("$HOME/.twurlrc")
}

// Represents a parsed ~/.twurlrc formatted file.
type Twurlrc struct {
	data map[string]interface{}
}

// Given the contents of a .twurlrc file, return a parsed data structure.
func Parse(text string) (*Twurlrc, error) {
	t := new(Twurlrc)
	t.data = make(map[string]interface{})
	err := goyaml.Unmarshal([]uint8(text), t.data)
	if err != nil {
		return nil, err
	}
	return t, nil
}

// Given a path to a twurlrc file, return a parsed data structure.
func Load(path string) (*Twurlrc, error) {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return Parse(string(text))
}

// Returns credentials for the given user profile and consumer key.
func (t *Twurlrc) GetCredentials(profile string, key string) *Credentials {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	keyMap := profileMap[profile].(map[interface{}]interface{})
	data := keyMap[key].(map[interface{}]interface{})
	return &Credentials{
		Token:          data["token"].(string),
		Username:       data["username"].(string),
		ConsumerKey:    data["consumer_key"].(string),
		ConsumerSecret: data["consumer_secret"].(string),
		Secret:         data["secret"].(string),
	}
}

// Returns the default credentials, as specified in the ~/.twurlrc file.
func (t *Twurlrc) GetDefaultCredentials() *Credentials {
	configMap := t.data["configuration"].(map[interface{}]interface{})
	parts := configMap["default_profile"].([]interface{})
	return t.GetCredentials(parts[0].(string), parts[1].(string))
}

// Returns a list of consumer keys authorized with the given profile.
func (t *Twurlrc) GetKeys(profile string) []string {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	keyMap := profileMap[profile].(map[interface{}]interface{})
	keys := make([]string, len(keyMap))
	i := 0
	for key, _ := range keyMap {
		keys[i] = key.(string)
		i++
	}
	return keys
}

// Returns a list of profiles listed in the ~/.twurlrc file.
func (t *Twurlrc) GetProfiles() []string {
	profileMap := t.data["profiles"].(map[interface{}]interface{})
	profiles := make([]string, len(profileMap))
	i := 0
	for key, _ := range profileMap {
		profiles[i] = key.(string)
		i++
	}
	return profiles
}
<|endoftext|>


<commit_before>package handlers

import (
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/oauth2"

	gh "github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	lru "github.com/hashicorp/golang-lru"
	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/event"
	"github.com/play-with-docker/play-with-docker/pwd"
	"github.com/play-with-docker/play-with-docker/pwd/types"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/urfave/negroni"
	oauth2FB "golang.org/x/oauth2/facebook"
	oauth2Github "golang.org/x/oauth2/github"
)

var core pwd.PWDApi
var e event.EventApi
var landings = map[string][]byte{}

var latencyHistogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
	Name:    "pwd_handlers_duration_ms",
	Help:    "How long it took to process a specific handler, in a specific host",
	Buckets: []float64{300, 1200, 5000},
}, []string{"action"})

type HandlerExtender func(h *mux.Router)

func init() {
	prometheus.MustRegister(latencyHistogramVec)

}

func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
	core = c
	e = ev
}

func Register(extend HandlerExtender) {
	initPlaygrounds()

	r := mux.NewRouter()
	corsRouter := mux.NewRouter()

	corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOriginValidator(func(origin string) bool {
		if strings.HasSuffix(origin, "localhost") ||
			strings.HasSuffix(origin, "play-with-docker.com") ||
			strings.HasSuffix(origin, "play-with-moby.com") {
			return true
		}
		return false
	}), gh.AllowedOrigins([]string{}))

	// Specific routes
	r.HandleFunc("/ping", Ping).Methods("GET")
	corsRouter.HandleFunc("/instances/images", GetInstanceImages).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", GetSession).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", CloseSession).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/setup", SessionSetup).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances", NewInstance).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", FileUpload).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/fstree", fsTree).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/file", file).Methods("GET")

	r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/editor", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/editor.html")
	})

	r.HandleFunc("/ooc", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/ooc.html")
	}).Methods("GET")
	r.HandleFunc("/503", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/503.html")
	}).Methods("GET")
	r.HandleFunc("/p/{sessionId}", Home).Methods("GET")
	r.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./www")))
	r.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/robots.txt")
	})

	corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
	r.Handle("/metrics", promhttp.Handler())

	// Generic routes
	r.HandleFunc("/", Landing).Methods("GET")

	corsRouter.HandleFunc("/users/me", LoggedInUser).Methods("GET")
	r.HandleFunc("/users/{userId:^(?me)}", GetUser).Methods("GET")
	r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")
	r.HandleFunc("/playgrounds", NewPlayground).Methods("PUT")
	r.HandleFunc("/playgrounds", ListPlaygrounds).Methods("GET")
	r.HandleFunc("/my/playground", GetCurrentPlayground).Methods("GET")

	corsRouter.HandleFunc("/", NewSession).Methods("POST")

	if extend != nil {
		extend(corsRouter)
	}

	n := negroni.Classic()

	r.PathPrefix("/").Handler(negroni.New(negroni.Wrap(corsHandler(corsRouter))))
	n.UseHandler(r)

	httpServer := http.Server{
		Addr:              "0.0.0.0:" + config.PortNumber,
		Handler:           n,
		IdleTimeout:       30 * time.Second,
		ReadHeaderTimeout: 5 * time.Second,
	}

	if config.UseLetsEncrypt {
		domainCache, err := lru.New(5000)
		if err != nil {
			log.Fatalf("Could not start domain cache. Got: %v", err)
		}
		certManager := autocert.Manager{
			Prompt: autocert.AcceptTOS,
			HostPolicy: func(ctx context.Context, host string) error {
				if _, found := domainCache.Get(host); !found {
					if playground := core.PlaygroundFindByDomain(host); playground == nil {
						return fmt.Errorf("Playground for domain %s was not found", host)
					}
					domainCache.Add(host, true)
				}
				return nil
			},
			Cache: autocert.DirCache(config.LetsEncryptCertsDir),
		}

		httpServer.TLSConfig = &tls.Config{
			GetCertificate: certManager.GetCertificate,
		}

		go func() {
			rr := mux.NewRouter()
			rr.HandleFunc("/ping", Ping).Methods("GET")
			rr.Handle("/metrics", promhttp.Handler())
			rr.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
				target := fmt.Sprintf("https://%s%s", r.Host, r.URL.Path)
				if len(r.URL.RawQuery) > 0 {
					target += "?" + r.URL.RawQuery
				}
				http.Redirect(rw, r, target, http.StatusMovedPermanently)
			})
			nr := negroni.Classic()
			nr.UseHandler(rr)
			log.Println("Starting redirect server")
			redirectServer := http.Server{
				Addr:              "0.0.0.0:3001",
				Handler:           certManager.HTTPHandler(nr),
				IdleTimeout:       30 * time.Second,
				ReadHeaderTimeout: 5 * time.Second,
			}
			log.Fatal(redirectServer.ListenAndServe())
		}()

		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServeTLS("", ""))
	} else {
		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServe())
	}
}

func initPlaygrounds() {
	pgs, err := core.PlaygroundList()
	if err != nil {
		log.Fatal("Error getting playgrounds for initialization")
	}

	for _, p := range pgs {
		initAssets(p)
		initOauthProviders(p)
	}
}

func initAssets(p *types.Playground) {
	if p.AssetsDir == "" {
		p.AssetsDir = "default"
	}

	var b bytes.Buffer
	t, err := template.New("landing.html").Delims("[[", "]]").ParseFiles(fmt.Sprintf("./www/%s/landing.html", p.AssetsDir))
	if err != nil {
		log.Fatalf("Error parsing template %v", err)
	}
	if err := t.Execute(&b, struct{ SegmentId string }{config.SegmentId}); err != nil {
		log.Fatalf("Error executing template %v", err)
	}
	landingBytes, err := ioutil.ReadAll(&b)
	if err != nil {
		log.Fatalf("Error reading template bytes %v", err)
	}
	landings[p.Id] = landingBytes
}

func initOauthProviders(p *types.Playground) {
	config.Providers[p.Id] = map[string]*oauth2.Config{}

	if p.GithubClientID != "" && p.GithubClientSecret != "" {
		conf := &oauth2.Config{
			ClientID:     p.GithubClientID,
			ClientSecret: p.GithubClientSecret,
			Scopes:       []string{"user:email"},
			Endpoint:     oauth2Github.Endpoint,
		}

		config.Providers[p.Id]["github"] = conf
	}
	if p.FacebookClientID != "" && p.FacebookClientSecret != "" {
		conf := &oauth2.Config{
			ClientID:     p.FacebookClientID,
			ClientSecret: p.FacebookClientSecret,
			Scopes:       []string{"email", "public_profile"},
			Endpoint:     oauth2FB.Endpoint,
		}

		config.Providers[p.Id]["facebook"] = conf
	}
	if p.DockerClientID != "" && p.DockerClientSecret != "" {
		oauth2.RegisterBrokenAuthHeaderProvider(".id.docker.com")
		conf := &oauth2.Config{
			ClientID:     p.DockerClientID,
			ClientSecret: p.DockerClientSecret,
			Scopes:       []string{"openid"},
			Endpoint: oauth2.Endpoint{
				AuthURL:  "https://id.docker.com/id/oauth/authorize/",
				TokenURL: "https://id.docker.com/id/oauth/token",
			},
		}

		config.Providers[p.Id]["docker"] = conf
	}
}
<commit_msg>Fix localhost<commit_after>package handlers

import (
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/oauth2"

	gh "github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	lru "github.com/hashicorp/golang-lru"
	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/event"
	"github.com/play-with-docker/play-with-docker/pwd"
	"github.com/play-with-docker/play-with-docker/pwd/types"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/urfave/negroni"
	oauth2FB "golang.org/x/oauth2/facebook"
	oauth2Github "golang.org/x/oauth2/github"
)

var core pwd.PWDApi
var e event.EventApi
var landings = map[string][]byte{}

var latencyHistogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
	Name:    "pwd_handlers_duration_ms",
	Help:    "How long it took to process a specific handler, in a specific host",
	Buckets: []float64{300, 1200, 5000},
}, []string{"action"})

type HandlerExtender func(h *mux.Router)

func init() {
	prometheus.MustRegister(latencyHistogramVec)

}

func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
	core = c
	e = ev
}

func Register(extend HandlerExtender) {
	initPlaygrounds()

	r := mux.NewRouter()
	corsRouter := mux.NewRouter()

	corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOriginValidator(func(origin string) bool {
		if strings.Contains(origin, "localhost") ||
			strings.HasSuffix(origin, "play-with-docker.com") ||
			strings.HasSuffix(origin, "play-with-moby.com") {
			return true
		}
		return false
	}), gh.AllowedOrigins([]string{}))

	// Specific routes
	r.HandleFunc("/ping", Ping).Methods("GET")
	corsRouter.HandleFunc("/instances/images", GetInstanceImages).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", GetSession).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}", CloseSession).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/setup", SessionSetup).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances", NewInstance).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", FileUpload).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/fstree", fsTree).Methods("GET")
	corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/file", file).Methods("GET")

	r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/editor", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/editor.html")
	})

	r.HandleFunc("/ooc", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/ooc.html")
	}).Methods("GET")
	r.HandleFunc("/503", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "./www/503.html")
	}).Methods("GET")
	r.HandleFunc("/p/{sessionId}", Home).Methods("GET")
	r.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./www")))
	r.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
		http.ServeFile(rw, r, "www/robots.txt")
	})

	corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
	r.Handle("/metrics", promhttp.Handler())

	// Generic routes
	r.HandleFunc("/", Landing).Methods("GET")

	corsRouter.HandleFunc("/users/me", LoggedInUser).Methods("GET")
	r.HandleFunc("/users/{userId:^(?me)}", GetUser).Methods("GET")
	r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
	r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")
	r.HandleFunc("/playgrounds", NewPlayground).Methods("PUT")
	r.HandleFunc("/playgrounds", ListPlaygrounds).Methods("GET")
	r.HandleFunc("/my/playground", GetCurrentPlayground).Methods("GET")

	corsRouter.HandleFunc("/", NewSession).Methods("POST")

	if extend != nil {
		extend(corsRouter)
	}

	n := negroni.Classic()

	r.PathPrefix("/").Handler(negroni.New(negroni.Wrap(corsHandler(corsRouter))))
	n.UseHandler(r)

	httpServer := http.Server{
		Addr:              "0.0.0.0:" + config.PortNumber,
		Handler:           n,
		IdleTimeout:       30 * time.Second,
		ReadHeaderTimeout: 5 * time.Second,
	}

	if config.UseLetsEncrypt {
		domainCache, err := lru.New(5000)
		if err != nil {
			log.Fatalf("Could not start domain cache. Got: %v", err)
		}
		certManager := autocert.Manager{
			Prompt: autocert.AcceptTOS,
			HostPolicy: func(ctx context.Context, host string) error {
				if _, found := domainCache.Get(host); !found {
					if playground := core.PlaygroundFindByDomain(host); playground == nil {
						return fmt.Errorf("Playground for domain %s was not found", host)
					}
					domainCache.Add(host, true)
				}
				return nil
			},
			Cache: autocert.DirCache(config.LetsEncryptCertsDir),
		}

		httpServer.TLSConfig = &tls.Config{
			GetCertificate: certManager.GetCertificate,
		}

		go func() {
			rr := mux.NewRouter()
			rr.HandleFunc("/ping", Ping).Methods("GET")
			rr.Handle("/metrics", promhttp.Handler())
			rr.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
				target := fmt.Sprintf("https://%s%s", r.Host, r.URL.Path)
				if len(r.URL.RawQuery) > 0 {
					target += "?" + r.URL.RawQuery
				}
				http.Redirect(rw, r, target, http.StatusMovedPermanently)
			})
			nr := negroni.Classic()
			nr.UseHandler(rr)
			log.Println("Starting redirect server")
			redirectServer := http.Server{
				Addr:              "0.0.0.0:3001",
				Handler:           certManager.HTTPHandler(nr),
				IdleTimeout:       30 * time.Second,
				ReadHeaderTimeout: 5 * time.Second,
			}
			log.Fatal(redirectServer.ListenAndServe())
		}()

		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServeTLS("", ""))
	} else {
		log.Println("Listening on port " + config.PortNumber)
		log.Fatal(httpServer.ListenAndServe())
	}
}

func initPlaygrounds() {
	pgs, err := core.PlaygroundList()
	if err != nil {
		log.Fatal("Error getting playgrounds for initialization")
	}

	for _, p := range pgs {
		initAssets(p)
		initOauthProviders(p)
	}
}

func initAssets(p *types.Playground) {
	if p.AssetsDir == "" {
		p.AssetsDir = "default"
	}

	var b bytes.Buffer
	t, err := template.New("landing.html").Delims("[[", "]]").ParseFiles(fmt.Sprintf("./www/%s/landing.html", p.AssetsDir))
	if err != nil {
		log.Fatalf("Error parsing template %v", err)
	}
	if err := t.Execute(&b, struct{ SegmentId string }{config.SegmentId}); err != nil {
		log.Fatalf("Error executing template %v", err)
	}
	landingBytes, err := ioutil.ReadAll(&b)
	if err != nil {
		log.Fatalf("Error reading template bytes %v", err)
	}
	landings[p.Id] = landingBytes
}

func initOauthProviders(p *types.Playground) {
	config.Providers[p.Id] = map[string]*oauth2.Config{}

	if p.GithubClientID != "" && p.GithubClientSecret != "" {
		conf := &oauth2.Config{
			ClientID:     p.GithubClientID,
			ClientSecret: p.GithubClientSecret,
			Scopes:       []string{"user:email"},
			Endpoint:     oauth2Github.Endpoint,
		}

		config.Providers[p.Id]["github"] = conf
	}
	if p.FacebookClientID != "" && p.FacebookClientSecret != "" {
		conf := &oauth2.Config{
			ClientID:     p.FacebookClientID,
			ClientSecret: p.FacebookClientSecret,
			Scopes:       []string{"email", "public_profile"},
			Endpoint:     oauth2FB.Endpoint,
		}

		config.Providers[p.Id]["facebook"] = conf
	}
	if p.DockerClientID != "" && p.DockerClientSecret != "" {
		oauth2.RegisterBrokenAuthHeaderProvider(".id.docker.com")
		conf := &oauth2.Config{
			ClientID:     p.DockerClientID,
			ClientSecret: p.DockerClientSecret,
			Scopes:       []string{"openid"},
			Endpoint: oauth2.Endpoint{
				AuthURL:  "https://id.docker.com/id/oauth/authorize/",
				TokenURL: "https://id.docker.com/id/oauth/token",
			},
		}

		config.Providers[p.Id]["docker"] = conf
	}
}
<|endoftext|>


<commit_before>package drouter

import (
	log "github.com/Sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/ziutek/utils/netaddr"
	"net"
	"os"
)

type p2pNetwork struct {
	network       *net.IPNet
	hostIP        net.IP
	selfIP        net.IP
	hostNamespace *netlink.Handle
	hostUnderlay  *net.IPNet
}

func newP2PNetwork(p2paddr string) (*p2pNetwork, error) {
	hns, err := netlinkHandleFromPid(1)
	if err != nil {
		log.Error("Failed to get the host's namespace.")
		return nil, err
	}

	log.Debugf("Making a p2p network for: %v", p2paddr)

	//check if drouter_veth0 already exists
	host_link, err := hns.LinkByName("drouter_veth0")
	if err == nil {
		//doesn't exist, create it
		host_link_veth := &netlink.Veth{
			LinkAttrs: netlink.LinkAttrs{Name: "drouter_veth0"},
			PeerName:  "drouter_veth1",
		}
		err2 := hns.LinkAdd(host_link_veth)
		if err2 != nil {
			return nil, err2
		}
		host_link, err2 = hns.LinkByName("drouter_veth0")
		if err2 != nil {
			return nil, err2
		}
	}

	int_link, err := hns.LinkByName("drouter_veth1")
	if err != nil {
		return nil, err
	}
	err = hns.LinkSetNsPid(int_link, os.Getpid())
	if err != nil {
		return nil, err
	}
	int_link, err = netlink.LinkByName("drouter_veth1")
	if err != nil {
		return nil, err
	}

	_, p2pIPNet, err := net.ParseCIDR(p2paddr)
	if err != nil {
		log.Errorf("Failed to parse the CIDR string for p2p network: %v", p2paddr)
		return nil, err
	}

	host_addr := *p2pIPNet
	host_addr.IP = netaddr.IPAdd(host_addr.IP, 1)
	host_netlink_addr := &netlink.Addr{
		IPNet: &host_addr,
		Label: "",
	}
	err = hns.AddrAdd(host_link, host_netlink_addr)
	if err != nil {
		return nil, err
	}

	int_addr := *p2pIPNet
	int_addr.IP = netaddr.IPAdd(int_addr.IP, 2)
	int_netlink_addr := &netlink.Addr{
		IPNet: &int_addr,
		Label: "",
	}
	err = netlink.AddrAdd(int_link, int_netlink_addr)
	if err != nil {
		return nil, err
	}

	err = netlink.LinkSetUp(int_link)
	if err != nil {
		return nil, err
	}

	err = hns.LinkSetUp(host_link)
	if err != nil {
		return nil, err
	}

	//discover host underlay address/network
	hunderlay := &net.IPNet{}
	hroutes, err := hns.RouteList(nil, netlink.FAMILY_V4)
	if err != nil {
		return nil, err
	}
Hroutes:
	for _, r := range hroutes {
		if r.Gw != nil {
			continue
		}
		link, err := hns.LinkByIndex(r.LinkIndex)
		if err != nil {
			return nil, err
		}
		addrs, err := hns.AddrList(link, netlink.FAMILY_V4)
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			if !addr.IP.Equal(r.Src) {
				continue
			}
			hunderlay.IP = addr.IP
			hunderlay.Mask = addr.Mask
			break Hroutes
		}
	}

	log.Debugf("Discovered host underlay as: %v", hunderlay)

	staticRoutes = append(staticRoutes, networkID(hunderlay))

	hroute := &netlink.Route{
		LinkIndex: int_link.Attrs().Index,
		Dst:       networkID(hunderlay),
		Gw:        host_addr.IP,
	}

	log.Debug("Adding drouter route to %v via %v.", hroute.Dst, hroute.Gw)
	err = netlink.RouteAdd(hroute)
	if err != nil {
		return nil, err
	}

	p2pnet := &p2pNetwork{
		network:       p2pIPNet,
		hostIP:        host_addr.IP,
		selfIP:        int_addr.IP,
		hostNamespace: hns,
		hostUnderlay:  hunderlay,
	}

	for _, sr := range staticRoutes {
		if subnetContainsSubnet(hroute.Dst, sr) {
			log.Debugf("Skipping static route %v covered by host underlay: %v", sr, hroute.Dst)
			continue
		}
		go p2pnet.addHostRoute(sr)
	}

	return p2pnet, nil
}

func (p *p2pNetwork) remove() error {
	host_link, err := p.hostNamespace.LinkByName("drouter_veth0")
	if err != nil {
		return err
	}

	return p.hostNamespace.LinkDel(host_link)
}

func (p *p2pNetwork) addHostRoute(sn *net.IPNet) {
	route := &netlink.Route{
		Gw:  p.selfIP,
		Dst: sn,
		Src: p.hostUnderlay.IP,
	}
	if (route.Dst.IP.To4() == nil) != (route.Gw.To4() == nil) {
		// Dst is a different IP family
		return
	}

	log.Debugf("Injecting shortcut route to %v via drouter into host routing table.", sn)
	err := p.hostNamespace.RouteAdd(route)
	if err != nil {
		log.Error(err)
	}
}

func (p *p2pNetwork) delHostRoute(sn *net.IPNet) {
	route := &netlink.Route{
		Gw:  p.selfIP,
		Dst: sn,
	}
	if (route.Dst.IP.To4() == nil) != (route.Gw.To4() == nil) {
		// Dst is a different IP family
		return
	}

	log.Debugf("Removing shortcut route to %v via drouter from host routing table.", sn)
	err := p.hostNamespace.RouteDel(route)
	if err != nil {
		log.Error(err)
	}
}
<commit_msg>not equal<commit_after>package drouter

import (
	log "github.com/Sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/ziutek/utils/netaddr"
	"net"
	"os"
)

type p2pNetwork struct {
	network       *net.IPNet
	hostIP        net.IP
	selfIP        net.IP
	hostNamespace *netlink.Handle
	hostUnderlay  *net.IPNet
}

func newP2PNetwork(p2paddr string) (*p2pNetwork, error) {
	hns, err := netlinkHandleFromPid(1)
	if err != nil {
		log.Error("Failed to get the host's namespace.")
		return nil, err
	}

	log.Debugf("Making a p2p network for: %v", p2paddr)

	//check if drouter_veth0 already exists
	host_link, err := hns.LinkByName("drouter_veth0")
	if err != nil {
		//doesn't exist, create it
		host_link_veth := &netlink.Veth{
			LinkAttrs: netlink.LinkAttrs{Name: "drouter_veth0"},
			PeerName:  "drouter_veth1",
		}
		err2 := hns.LinkAdd(host_link_veth)
		if err2 != nil {
			return nil, err2
		}
		host_link, err2 = hns.LinkByName("drouter_veth0")
		if err2 != nil {
			return nil, err2
		}
	}

	int_link, err := hns.LinkByName("drouter_veth1")
	if err != nil {
		return nil, err
	}
	err = hns.LinkSetNsPid(int_link, os.Getpid())
	if err != nil {
		return nil, err
	}
	int_link, err = netlink.LinkByName("drouter_veth1")
	if err != nil {
		return nil, err
	}

	_, p2pIPNet, err := net.ParseCIDR(p2paddr)
	if err != nil {
		log.Errorf("Failed to parse the CIDR string for p2p network: %v", p2paddr)
		return nil, err
	}

	host_addr := *p2pIPNet
	host_addr.IP = netaddr.IPAdd(host_addr.IP, 1)
	host_netlink_addr := &netlink.Addr{
		IPNet: &host_addr,
		Label: "",
	}
	err = hns.AddrAdd(host_link, host_netlink_addr)
	if err != nil {
		return nil, err
	}

	int_addr := *p2pIPNet
	int_addr.IP = netaddr.IPAdd(int_addr.IP, 2)
	int_netlink_addr := &netlink.Addr{
		IPNet: &int_addr,
		Label: "",
	}
	err = netlink.AddrAdd(int_link, int_netlink_addr)
	if err != nil {
		return nil, err
	}

	err = netlink.LinkSetUp(int_link)
	if err != nil {
		return nil, err
	}

	err = hns.LinkSetUp(host_link)
	if err != nil {
		return nil, err
	}

	//discover host underlay address/network
	hunderlay := &net.IPNet{}
	hroutes, err := hns.RouteList(nil, netlink.FAMILY_V4)
	if err != nil {
		return nil, err
	}
Hroutes:
	for _, r := range hroutes {
		if r.Gw != nil {
			continue
		}
		link, err := hns.LinkByIndex(r.LinkIndex)
		if err != nil {
			return nil, err
		}
		addrs, err := hns.AddrList(link, netlink.FAMILY_V4)
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			if !addr.IP.Equal(r.Src) {
				continue
			}
			hunderlay.IP = addr.IP
			hunderlay.Mask = addr.Mask
			break Hroutes
		}
	}

	log.Debugf("Discovered host underlay as: %v", hunderlay)

	staticRoutes = append(staticRoutes, networkID(hunderlay))

	hroute := &netlink.Route{
		LinkIndex: int_link.Attrs().Index,
		Dst:       networkID(hunderlay),
		Gw:        host_addr.IP,
	}

	log.Debug("Adding drouter route to %v via %v.", hroute.Dst, hroute.Gw)
	err = netlink.RouteAdd(hroute)
	if err != nil {
		return nil, err
	}

	p2pnet := &p2pNetwork{
		network:       p2pIPNet,
		hostIP:        host_addr.IP,
		selfIP:        int_addr.IP,
		hostNamespace: hns,
		hostUnderlay:  hunderlay,
	}

	for _, sr := range staticRoutes {
		if subnetContainsSubnet(hroute.Dst, sr) {
			log.Debugf("Skipping static route %v covered by host underlay: %v", sr, hroute.Dst)
			continue
		}
		go p2pnet.addHostRoute(sr)
	}

	return p2pnet, nil
}

func (p *p2pNetwork) remove() error {
	host_link, err := p.hostNamespace.LinkByName("drouter_veth0")
	if err != nil {
		return err
	}

	return p.hostNamespace.LinkDel(host_link)
}

func (p *p2pNetwork) addHostRoute(sn *net.IPNet) {
	route := &netlink.Route{
		Gw:  p.selfIP,
		Dst: sn,
		Src: p.hostUnderlay.IP,
	}
	if (route.Dst.IP.To4() == nil) != (route.Gw.To4() == nil) {
		// Dst is a different IP family
		return
	}

	log.Debugf("Injecting shortcut route to %v via drouter into host routing table.", sn)
	err := p.hostNamespace.RouteAdd(route)
	if err != nil {
		log.Error(err)
	}
}

func (p *p2pNetwork) delHostRoute(sn *net.IPNet) {
	route := &netlink.Route{
		Gw:  p.selfIP,
		Dst: sn,
	}
	if (route.Dst.IP.To4() == nil) != (route.Gw.To4() == nil) {
		// Dst is a different IP family
		return
	}

	log.Debugf("Removing shortcut route to %v via drouter from host routing table.", sn)
	err := p.hostNamespace.RouteDel(route)
	if err != nil {
		log.Error(err)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

func lits(strs ...string) []node {
	l := make([]node, 0, len(strs))
	for _, s := range strs {
		l = append(l, lit{val: s})
	}
	return l
}

var tests = []struct {
	ins  []string
	want interface{}
}{
	{
		ins:  []string{"", " ", "\n"},
		want: nil,
	},
	{
		ins:  []string{"# foo", "# foo\n"},
		want: comment{text: " foo"},
	},
	{
		ins:  []string{"foo", "foo ", " foo"},
		want: command{args: lits("foo")},
	},
	{
		ins: []string{"foo; bar", "foo; bar;", "\nfoo\nbar\n"},
		want: []node{
			command{args: lits("foo")},
			command{args: lits("bar")},
		},
	},
	{
		ins:  []string{"foo a b", " foo  a  b ", "foo \\\n a b"},
		want: command{args: lits("foo", "a", "b")},
	},
	{
		ins: []string{"( foo; )", "(foo;)", "(\nfoo\n)"},
		want: subshell{stmts: []node{
			command{args: lits("foo")},
		}},
	},
	{
		ins: []string{"{ foo; }", "{foo;}", "{\nfoo\n}"},
		want: block{stmts: []node{
			command{args: lits("foo")},
		}},
	},
	{
		ins: []string{
			"if a; then b; fi",
			"if a\nthen\nb\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("b")},
			},
		},
	},
	{
		ins: []string{
			"if a; then b; else c; fi",
			"if a\nthen b\nelse\nc\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("b")},
			},
			elseStmts: []node{
				command{args: lits("c")},
			},
		},
	},
	{
		ins: []string{
			"if a; then a; elif b; then b; elif c; then c; else d; fi",
			"if a\nthen a\nelif b\nthen b\nelif c\nthen c\nelse\nd\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("a")},
			},
			elifs: []node{
				elif{cond: command{args: lits("b")},
					thenStmts: []node{
						command{args: lits("b")},
					}},
				elif{cond: command{args: lits("c")},
					thenStmts: []node{
						command{args: lits("c")},
					}},
			},
			elseStmts: []node{
				command{args: lits("d")},
			},
		},
	},
	{
		ins: []string{"while a; do b; done", "while a\ndo\nb\ndone"},
		want: whileStmt{
			cond: command{args: lits("a")},
			doStmts: []node{
				command{args: lits("b")},
			},
		},
	},
	{
		ins:  []string{"echo ' ' \"foo bar\""},
		want: command{args: lits("echo", "' '", "\"foo bar\"")},
	},
	{
		ins:  []string{"$a ${b} s{s s=s"},
		want: command{args: lits("$a", "${b}", "s{s", "s=s")},
	},
	{
		ins: []string{"foo && bar", "foo&&bar", "foo &&\nbar"},
		want: binaryExpr{
			op: "&&",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo || bar", "foo||bar", "foo ||\nbar"},
		want: binaryExpr{
			op: "||",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo && bar || else"},
		want: binaryExpr{
			op: "&&",
			X:  command{args: lits("foo")},
			Y: binaryExpr{
				op: "||",
				X:  command{args: lits("bar")},
				Y:  command{args: lits("else")},
			},
		},
	},
	{
		ins: []string{"foo | bar"},
		want: binaryExpr{
			op: "|",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo | bar | extra"},
		want: binaryExpr{
			op: "|",
			X:  command{args: lits("foo")},
			Y: binaryExpr{
				op: "|",
				X:  command{args: lits("bar")},
				Y:  command{args: lits("extra")},
			},
		},
	},
	{
		ins: []string{
			"foo() { a; b; }",
			"foo() {\na\nb\n}",
			"foo ( ) {\na\nb\n}",
		},
		want: funcDecl{
			name: lit{val: "foo"},
			body: block{stmts: []node{
				command{args: lits("a")},
				command{args: lits("b")},
			}},
		},
	},
	{
		ins: []string{
			"foo >a >>b <c",
			"foo > a >> b < c",
		},
		want: command{
			args: []node{
				lit{val: "foo"},
				redirect{op: ">", obj: lit{val: "a"}},
				redirect{op: ">>", obj: lit{val: "b"}},
				redirect{op: "<", obj: lit{val: "c"}},
			},
		},
	},
}

func wantedProg(v interface{}) (p prog) {
	switch x := v.(type) {
	case []node:
		p.stmts = x
	case node:
		p.stmts = append(p.stmts, x)
	}
	return
}

func TestParseAST(t *testing.T) {
	for _, c := range tests {
		want := wantedProg(c.want)
		for _, in := range c.ins {
			r := strings.NewReader(in)
			got, err := parse(r, "")
			if err != nil {
				t.Fatalf("Unexpected error in %q: %v", in, err)
			}
			if !reflect.DeepEqual(got, want) {
				t.Fatalf("AST mismatch in %q\nwant: %s\ngot:  %s\ndumps:\n%#v\n%#v",
					in, want.String(), got.String(), want, got)
			}
		}
	}
}

func TestPrintAST(t *testing.T) {
	for _, c := range tests {
		in := wantedProg(c.want)
		want := c.ins[0]
		got := in.String()
		if got != want {
			t.Fatalf("AST print mismatch\nwant: %s\ngot:  %s",
				want, got)
		}
	}
}
<commit_msg>Test pipes without spaces<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

func lits(strs ...string) []node {
	l := make([]node, 0, len(strs))
	for _, s := range strs {
		l = append(l, lit{val: s})
	}
	return l
}

var tests = []struct {
	ins  []string
	want interface{}
}{
	{
		ins:  []string{"", " ", "\n"},
		want: nil,
	},
	{
		ins:  []string{"# foo", "# foo\n"},
		want: comment{text: " foo"},
	},
	{
		ins:  []string{"foo", "foo ", " foo"},
		want: command{args: lits("foo")},
	},
	{
		ins: []string{"foo; bar", "foo; bar;", "\nfoo\nbar\n"},
		want: []node{
			command{args: lits("foo")},
			command{args: lits("bar")},
		},
	},
	{
		ins:  []string{"foo a b", " foo  a  b ", "foo \\\n a b"},
		want: command{args: lits("foo", "a", "b")},
	},
	{
		ins: []string{"( foo; )", "(foo;)", "(\nfoo\n)"},
		want: subshell{stmts: []node{
			command{args: lits("foo")},
		}},
	},
	{
		ins: []string{"{ foo; }", "{foo;}", "{\nfoo\n}"},
		want: block{stmts: []node{
			command{args: lits("foo")},
		}},
	},
	{
		ins: []string{
			"if a; then b; fi",
			"if a\nthen\nb\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("b")},
			},
		},
	},
	{
		ins: []string{
			"if a; then b; else c; fi",
			"if a\nthen b\nelse\nc\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("b")},
			},
			elseStmts: []node{
				command{args: lits("c")},
			},
		},
	},
	{
		ins: []string{
			"if a; then a; elif b; then b; elif c; then c; else d; fi",
			"if a\nthen a\nelif b\nthen b\nelif c\nthen c\nelse\nd\nfi",
		},
		want: ifStmt{
			cond: command{args: lits("a")},
			thenStmts: []node{
				command{args: lits("a")},
			},
			elifs: []node{
				elif{cond: command{args: lits("b")},
					thenStmts: []node{
						command{args: lits("b")},
					}},
				elif{cond: command{args: lits("c")},
					thenStmts: []node{
						command{args: lits("c")},
					}},
			},
			elseStmts: []node{
				command{args: lits("d")},
			},
		},
	},
	{
		ins: []string{"while a; do b; done", "while a\ndo\nb\ndone"},
		want: whileStmt{
			cond: command{args: lits("a")},
			doStmts: []node{
				command{args: lits("b")},
			},
		},
	},
	{
		ins:  []string{"echo ' ' \"foo bar\""},
		want: command{args: lits("echo", "' '", "\"foo bar\"")},
	},
	{
		ins:  []string{"$a ${b} s{s s=s"},
		want: command{args: lits("$a", "${b}", "s{s", "s=s")},
	},
	{
		ins: []string{"foo && bar", "foo&&bar", "foo &&\nbar"},
		want: binaryExpr{
			op: "&&",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo || bar", "foo||bar", "foo ||\nbar"},
		want: binaryExpr{
			op: "||",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo && bar || else"},
		want: binaryExpr{
			op: "&&",
			X:  command{args: lits("foo")},
			Y: binaryExpr{
				op: "||",
				X:  command{args: lits("bar")},
				Y:  command{args: lits("else")},
			},
		},
	},
	{
		ins: []string{"foo | bar", "foo|bar"},
		want: binaryExpr{
			op: "|",
			X:  command{args: lits("foo")},
			Y:  command{args: lits("bar")},
		},
	},
	{
		ins: []string{"foo | bar | extra"},
		want: binaryExpr{
			op: "|",
			X:  command{args: lits("foo")},
			Y: binaryExpr{
				op: "|",
				X:  command{args: lits("bar")},
				Y:  command{args: lits("extra")},
			},
		},
	},
	{
		ins: []string{
			"foo() { a; b; }",
			"foo() {\na\nb\n}",
			"foo ( ) {\na\nb\n}",
		},
		want: funcDecl{
			name: lit{val: "foo"},
			body: block{stmts: []node{
				command{args: lits("a")},
				command{args: lits("b")},
			}},
		},
	},
	{
		ins: []string{
			"foo >a >>b <c",
			"foo > a >> b < c",
		},
		want: command{
			args: []node{
				lit{val: "foo"},
				redirect{op: ">", obj: lit{val: "a"}},
				redirect{op: ">>", obj: lit{val: "b"}},
				redirect{op: "<", obj: lit{val: "c"}},
			},
		},
	},
}

func wantedProg(v interface{}) (p prog) {
	switch x := v.(type) {
	case []node:
		p.stmts = x
	case node:
		p.stmts = append(p.stmts, x)
	}
	return
}

func TestParseAST(t *testing.T) {
	for _, c := range tests {
		want := wantedProg(c.want)
		for _, in := range c.ins {
			r := strings.NewReader(in)
			got, err := parse(r, "")
			if err != nil {
				t.Fatalf("Unexpected error in %q: %v", in, err)
			}
			if !reflect.DeepEqual(got, want) {
				t.Fatalf("AST mismatch in %q\nwant: %s\ngot:  %s\ndumps:\n%#v\n%#v",
					in, want.String(), got.String(), want, got)
			}
		}
	}
}

func TestPrintAST(t *testing.T) {
	for _, c := range tests {
		in := wantedProg(c.want)
		want := c.ins[0]
		got := in.String()
		if got != want {
			t.Fatalf("AST print mismatch\nwant: %s\ngot:  %s",
				want, got)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/token"
	"strconv"
	"strings"
)

var (
	builtinFuncs = []string{
		"append",
		"cap",
		"close",
		"complex",
		"copy",
		"delete",
		"imag",
		"len",
		"make",
		"new",
		"panic",
		"print",
		"println",
		"real",
		"recover",
	}
)

// replaceAllRawStringLitByStringLit replaces all raw string literals in root by string literals.
func replaceAllRawStringLitByStringLit(root ast.Node) {
	ast.Inspect(root, func(n ast.Node) bool {
		if n, ok := n.(*ast.BasicLit); ok {
			if isRawStringLit(n) {
				n.Value = strconv.Quote(strings.Trim(n.Value, "`"))
			}
		}

		return true
	})
}

// getAssertImport returns *ast.ImportSpec of "github.com/ToQoz/gopwt/assert"
// if it is not found, this returns nil
func getAssertImport(a *ast.File) *ast.ImportSpec {
	for _, decl := range a.Decls {
		decl, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		if len(decl.Specs) == 0 {
			continue
		}

		if _, ok := decl.Specs[0].(*ast.ImportSpec); !ok {
			continue
		}

		for _, imp := range decl.Specs {
			imp := imp.(*ast.ImportSpec)

			if imp.Path.Value == `"github.com/ToQoz/gopwt/assert"` {
				return imp
			}
		}
	}

	return nil
}

// isAssert returns ok if given CallExpr is github.com/ToQoz/gopwt/assert.OK
func isAssert(x *ast.Ident, c *ast.CallExpr) bool {
	if s, ok := c.Fun.(*ast.SelectorExpr); ok {
		return s.X.(*ast.Ident).Name == x.Name && s.Sel.Name == "OK"
	}

	return false
}

func isBuiltinFunc(n *ast.CallExpr) bool {
	if f, ok := n.Fun.(*ast.Ident); ok {
		for _, b := range builtinFuncs {
			if f.Name == b {
				return true
			}
		}
	}

	return false
}

func isMapType(n ast.Node) bool {
	if n, ok := n.(*ast.CompositeLit); ok {
		_, ismap := n.Type.(*ast.MapType)
		return ismap
	}

	return false
}

func isRawStringLit(n *ast.BasicLit) bool {
	return n.Kind == token.STRING && strings.HasPrefix(n.Value, "`") && strings.HasSuffix(n.Value, "`")
}

func createUntypedCallExprFromBuiltinCallExpr(n *ast.CallExpr) *ast.CallExpr {
	createAltBuiltin := func(bfuncName string, args []ast.Expr) *ast.CallExpr {
		return &ast.CallExpr{
			Fun:  &ast.SelectorExpr{X: translatedassertImportIdent, Sel: &ast.Ident{Name: "B" + bfuncName}},
			Args: args,
		}
	}

	name := n.Fun.(*ast.Ident).Name

	switch name {
	case "append", "cap", "complex", "copy", "imag", "len", "real":
		return createAltBuiltin(name, n.Args)
	case "new":
		return createAltBuiltin(name, []ast.Expr{createReflectTypeExprFromTypeExpr(n.Args[0])})
	case "make":
		args := []ast.Expr{}
		args = append(args, createReflectTypeExprFromTypeExpr(n.Args[0]))
		args = append(args, n.Args[1:]...)
		return createAltBuiltin(name, args)
	default:
		panic(fmt.Errorf("%s can't be used in assert", name))
	}
}

// createUntypedExprFromBinaryExpr creates untyped operator-func(translatedassert.Op*()) from BinaryExpr
// if given BinaryExpr is untyped, returns it.
func createUntypedExprFromBinaryExpr(n *ast.BinaryExpr) ast.Expr {
	createFuncOp := func(opName string, x ast.Expr, y ast.Expr) *ast.CallExpr {
		return &ast.CallExpr{
			Fun:  &ast.SelectorExpr{X: translatedassertImportIdent, Sel: &ast.Ident{Name: "Op" + opName}},
			Args: []ast.Expr{x, y},
		}
	}

	// http://golang.org/ref/spec#Operators_and_Delimiters
	// +    sum                    integers, floats, complex values, strings
	// -    difference             integers, floats, complex values
	// *    product                integers, floats, complex values
	// /    quotient               integers, floats, complex values
	// %    remainder              integers

	// &    bitwise AND            integers
	// |    bitwise OR             integers
	// ^    bitwise XOR            integers
	// &^   bit clear (AND NOT)    integers

	// <<   left shift             integer << unsigned integer
	// >>   right shift            integer >> unsigned integer

	// http://golang.org/ref/spec#Logical_operators
	// Logical operators apply to boolean values and yield a result of the same type as the operands. The right operand is evaluated conditionally.

	// &&    conditional AND    p && q  is  "if p then q else false"
	// ||    conditional OR     p || q  is  "if p then true else q"
	switch n.Op {
	case token.ADD: // +
		return createFuncOp("ADD", n.X, n.Y)
	case token.SUB: // -
		return createFuncOp("SUB", n.X, n.Y)
	case token.MUL: // *
		return createFuncOp("MUL", n.X, n.Y)
	case token.QUO: // /
		return createFuncOp("QUO", n.X, n.Y)
	case token.REM: // %
		return createFuncOp("REM", n.X, n.Y)
	case token.AND: // &
		return createFuncOp("AND", n.X, n.Y)
	case token.OR: // |
		return createFuncOp("OR", n.X, n.Y)
	case token.XOR: // ^
		return createFuncOp("XOR", n.X, n.Y)
	case token.AND_NOT: // &^
		return createFuncOp("ANDNOT", n.X, n.Y)
	case token.SHL: // <<
		return createFuncOp("SHL", n.X, n.Y)
	case token.SHR: // >>
		return createFuncOp("SHR", n.X, n.Y)
	case token.LAND: // &&
		return createFuncOp("LAND", n.X, n.Y)
	case token.LOR: // ||
		return createFuncOp("LOR", n.X, n.Y)
	}

	return n
}

// f(a, b) -> translatedassert.FRVInterface(translatedassert.MFCall(filename, line, pos, f, translatedassert.RVOf(a), translatedassert.RVOf(b)))
func createMemorizedFuncCall(filename string, line int, n *ast.CallExpr, returnType string) *ast.CallExpr {
	c := &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "MFCall"},
		},
		Args: []ast.Expr{
			&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(filename)},
			&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(line)},
			&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(n.Pos()))},
			createReflectValueOfExpr(n.Fun),
		},
	}

	args := []ast.Expr{}
	for _, a := range n.Args {
		args = append(args, createReflectValueOfExpr(a))
	}
	c.Args = append(c.Args, args...)

	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "FRV" + returnType},
		},
		Args: []ast.Expr{c},
	}
}

// createReflectTypeExprFromTypeExpr create ast of reflect.Type from ast of type.
func createReflectTypeExprFromTypeExpr(t ast.Expr) ast.Expr {
	canUseCompositeLit := true

	if t, ok := t.(*ast.Ident); ok {
		switch t.Name {
		case "string", "rune",
			"uint", "uint8", "uint16", "uint32", "uint64",
			"int8", "int32", "int64", "int",
			"float32", "float64",
			"complex64", "complex128",
			"bool", "uintptr", "error":

			canUseCompositeLit = false
		}
	}

	if _, ok := t.(*ast.ChanType); ok {
		canUseCompositeLit = false
	}

	if !canUseCompositeLit {
		rv := &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X: createReflectValueOfExpr(&ast.CallExpr{
					Fun:  &ast.Ident{Name: "new"},
					Args: []ast.Expr{t},
				}),
				Sel: &ast.Ident{Name: "Elem"},
			},
		}

		return &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X:   rv,
				Sel: &ast.Ident{Name: "Type"},
			},
		}
	}

	return createReflectTypeOfExpr(&ast.CompositeLit{Type: t})
}

func createReflectInterfaceExpr(rv ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVInterface"},
		},
		Args: []ast.Expr{rv},
	}
}

func createReflectBoolExpr(rv ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVBool"},
		},
		Args: []ast.Expr{rv},
	}
}

func createReflectValueOfExpr(v ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVOf"},
		},
		Args: []ast.Expr{v},
	}
}

func createReflectTypeOfExpr(v ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RTOf"},
		},
		Args: []ast.Expr{v},
	}
}

func createPosValuePairExpr(ps []printExpr) []ast.Expr {
	args := []ast.Expr{}

	for _, n := range ps {
		a := &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X:   translatedassertImportIdent,
				Sel: &ast.Ident{Name: "NewPosValuePair"},
			},
			Args: []ast.Expr{
				&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(n.Pos))},
				n.Expr,
			},
		}

		args = append(args, a)
	}

	return args
}

func createRawStringLit(s string) *ast.BasicLit {
	return &ast.BasicLit{Kind: token.STRING, Value: "`" + s + "`"}
}
<commit_msg>Fix isAssert's panic on *ast.SelectorExpr.X != *ast.Ident<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/token"
	"strconv"
	"strings"
)

var (
	builtinFuncs = []string{
		"append",
		"cap",
		"close",
		"complex",
		"copy",
		"delete",
		"imag",
		"len",
		"make",
		"new",
		"panic",
		"print",
		"println",
		"real",
		"recover",
	}
)

// replaceAllRawStringLitByStringLit replaces all raw string literals in root by string literals.
func replaceAllRawStringLitByStringLit(root ast.Node) {
	ast.Inspect(root, func(n ast.Node) bool {
		if n, ok := n.(*ast.BasicLit); ok {
			if isRawStringLit(n) {
				n.Value = strconv.Quote(strings.Trim(n.Value, "`"))
			}
		}

		return true
	})
}

// getAssertImport returns *ast.ImportSpec of "github.com/ToQoz/gopwt/assert"
// if it is not found, this returns nil
func getAssertImport(a *ast.File) *ast.ImportSpec {
	for _, decl := range a.Decls {
		decl, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		if len(decl.Specs) == 0 {
			continue
		}

		if _, ok := decl.Specs[0].(*ast.ImportSpec); !ok {
			continue
		}

		for _, imp := range decl.Specs {
			imp := imp.(*ast.ImportSpec)

			if imp.Path.Value == `"github.com/ToQoz/gopwt/assert"` {
				return imp
			}
		}
	}

	return nil
}

// isAssert returns ok if given CallExpr is github.com/ToQoz/gopwt/assert.OK
func isAssert(x *ast.Ident, c *ast.CallExpr) bool {
	if s, ok := c.Fun.(*ast.SelectorExpr); ok {
		if xident, ok := s.X.(*ast.Ident); ok {
			return xident.Name == x.Name && s.Sel.Name == "OK"
		}
	}

	return false
}

func isBuiltinFunc(n *ast.CallExpr) bool {
	if f, ok := n.Fun.(*ast.Ident); ok {
		for _, b := range builtinFuncs {
			if f.Name == b {
				return true
			}
		}
	}

	return false
}

func isMapType(n ast.Node) bool {
	if n, ok := n.(*ast.CompositeLit); ok {
		_, ismap := n.Type.(*ast.MapType)
		return ismap
	}

	return false
}

func isRawStringLit(n *ast.BasicLit) bool {
	return n.Kind == token.STRING && strings.HasPrefix(n.Value, "`") && strings.HasSuffix(n.Value, "`")
}

func createUntypedCallExprFromBuiltinCallExpr(n *ast.CallExpr) *ast.CallExpr {
	createAltBuiltin := func(bfuncName string, args []ast.Expr) *ast.CallExpr {
		return &ast.CallExpr{
			Fun:  &ast.SelectorExpr{X: translatedassertImportIdent, Sel: &ast.Ident{Name: "B" + bfuncName}},
			Args: args,
		}
	}

	name := n.Fun.(*ast.Ident).Name

	switch name {
	case "append", "cap", "complex", "copy", "imag", "len", "real":
		return createAltBuiltin(name, n.Args)
	case "new":
		return createAltBuiltin(name, []ast.Expr{createReflectTypeExprFromTypeExpr(n.Args[0])})
	case "make":
		args := []ast.Expr{}
		args = append(args, createReflectTypeExprFromTypeExpr(n.Args[0]))
		args = append(args, n.Args[1:]...)
		return createAltBuiltin(name, args)
	default:
		panic(fmt.Errorf("%s can't be used in assert", name))
	}
}

// createUntypedExprFromBinaryExpr creates untyped operator-func(translatedassert.Op*()) from BinaryExpr
// if given BinaryExpr is untyped, returns it.
func createUntypedExprFromBinaryExpr(n *ast.BinaryExpr) ast.Expr {
	createFuncOp := func(opName string, x ast.Expr, y ast.Expr) *ast.CallExpr {
		return &ast.CallExpr{
			Fun:  &ast.SelectorExpr{X: translatedassertImportIdent, Sel: &ast.Ident{Name: "Op" + opName}},
			Args: []ast.Expr{x, y},
		}
	}

	// http://golang.org/ref/spec#Operators_and_Delimiters
	// +    sum                    integers, floats, complex values, strings
	// -    difference             integers, floats, complex values
	// *    product                integers, floats, complex values
	// /    quotient               integers, floats, complex values
	// %    remainder              integers

	// &    bitwise AND            integers
	// |    bitwise OR             integers
	// ^    bitwise XOR            integers
	// &^   bit clear (AND NOT)    integers

	// <<   left shift             integer << unsigned integer
	// >>   right shift            integer >> unsigned integer

	// http://golang.org/ref/spec#Logical_operators
	// Logical operators apply to boolean values and yield a result of the same type as the operands. The right operand is evaluated conditionally.

	// &&    conditional AND    p && q  is  "if p then q else false"
	// ||    conditional OR     p || q  is  "if p then true else q"
	switch n.Op {
	case token.ADD: // +
		return createFuncOp("ADD", n.X, n.Y)
	case token.SUB: // -
		return createFuncOp("SUB", n.X, n.Y)
	case token.MUL: // *
		return createFuncOp("MUL", n.X, n.Y)
	case token.QUO: // /
		return createFuncOp("QUO", n.X, n.Y)
	case token.REM: // %
		return createFuncOp("REM", n.X, n.Y)
	case token.AND: // &
		return createFuncOp("AND", n.X, n.Y)
	case token.OR: // |
		return createFuncOp("OR", n.X, n.Y)
	case token.XOR: // ^
		return createFuncOp("XOR", n.X, n.Y)
	case token.AND_NOT: // &^
		return createFuncOp("ANDNOT", n.X, n.Y)
	case token.SHL: // <<
		return createFuncOp("SHL", n.X, n.Y)
	case token.SHR: // >>
		return createFuncOp("SHR", n.X, n.Y)
	case token.LAND: // &&
		return createFuncOp("LAND", n.X, n.Y)
	case token.LOR: // ||
		return createFuncOp("LOR", n.X, n.Y)
	}

	return n
}

// f(a, b) -> translatedassert.FRVInterface(translatedassert.MFCall(filename, line, pos, f, translatedassert.RVOf(a), translatedassert.RVOf(b)))
func createMemorizedFuncCall(filename string, line int, n *ast.CallExpr, returnType string) *ast.CallExpr {
	c := &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "MFCall"},
		},
		Args: []ast.Expr{
			&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(filename)},
			&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(line)},
			&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(n.Pos()))},
			createReflectValueOfExpr(n.Fun),
		},
	}

	args := []ast.Expr{}
	for _, a := range n.Args {
		args = append(args, createReflectValueOfExpr(a))
	}
	c.Args = append(c.Args, args...)

	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "FRV" + returnType},
		},
		Args: []ast.Expr{c},
	}
}

// createReflectTypeExprFromTypeExpr create ast of reflect.Type from ast of type.
func createReflectTypeExprFromTypeExpr(t ast.Expr) ast.Expr {
	canUseCompositeLit := true

	if t, ok := t.(*ast.Ident); ok {
		switch t.Name {
		case "string", "rune",
			"uint", "uint8", "uint16", "uint32", "uint64",
			"int8", "int32", "int64", "int",
			"float32", "float64",
			"complex64", "complex128",
			"bool", "uintptr", "error":

			canUseCompositeLit = false
		}
	}

	if _, ok := t.(*ast.ChanType); ok {
		canUseCompositeLit = false
	}

	if !canUseCompositeLit {
		rv := &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X: createReflectValueOfExpr(&ast.CallExpr{
					Fun:  &ast.Ident{Name: "new"},
					Args: []ast.Expr{t},
				}),
				Sel: &ast.Ident{Name: "Elem"},
			},
		}

		return &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X:   rv,
				Sel: &ast.Ident{Name: "Type"},
			},
		}
	}

	return createReflectTypeOfExpr(&ast.CompositeLit{Type: t})
}

func createReflectInterfaceExpr(rv ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVInterface"},
		},
		Args: []ast.Expr{rv},
	}
}

func createReflectBoolExpr(rv ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVBool"},
		},
		Args: []ast.Expr{rv},
	}
}

func createReflectValueOfExpr(v ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RVOf"},
		},
		Args: []ast.Expr{v},
	}
}

func createReflectTypeOfExpr(v ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   translatedassertImportIdent,
			Sel: &ast.Ident{Name: "RTOf"},
		},
		Args: []ast.Expr{v},
	}
}

func createPosValuePairExpr(ps []printExpr) []ast.Expr {
	args := []ast.Expr{}

	for _, n := range ps {
		a := &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				X:   translatedassertImportIdent,
				Sel: &ast.Ident{Name: "NewPosValuePair"},
			},
			Args: []ast.Expr{
				&ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(n.Pos))},
				n.Expr,
			},
		}

		args = append(args, a)
	}

	return args
}

func createRawStringLit(s string) *ast.BasicLit {
	return &ast.BasicLit{Kind: token.STRING, Value: "`" + s + "`"}
}
<|endoftext|>


<commit_before>package edit

import (
	"errors"
	"os"

	"github.com/elves/elvish/eval"
)

// CompleterTable provides $le:completer. It implements eval.IndexSetter.
type CompleterTable map[string]ArgCompleter

var _ eval.IndexSetter = CompleterTable(nil)

var (
	ErrCompleterIndexMustBeString = errors.New("index of completer table must be string")
	ErrCompleterValueMustBeFunc   = errors.New("value of completer table must be function")
)

func (CompleterTable) Kind() string {
	return "map"
}

func (ct CompleterTable) Repr(indent int) string {
	return "<repr not implemented yet>"
}

func (ct CompleterTable) IndexOne(idx eval.Value) eval.Value {
	return eval.String("<get not implemented yet>")
}

func (ct CompleterTable) IndexSet(idx eval.Value, v eval.Value) {
	head, ok := idx.(eval.String)
	if !ok {
		throw(ErrCompleterIndexMustBeString)
	}
	value, ok := v.(eval.Fn)
	if !ok {
		throw(ErrCompleterValueMustBeFunc)
	}
	ct[string(head)] = FnAsArgCompleter{value}
}

// ArgCompleter is an argument completer. Its Complete method is called with all
// words of the form. There are at least two words: the first one being the form
// head and the last word being the current argument to complete. It should
// return a list of candidates for the current argument and errors.
type ArgCompleter interface {
	Complete([]string, *Editor) ([]*candidate, error)
}

type FuncArgCompleter struct {
	impl func([]string, *Editor) ([]*candidate, error)
}

func (fac FuncArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
	return fac.impl(words, ed)
}

var DefaultArgCompleter = ""
var argCompleter map[string]ArgCompleter

func init() {
	argCompleter = map[string]ArgCompleter{
		DefaultArgCompleter: FuncArgCompleter{complFilename},
		"sudo":              FuncArgCompleter{complSudo},
	}
}

func completeArg(words []string, ed *Editor) ([]*candidate, error) {
	Logger.Printf("completing argument: %q", words)
	compl, ok := argCompleter[words[0]]
	if !ok {
		compl = argCompleter[DefaultArgCompleter]
	}
	return compl.Complete(words, ed)
}

func complFilename(words []string, ed *Editor) ([]*candidate, error) {
	return complFilenameInner(words[len(words)-1], false)
}

func complSudo(words []string, ed *Editor) ([]*candidate, error) {
	if len(words) == 2 {
		return complFormHeadInner(words[1], ed)
	}
	return completeArg(words[1:], ed)
}

type FnAsArgCompleter struct {
	Fn eval.Fn
}

func (fac FnAsArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
	in, err := makeClosedStdin()
	if err != nil {
		return nil, err
	}
	ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}}

	wordValues := make([]eval.Value, len(words))
	for i, word := range words {
		wordValues[i] = eval.String(word)
	}

	// XXX There is no source to pass to NewTopEvalCtx.
	ec := eval.NewTopEvalCtx(ed.evaler, "[editor completer]", "", ports)
	values, err := ec.PCaptureOutput(fac.Fn, wordValues)
	if err != nil {
		ed.notify("completer error: %v", err)
		return nil, err
	}

	cands := make([]*candidate, len(values))
	for i, v := range values {
		s := eval.ToString(v)
		cands[i] = &candidate{text: s}
	}
	return cands, nil
}
<commit_msg>Support indexing $le:completer for getting.<commit_after>package edit

import (
	"errors"
	"os"

	"github.com/elves/elvish/eval"
)

// CompleterTable provides $le:completer. It implements eval.IndexSetter.
type CompleterTable map[string]ArgCompleter

var _ eval.IndexSetter = CompleterTable(nil)

var (
	ErrCompleterIndexMustBeString = errors.New("index of completer table must be string")
	ErrCompleterValueMustBeFunc   = errors.New("value of completer table must be function")
)

func (CompleterTable) Kind() string {
	return "map"
}

func (ct CompleterTable) Repr(indent int) string {
	return "<repr not implemented yet>"
}

func (ct CompleterTable) IndexOne(idx eval.Value) eval.Value {
	head, ok := idx.(eval.String)
	if !ok {
		throw(ErrCompleterIndexMustBeString)
	}
	v := ct[string(head)]
	if fac, ok := v.(FnAsArgCompleter); ok {
		return fac.Fn
	}
	return eval.String("<get not implemented yet>")
}

func (ct CompleterTable) IndexSet(idx eval.Value, v eval.Value) {
	head, ok := idx.(eval.String)
	if !ok {
		throw(ErrCompleterIndexMustBeString)
	}
	value, ok := v.(eval.FnValue)
	if !ok {
		throw(ErrCompleterValueMustBeFunc)
	}
	ct[string(head)] = FnAsArgCompleter{value}
}

// ArgCompleter is an argument completer. Its Complete method is called with all
// words of the form. There are at least two words: the first one being the form
// head and the last word being the current argument to complete. It should
// return a list of candidates for the current argument and errors.
type ArgCompleter interface {
	Complete([]string, *Editor) ([]*candidate, error)
}

type FuncArgCompleter struct {
	impl func([]string, *Editor) ([]*candidate, error)
}

func (fac FuncArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
	return fac.impl(words, ed)
}

var DefaultArgCompleter = ""
var argCompleter map[string]ArgCompleter

func init() {
	argCompleter = map[string]ArgCompleter{
		DefaultArgCompleter: FuncArgCompleter{complFilename},
		"sudo":              FuncArgCompleter{complSudo},
	}
}

func completeArg(words []string, ed *Editor) ([]*candidate, error) {
	Logger.Printf("completing argument: %q", words)
	compl, ok := argCompleter[words[0]]
	if !ok {
		compl = argCompleter[DefaultArgCompleter]
	}
	return compl.Complete(words, ed)
}

func complFilename(words []string, ed *Editor) ([]*candidate, error) {
	return complFilenameInner(words[len(words)-1], false)
}

func complSudo(words []string, ed *Editor) ([]*candidate, error) {
	if len(words) == 2 {
		return complFormHeadInner(words[1], ed)
	}
	return completeArg(words[1:], ed)
}

type FnAsArgCompleter struct {
	Fn eval.FnValue
}

func (fac FnAsArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
	in, err := makeClosedStdin()
	if err != nil {
		return nil, err
	}
	ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}}

	wordValues := make([]eval.Value, len(words))
	for i, word := range words {
		wordValues[i] = eval.String(word)
	}

	// XXX There is no source to pass to NewTopEvalCtx.
	ec := eval.NewTopEvalCtx(ed.evaler, "[editor completer]", "", ports)
	values, err := ec.PCaptureOutput(fac.Fn, wordValues)
	if err != nil {
		ed.notify("completer error: %v", err)
		return nil, err
	}

	cands := make([]*candidate, len(values))
	for i, v := range values {
		s := eval.ToString(v)
		cands[i] = &candidate{text: s}
	}
	return cands, nil
}
<|endoftext|>