<commit_before>package builder

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/MakeNowJust/heredoc"
	"github.com/digitalocean/doctl/commands/charm"
	"github.com/digitalocean/doctl/commands/charm/template"
	"github.com/digitalocean/godo"
	dockertypes "github.com/docker/docker/api/types"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/pkg/archive"
	"github.com/kballard/go-shellquote"
)

const (
	// CNBBuilderImage represents the local cnb builder.
	CNBBuilderImage = "digitaloceanapps/cnb-local-builder:v0.50.2"

	appVarAllowListKey = "APP_VARS"
	appVarPrefix       = "APP_VAR_"
	cnbCacheDir        = "/cnb/cache"
)

var dockerSocketPath = "/var/run/docker.sock"

// CNBComponentBuilder represents a CNB builder.
type CNBComponentBuilder struct {
	baseComponentBuilder
	versioning           CNBVersioning
	localCacheDir        string
	buildContainer       containertypes.ContainerCreateCreatedBody
	builderImageOverride string
	cnbComponent         godo.AppCNBBuildableComponentSpec
}

// CNBVersioning contains CNB versioning config.
type CNBVersioning struct {
	Buildpacks []*Buildpack
}

// Buildpack represents a CNB buildpack.
type Buildpack struct {
	ID      string `json:"id,omitempty"`
	Version string `json:"version,omitempty"`
}

// Build attempts to build the requested component using the CNB Builder and tags the resulting container images.
func (b *CNBComponentBuilder) Build(ctx context.Context) (res ComponentBuilderResult, err error) {
	if b.component == nil {
		return res, errors.New("no component was provided for the build")
	}

	env, err := b.cnbEnv(ctx)
	if err != nil {
		return res, fmt.Errorf("configuring environment variables: %w", err)
	}

	var sourceDockerSock string
	switch runtime.GOOS {
	case "darwin", "windows":
		// mac/windows docker-for-desktop includes the raw socket in the VM
		sourceDockerSock = "/var/run/docker.sock.raw"
	default:
		sourceDockerSock, err = filepath.EvalSymlinks(dockerSocketPath)
		if err != nil {
			return res, fmt.Errorf("finding docker engine socket: %w", err)
		}
	}

	mounts := []mount.Mount{{
		Type:   mount.TypeBind,
		Source: sourceDockerSock,
		Target: dockerSocketPath,
	}}
	if !b.copyOnWriteSemantics {
		mounts = append(mounts, mount.Mount{
			Type:   mount.TypeBind,
			Source: b.contextDir,
			Target: "/workspace",
		})
	}

	if b.localCacheDir != "" {
		mounts = append(mounts, mount.Mount{
			Type:   mount.TypeBind,
			Source: b.localCacheDir,
			Target: cnbCacheDir,
		})
	}

	b.buildContainer, err = b.cli.ContainerCreate(ctx, &containertypes.Config{
		Image:        b.builderImage(),
		Entrypoint:   []string{"sh", "-c", "sleep infinity"},
		AttachStdout: true,
		AttachStderr: true,
	}, &containertypes.HostConfig{
		Mounts: mounts,
	}, nil, nil, "")
	if err != nil {
		return res, fmt.Errorf("creating build container: %w", err)
	}

	start := time.Now()
	defer func() {
		res.BuildDuration = time.Since(start)
		// we use context.Background() so we can remove the container if the original context is cancelled.
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		_ = b.cli.ContainerRemove(ctx, b.buildContainer.ID, dockertypes.ContainerRemoveOptions{
			Force: true,
		})
		b.buildContainer = containertypes.ContainerCreateCreatedBody{}
	}()

	if err := b.cli.ContainerStart(ctx, b.buildContainer.ID, dockertypes.ContainerStartOptions{}); err != nil {
		return res, fmt.Errorf("starting build container: %w", err)
	}

	lw := b.getLogWriter()
	if b.copyOnWriteSemantics {
		template.Render(lw, "{{success checkmark}} mounting app workspace{{nl}}", nil)
		// Prepare source copy info.
		srcInfo, err := archive.CopyInfoSourcePath(b.contextDir, true)
		if err != nil {
			return res, fmt.Errorf("preparing app workspace: %w", err)
		}
		srcArchive, err := archive.TarResource(srcInfo)
		if err != nil {
			return res, fmt.Errorf("preparing app workspace: %w", err)
		}
		defer srcArchive.Close()
		dstInfo := archive.CopyInfo{
			Path:  "/workspace",
			IsDir: true,
		}
		archDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
		if err != nil {
			return res, fmt.Errorf("archiving app workspace: %w", err)
		}
		defer preparedArchive.Close()
		err = b.cli.CopyToContainer(ctx, b.buildContainer.ID, archDir, preparedArchive, dockertypes.CopyToContainerOptions{
			AllowOverwriteDirWithFile: false,
			CopyUIDGID:                false,
		})
		if err != nil {
			return res, fmt.Errorf("copying app workspace to build container: %w", err)
		}
	}

	template.Render(lw, "{{success checkmark}} building{{nl 2}}", nil)
	err = b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", "/.app_platform/build.sh"},
		env,
		b.getLogWriter(),
		nil,
	)
	if err != nil {
		return res, err
	}
	res.Image = b.AppImageOutputName()

	if b.component.GetType() == godo.AppComponentTypeStaticSite {
		err = b.buildStaticSiteImage(ctx)
		if err != nil {
			return res, err
		}
		res.Image = b.StaticSiteImageOutputName()
	}

	return res, nil
}

func (b *CNBComponentBuilder) readFileFromContainer(ctx context.Context, path string) (string, error) {
	var buf bytes.Buffer
	err := b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"cat", path},
		nil,
		&buf,
		nil,
	)
	if err != nil {
		return buf.String(), err
	}
	return buf.String(), nil
}

func (b *CNBComponentBuilder) writeFileToContainer(ctx context.Context, path string, content []byte) error {
	return b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", "cat >" + shellquote.Join(path)},
		nil,
		nil,
		bytes.NewReader(content),
	)
}

func (b *CNBComponentBuilder) buildStaticSiteImage(ctx context.Context) error {
	lw := b.getLogWriter()

	workspacePath, err := b.readFileFromContainer(ctx, "/.app_platform/local/WORKSPACE_PATH")
	if err != nil {
		return err
	}

	assetsPath, err := b.readFileFromContainer(ctx, "/.app_platform/local/ASSETS_PATH")
	if err != nil {
		return err
	}
	assetsPath, err = filepath.Rel(workspacePath, assetsPath)
	if assetsPath == "." {
		assetsPath = "./"
	} else {
		assetsPath = "./" + assetsPath + "/"
	}

	template.Render(lw, `{{success checkmark}} building static site image{{nl 2}}`, nil)

	err = b.writeFileToContainer(ctx, workspacePath+"/nginx.conf", []byte(b.getStaticNginxConfig()))
	if err != nil {
		return fmt.Errorf("writing nginx config: %w", err)
	}

	dockerfile, buildArgs, err := b.staticSiteDockerfile(assetsPath)
	if err != nil {
		return err
	}
	err = b.writeFileToContainer(ctx, workspacePath+"/Dockerfile.static", dockerfile)
	if err != nil {
		return fmt.Errorf("writing static site config: %w", err)
	}

	// build the static site docker image within the build container
	dockerBuildCmd := []string{
		"docker", "build",
		"-t", b.StaticSiteImageOutputName(),
		"-f", workspacePath + "/Dockerfile.static",
	}
	dockerBuildCmd = append(dockerBuildCmd, buildArgsToCmd(buildArgs)...)
	dockerBuildCmd = append(dockerBuildCmd, workspacePath)

	err = b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", shellquote.Join(dockerBuildCmd...)},
		nil,
		lw,
		nil,
	)
	if err != nil {
		return err
	}

	return nil
}

func (b *CNBComponentBuilder) staticSiteDockerfile(assetsPath string) (dockerfile []byte, buildArgs map[string]*string, err error) {
	dockerfile = []byte(`
ARG nginx_image
ARG assets_path
FROM ${nginx_image}

COPY ${assets_path} /www
RUN test -f /www/nginx.conf && rm -f /www/nginx.conf

COPY ./nginx.conf /etc/nginx/conf.d/default.conf
`)

	buildArgs = map[string]*string{
		"nginx_image": strPtr(StaticSiteNginxImage),
		"assets_path": &assetsPath,
	}
	return
}

func (b *CNBComponentBuilder) cnbEnv(ctx context.Context) ([]string, error) {
	envs := []string{}
	appVars := []string{}

	envMap, err := b.getEnvMap()
	if err != nil {
		return nil, err
	}
	for k, v := range envMap {
		envs = append(envs, appVarPrefix+k+"="+v)
		appVars = append(appVars, k)
	}
	if len(appVars) > 0 {
		sort.Strings(appVars)
		envs = append(envs, appVarAllowListKey+"="+strings.Join(appVars, ","))
	}

	envs = append(envs, "CNB_UPLOAD_RETRY=1")
	envs = append(envs, "APP_IMAGE_URL="+b.AppImageOutputName())
	envs = append(envs, "APP_PLATFORM_COMPONENT_TYPE="+string(b.component.GetType()))
	if b.component.GetSourceDir() != "" {
		envs = append(envs, "SOURCE_DIR="+b.component.GetSourceDir())
	}

	if b.buildCommandOverride != "" {
		template.Render(b.getLogWriter(), heredoc.Doc(`
			{{success checkmark}} overriding default build command with custom command:
			{{highlight .}}
		`,
		), charm.IndentString(4, b.buildCommandOverride))
		envs = append(envs, "BUILD_COMMAND="+b.buildCommandOverride)
	} else if b.cnbComponent.GetBuildCommand() != "" {
		envs = append(envs, "BUILD_COMMAND="+b.cnbComponent.GetBuildCommand())
	}

	if len(b.versioning.Buildpacks) > 0 {
		versioningJSON, err := json.Marshal(b.versioning.Buildpacks)
		if err != nil {
			return nil, fmt.Errorf("computing buildpack versioning: %w", err)
		}
		envs = append(envs, "VERSION_PINNING_LIST="+string(versioningJSON))
	}

	if exists, err := ImageExists(ctx, b.cli, b.AppImageOutputName()); err != nil {
		return nil, err
	} else if exists {
		envs = append(envs, "PREVIOUS_APP_IMAGE_URL="+b.AppImageOutputName())
	}

	if b.localCacheDir != "" {
		envs = append(envs, "APP_CACHE_DIR="+cnbCacheDir)
	}

	sort.Strings(envs)

	return envs, nil
}

func buildArgsToCmd(buildArgs map[string]*string) []string {
	var (
		cmd  []string
		keys = make([]string, 0, len(buildArgs))
	)
	for k, v := range buildArgs {
		if v != nil {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	for _, k := range keys {
		cmd = append(cmd, "--build-arg", k+"="+(*buildArgs[k]))
	}
	return cmd
}

func (b *CNBComponentBuilder) builderImage() string {
	if b.builderImageOverride != "" {
		return b.builderImageOverride
	}

	return CNBBuilderImage
}
<commit_msg>clearer copying workspace to container message (#1249)<commit_after>package builder

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/MakeNowJust/heredoc"
	"github.com/digitalocean/doctl/commands/charm"
	"github.com/digitalocean/doctl/commands/charm/template"
	"github.com/digitalocean/godo"
	dockertypes "github.com/docker/docker/api/types"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/pkg/archive"
	"github.com/kballard/go-shellquote"
)

const (
	// CNBBuilderImage represents the local cnb builder.
	CNBBuilderImage = "digitaloceanapps/cnb-local-builder:v0.50.2"

	appVarAllowListKey = "APP_VARS"
	appVarPrefix       = "APP_VAR_"
	cnbCacheDir        = "/cnb/cache"
)

var dockerSocketPath = "/var/run/docker.sock"

// CNBComponentBuilder represents a CNB builder.
type CNBComponentBuilder struct {
	baseComponentBuilder
	versioning           CNBVersioning
	localCacheDir        string
	buildContainer       containertypes.ContainerCreateCreatedBody
	builderImageOverride string
	cnbComponent         godo.AppCNBBuildableComponentSpec
}

// CNBVersioning contains CNB versioning config.
type CNBVersioning struct {
	Buildpacks []*Buildpack
}

// Buildpack represents a CNB buildpack.
type Buildpack struct {
	ID      string `json:"id,omitempty"`
	Version string `json:"version,omitempty"`
}

// Build attempts to build the requested component using the CNB Builder and tags the resulting container images.
func (b *CNBComponentBuilder) Build(ctx context.Context) (res ComponentBuilderResult, err error) {
	if b.component == nil {
		return res, errors.New("no component was provided for the build")
	}

	env, err := b.cnbEnv(ctx)
	if err != nil {
		return res, fmt.Errorf("configuring environment variables: %w", err)
	}

	var sourceDockerSock string
	switch runtime.GOOS {
	case "darwin", "windows":
		// mac/windows docker-for-desktop includes the raw socket in the VM
		sourceDockerSock = "/var/run/docker.sock.raw"
	default:
		sourceDockerSock, err = filepath.EvalSymlinks(dockerSocketPath)
		if err != nil {
			return res, fmt.Errorf("finding docker engine socket: %w", err)
		}
	}

	mounts := []mount.Mount{{
		Type:   mount.TypeBind,
		Source: sourceDockerSock,
		Target: dockerSocketPath,
	}}
	if !b.copyOnWriteSemantics {
		mounts = append(mounts, mount.Mount{
			Type:   mount.TypeBind,
			Source: b.contextDir,
			Target: "/workspace",
		})
	}

	if b.localCacheDir != "" {
		mounts = append(mounts, mount.Mount{
			Type:   mount.TypeBind,
			Source: b.localCacheDir,
			Target: cnbCacheDir,
		})
	}

	b.buildContainer, err = b.cli.ContainerCreate(ctx, &containertypes.Config{
		Image:        b.builderImage(),
		Entrypoint:   []string{"sh", "-c", "sleep infinity"},
		AttachStdout: true,
		AttachStderr: true,
	}, &containertypes.HostConfig{
		Mounts: mounts,
	}, nil, nil, "")
	if err != nil {
		return res, fmt.Errorf("creating build container: %w", err)
	}

	start := time.Now()
	defer func() {
		res.BuildDuration = time.Since(start)
		// we use context.Background() so we can remove the container if the original context is cancelled.
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		_ = b.cli.ContainerRemove(ctx, b.buildContainer.ID, dockertypes.ContainerRemoveOptions{
			Force: true,
		})
		b.buildContainer = containertypes.ContainerCreateCreatedBody{}
	}()

	if err := b.cli.ContainerStart(ctx, b.buildContainer.ID, dockertypes.ContainerStartOptions{}); err != nil {
		return res, fmt.Errorf("starting build container: %w", err)
	}

	lw := b.getLogWriter()
	if !b.copyOnWriteSemantics {
		template.Render(lw, "{{success checkmark}} mounting app workspace{{nl}}", nil)
	} else {
		template.Render(lw, heredoc.Doc(`
			{{success checkmark}} copying local app workspace to build container
			  {{muted (print "    local: " .)}}
			  {{muted "container: /workspace"}}
		`), b.contextDir)
		// Prepare source copy info.
		srcInfo, err := archive.CopyInfoSourcePath(b.contextDir, true)
		if err != nil {
			return res, fmt.Errorf("preparing app workspace: %w", err)
		}
		srcArchive, err := archive.TarResource(srcInfo)
		if err != nil {
			return res, fmt.Errorf("preparing app workspace: %w", err)
		}
		defer srcArchive.Close()
		dstInfo := archive.CopyInfo{
			Path:  "/workspace",
			IsDir: true,
		}
		archDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
		if err != nil {
			return res, fmt.Errorf("archiving app workspace: %w", err)
		}
		defer preparedArchive.Close()
		err = b.cli.CopyToContainer(ctx, b.buildContainer.ID, archDir, preparedArchive, dockertypes.CopyToContainerOptions{
			AllowOverwriteDirWithFile: false,
			CopyUIDGID:                false,
		})
		if err != nil {
			return res, fmt.Errorf("copying app workspace to build container: %w", err)
		}
	}

	template.Render(lw, "{{success checkmark}} building{{nl 2}}", nil)
	err = b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", "/.app_platform/build.sh"},
		env,
		b.getLogWriter(),
		nil,
	)
	if err != nil {
		return res, err
	}
	res.Image = b.AppImageOutputName()

	if b.component.GetType() == godo.AppComponentTypeStaticSite {
		err = b.buildStaticSiteImage(ctx)
		if err != nil {
			return res, err
		}
		res.Image = b.StaticSiteImageOutputName()
	}

	return res, nil
}

func (b *CNBComponentBuilder) readFileFromContainer(ctx context.Context, path string) (string, error) {
	var buf bytes.Buffer
	err := b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"cat", path},
		nil,
		&buf,
		nil,
	)
	if err != nil {
		return buf.String(), err
	}
	return buf.String(), nil
}

func (b *CNBComponentBuilder) writeFileToContainer(ctx context.Context, path string, content []byte) error {
	return b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", "cat >" + shellquote.Join(path)},
		nil,
		nil,
		bytes.NewReader(content),
	)
}

func (b *CNBComponentBuilder) buildStaticSiteImage(ctx context.Context) error {
	lw := b.getLogWriter()

	workspacePath, err := b.readFileFromContainer(ctx, "/.app_platform/local/WORKSPACE_PATH")
	if err != nil {
		return err
	}

	assetsPath, err := b.readFileFromContainer(ctx, "/.app_platform/local/ASSETS_PATH")
	if err != nil {
		return err
	}
	assetsPath, err = filepath.Rel(workspacePath, assetsPath)
	if assetsPath == "." {
		assetsPath = "./"
	} else {
		assetsPath = "./" + assetsPath + "/"
	}

	template.Render(lw, `{{success checkmark}} building static site image{{nl 2}}`, nil)

	err = b.writeFileToContainer(ctx, workspacePath+"/nginx.conf", []byte(b.getStaticNginxConfig()))
	if err != nil {
		return fmt.Errorf("writing nginx config: %w", err)
	}

	dockerfile, buildArgs, err := b.staticSiteDockerfile(assetsPath)
	if err != nil {
		return err
	}
	err = b.writeFileToContainer(ctx, workspacePath+"/Dockerfile.static", dockerfile)
	if err != nil {
		return fmt.Errorf("writing static site config: %w", err)
	}

	// build the static site docker image within the build container
	dockerBuildCmd := []string{
		"docker", "build",
		"-t", b.StaticSiteImageOutputName(),
		"-f", workspacePath + "/Dockerfile.static",
	}
	dockerBuildCmd = append(dockerBuildCmd, buildArgsToCmd(buildArgs)...)
	dockerBuildCmd = append(dockerBuildCmd, workspacePath)

	err = b.runExec(
		ctx,
		b.buildContainer.ID,
		[]string{"sh", "-c", shellquote.Join(dockerBuildCmd...)},
		nil,
		lw,
		nil,
	)
	if err != nil {
		return err
	}

	return nil
}

func (b *CNBComponentBuilder) staticSiteDockerfile(assetsPath string) (dockerfile []byte, buildArgs map[string]*string, err error) {
	dockerfile = []byte(`
ARG nginx_image
ARG assets_path
FROM ${nginx_image}

COPY ${assets_path} /www
RUN test -f /www/nginx.conf && rm -f /www/nginx.conf

COPY ./nginx.conf /etc/nginx/conf.d/default.conf
`)

	buildArgs = map[string]*string{
		"nginx_image": strPtr(StaticSiteNginxImage),
		"assets_path": &assetsPath,
	}
	return
}

func (b *CNBComponentBuilder) cnbEnv(ctx context.Context) ([]string, error) {
	envs := []string{}
	appVars := []string{}

	envMap, err := b.getEnvMap()
	if err != nil {
		return nil, err
	}
	for k, v := range envMap {
		envs = append(envs, appVarPrefix+k+"="+v)
		appVars = append(appVars, k)
	}
	if len(appVars) > 0 {
		sort.Strings(appVars)
		envs = append(envs, appVarAllowListKey+"="+strings.Join(appVars, ","))
	}

	envs = append(envs, "CNB_UPLOAD_RETRY=1")
	envs = append(envs, "APP_IMAGE_URL="+b.AppImageOutputName())
	envs = append(envs, "APP_PLATFORM_COMPONENT_TYPE="+string(b.component.GetType()))
	if b.component.GetSourceDir() != "" {
		envs = append(envs, "SOURCE_DIR="+b.component.GetSourceDir())
	}

	if b.buildCommandOverride != "" {
		template.Render(b.getLogWriter(), heredoc.Doc(`
			{{success checkmark}} overriding default build command with custom command:
			{{highlight .}}
		`,
		), charm.IndentString(4, b.buildCommandOverride))
		envs = append(envs, "BUILD_COMMAND="+b.buildCommandOverride)
	} else if b.cnbComponent.GetBuildCommand() != "" {
		envs = append(envs, "BUILD_COMMAND="+b.cnbComponent.GetBuildCommand())
	}

	if len(b.versioning.Buildpacks) > 0 {
		versioningJSON, err := json.Marshal(b.versioning.Buildpacks)
		if err != nil {
			return nil, fmt.Errorf("computing buildpack versioning: %w", err)
		}
		envs = append(envs, "VERSION_PINNING_LIST="+string(versioningJSON))
	}

	if exists, err := ImageExists(ctx, b.cli, b.AppImageOutputName()); err != nil {
		return nil, err
	} else if exists {
		envs = append(envs, "PREVIOUS_APP_IMAGE_URL="+b.AppImageOutputName())
	}

	if b.localCacheDir != "" {
		envs = append(envs, "APP_CACHE_DIR="+cnbCacheDir)
	}

	sort.Strings(envs)

	return envs, nil
}

func buildArgsToCmd(buildArgs map[string]*string) []string {
	var (
		cmd  []string
		keys = make([]string, 0, len(buildArgs))
	)
	for k, v := range buildArgs {
		if v != nil {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	for _, k := range keys {
		cmd = append(cmd, "--build-arg", k+"="+(*buildArgs[k]))
	}
	return cmd
}

func (b *CNBComponentBuilder) builderImage() string {
	if b.builderImageOverride != "" {
		return b.builderImageOverride
	}

	return CNBBuilderImage
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Pinterest.com

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 bender

import (
	"math/rand"
	"time"
)

// ExponentialIntervalGenerator creates an IntervalGenerator that outputs
// exponentially distributed intervals. The resulting arrivals constitute
// a Poisson process. The rate parameter is the average queries per second
// for the generator, and corresponds to the reciprocal of the lambda parameter
// to an exponential distribution. In English, if you want to generate 30 QPS
// on average, pass 30 as the value of rate.
func ExponentialIntervalGenerator(rate float64) IntervalGenerator {
	rate = rate / float64(time.Second)
	return func(_ int64) int64 {
		return int64(rand.ExpFloat64() / rate)
	}
}

type Step struct {
	Rate float64
	For  time.Duration
}

func RampedExponentialIntervalGenerator(ramp []Step) IntervalGenerator {
	rate := ramp[0].Rate / float64(time.Second)
	return func(_ int64) int64 {
		return int64(rand.ExpFloat64() / rate)
	}
}

// UniformIntervalGenerator creates and IntervalGenerator that outputs 1/rate every time it is
// called. Boring, right?
func UniformIntervalGenerator(rate float64) IntervalGenerator {
	irate := int64(rate / float64(time.Second))
	return func(_ int64) int64 {
		return irate
	}
}
<commit_msg>bender: provide proper ramped interval generator<commit_after>/*
Copyright 2014 Pinterest.com

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 bender

import (
	"math/rand"
	"time"
)

// ExponentialIntervalGenerator creates an IntervalGenerator that outputs
// exponentially distributed intervals. The resulting arrivals constitute
// a Poisson process. The rate parameter is the average queries per second
// for the generator, and corresponds to the reciprocal of the lambda parameter
// to an exponential distribution. In English, if you want to generate 30 QPS
// on average, pass 30 as the value of rate.
func ExponentialIntervalGenerator(rate float64) IntervalGenerator {
	rate = rate / float64(time.Second)
	return func(_ int64) int64 {
		return int64(rand.ExpFloat64() / rate)
	}
}

// RampedExponentialIntervalGenerator increases the average rate linearely
// from 0 to rate during the given ramp duration and keeps rate afterwards.
func RampedExponentialIntervalGenerator(rate float64, ramp time.Duration) IntervalGenerator {
	rate = rate / float64(time.Second)
	framp := float64(ramp)
	start := time.Now()
	return func(_ int64) int64 {
		elapsed := float64(time.Since(start))
		factor := elapsed / framp
		if factor > 1 {
			factor = 1
		} else if factor < 0.05 {
			// Do not let effective rate drop below 20% of the
			// final targe rate: Otherwise the tests does not
			// realy start as basicaly no request are generated.
			factor = 0.05
		}
		return int64(rand.ExpFloat64() / (factor * rate))
	}
}

// UniformIntervalGenerator creates and IntervalGenerator that outputs 1/rate every time it is
// called. Boring, right?
func UniformIntervalGenerator(rate float64) IntervalGenerator {
	irate := int64(rate / float64(time.Second))
	return func(_ int64) int64 {
		return irate
	}
}
<|endoftext|>


<commit_before>package cryptocore

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"log"

	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// RandBytes gets "n" random bytes from /dev/urandom or panics
func RandBytes(n int) []byte {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		log.Panic("Failed to read random bytes: " + err.Error())
	}
	return b
}

// RandUint64 returns a secure random uint64
func RandUint64() uint64 {
	b := RandBytes(8)
	return binary.BigEndian.Uint64(b)
}

type nonceGenerator struct {
	lastNonce []byte
	nonceLen  int // bytes
}

// Get a random "nonceLen"-byte nonce
func (n *nonceGenerator) Get() []byte {
	nonce := RandBytes(n.nonceLen)
	tlog.Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce))
	if bytes.Equal(nonce, n.lastNonce) {
		m := fmt.Sprintf("Got the same nonce twice: %s. This should never happen!", hex.EncodeToString(nonce))
		log.Panic(m)
	}
	n.lastNonce = nonce
	return nonce
}
<commit_msg>cryptocore: remove lastNonce check<commit_after>package cryptocore

import (
	"crypto/rand"
	"encoding/binary"
	"log"
)

// RandBytes gets "n" random bytes from /dev/urandom or panics
func RandBytes(n int) []byte {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		log.Panic("Failed to read random bytes: " + err.Error())
	}
	return b
}

// RandUint64 returns a secure random uint64
func RandUint64() uint64 {
	b := RandBytes(8)
	return binary.BigEndian.Uint64(b)
}

type nonceGenerator struct {
	nonceLen int // bytes
}

// Get a random "nonceLen"-byte nonce
func (n *nonceGenerator) Get() []byte {
	nonce := RandBytes(n.nonceLen)
	return nonce
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package csicommon

import (
	"context"
	"fmt"
	"runtime/debug"
	"strings"
	"sync/atomic"

	"github.com/ceph/ceph-csi/internal/util"

	"github.com/container-storage-interface/spec/lib/go/csi"
	"github.com/kubernetes-csi/csi-lib-utils/protosanitizer"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"k8s.io/klog/v2"
)

func parseEndpoint(ep string) (string, string, error) {
	if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") {
		s := strings.SplitN(ep, "://", 2)
		if s[1] != "" {
			return s[0], s[1], nil
		}
	}
	return "", "", fmt.Errorf("invalid endpoint: %v", ep)
}

// NewVolumeCapabilityAccessMode returns volume access mode.
func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *csi.VolumeCapability_AccessMode {
	return &csi.VolumeCapability_AccessMode{Mode: mode}
}

// NewDefaultNodeServer initializes default node server.
func NewDefaultNodeServer(d *CSIDriver, t string, topology map[string]string) *DefaultNodeServer {
	d.topology = topology
	return &DefaultNodeServer{
		Driver: d,
		Type:   t,
	}
}

// NewDefaultIdentityServer initializes default identity servier.
func NewDefaultIdentityServer(d *CSIDriver) *DefaultIdentityServer {
	return &DefaultIdentityServer{
		Driver: d,
	}
}

// NewDefaultControllerServer initializes default controller server.
func NewDefaultControllerServer(d *CSIDriver) *DefaultControllerServer {
	return &DefaultControllerServer{
		Driver: d,
	}
}

// NewControllerServiceCapability returns controller capabilities.
func NewControllerServiceCapability(ctrlCap csi.ControllerServiceCapability_RPC_Type) *csi.ControllerServiceCapability {
	return &csi.ControllerServiceCapability{
		Type: &csi.ControllerServiceCapability_Rpc{
			Rpc: &csi.ControllerServiceCapability_RPC{
				Type: ctrlCap,
			},
		},
	}
}

func getReqID(req interface{}) string {
	// if req is nil empty string will be returned
	reqID := ""
	switch r := req.(type) {
	case *csi.CreateVolumeRequest:
		reqID = r.Name

	case *csi.DeleteVolumeRequest:
		reqID = r.VolumeId

	case *csi.CreateSnapshotRequest:
		reqID = r.Name
	case *csi.DeleteSnapshotRequest:
		reqID = r.SnapshotId

	case *csi.ControllerExpandVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodeStageVolumeRequest:
		reqID = r.VolumeId
	case *csi.NodeUnstageVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodePublishVolumeRequest:
		reqID = r.VolumeId
	case *csi.NodeUnpublishVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodeExpandVolumeRequest:
		reqID = r.VolumeId
	}
	return reqID
}

var id uint64

func contextIDInjector(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	atomic.AddUint64(&id, 1)
	ctx = context.WithValue(ctx, util.CtxKey, id)
	reqID := getReqID(req)
	if reqID != "" {
		ctx = context.WithValue(ctx, util.ReqID, reqID)
	}
	return handler(ctx, req)
}

func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	util.ExtendedLog(ctx, "GRPC call: %s", info.FullMethod)
	util.TraceLog(ctx, "GRPC request: %s", protosanitizer.StripSecrets(req))
	resp, err := handler(ctx, req)
	if err != nil {
		klog.Errorf(util.Log(ctx, "GRPC error: %v"), err)
	} else {
		util.TraceLog(ctx, "GRPC response: %s", protosanitizer.StripSecrets(resp))
	}
	return resp, err
}

func panicHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			klog.Errorf("panic occurred: %v", r)
			debug.PrintStack()
			err = status.Errorf(codes.Internal, "panic %v", r)
		}
	}()
	return handler(ctx, req)
}
<commit_msg>util: avoid secret logging in GRPC Replication Request<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package csicommon

import (
	"context"
	"fmt"
	"runtime/debug"
	"strings"
	"sync/atomic"

	"github.com/ceph/ceph-csi/internal/util"

	"github.com/container-storage-interface/spec/lib/go/csi"
	rp "github.com/kube-storage/replication-lib-utils/protosanitizer"
	"github.com/kube-storage/spec/lib/go/replication"
	"github.com/kubernetes-csi/csi-lib-utils/protosanitizer"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"k8s.io/klog/v2"
)

func parseEndpoint(ep string) (string, string, error) {
	if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") {
		s := strings.SplitN(ep, "://", 2)
		if s[1] != "" {
			return s[0], s[1], nil
		}
	}
	return "", "", fmt.Errorf("invalid endpoint: %v", ep)
}

// NewVolumeCapabilityAccessMode returns volume access mode.
func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *csi.VolumeCapability_AccessMode {
	return &csi.VolumeCapability_AccessMode{Mode: mode}
}

// NewDefaultNodeServer initializes default node server.
func NewDefaultNodeServer(d *CSIDriver, t string, topology map[string]string) *DefaultNodeServer {
	d.topology = topology
	return &DefaultNodeServer{
		Driver: d,
		Type:   t,
	}
}

// NewDefaultIdentityServer initializes default identity servier.
func NewDefaultIdentityServer(d *CSIDriver) *DefaultIdentityServer {
	return &DefaultIdentityServer{
		Driver: d,
	}
}

// NewDefaultControllerServer initializes default controller server.
func NewDefaultControllerServer(d *CSIDriver) *DefaultControllerServer {
	return &DefaultControllerServer{
		Driver: d,
	}
}

// NewControllerServiceCapability returns controller capabilities.
func NewControllerServiceCapability(ctrlCap csi.ControllerServiceCapability_RPC_Type) *csi.ControllerServiceCapability {
	return &csi.ControllerServiceCapability{
		Type: &csi.ControllerServiceCapability_Rpc{
			Rpc: &csi.ControllerServiceCapability_RPC{
				Type: ctrlCap,
			},
		},
	}
}

// Add replication request names to the list when we implement more API's.
func isReplicationRequest(req interface{}) bool {
	isReplicationRequest := true
	switch req.(type) {
	case *replication.EnableVolumeReplicationRequest:
	case *replication.DisableVolumeReplicationRequest:
	case *replication.PromoteVolumeRequest:
	case *replication.DemoteVolumeRequest:
	case *replication.ResyncVolumeRequest:
	default:
		isReplicationRequest = false
	}
	return isReplicationRequest
}

func getReqID(req interface{}) string {
	// if req is nil empty string will be returned
	reqID := ""
	switch r := req.(type) {
	case *csi.CreateVolumeRequest:
		reqID = r.Name

	case *csi.DeleteVolumeRequest:
		reqID = r.VolumeId

	case *csi.CreateSnapshotRequest:
		reqID = r.Name
	case *csi.DeleteSnapshotRequest:
		reqID = r.SnapshotId

	case *csi.ControllerExpandVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodeStageVolumeRequest:
		reqID = r.VolumeId
	case *csi.NodeUnstageVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodePublishVolumeRequest:
		reqID = r.VolumeId
	case *csi.NodeUnpublishVolumeRequest:
		reqID = r.VolumeId

	case *csi.NodeExpandVolumeRequest:
		reqID = r.VolumeId

	case *replication.EnableVolumeReplicationRequest:
		reqID = r.VolumeId
	case *replication.DisableVolumeReplicationRequest:
		reqID = r.VolumeId

	case *replication.PromoteVolumeRequest:
		reqID = r.VolumeId
	case *replication.DemoteVolumeRequest:
		reqID = r.VolumeId

	case *replication.ResyncVolumeRequest:
		reqID = r.VolumeId
	}
	return reqID
}

var id uint64

func contextIDInjector(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	atomic.AddUint64(&id, 1)
	ctx = context.WithValue(ctx, util.CtxKey, id)
	reqID := getReqID(req)
	if reqID != "" {
		ctx = context.WithValue(ctx, util.ReqID, reqID)
	}
	return handler(ctx, req)
}

func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	util.ExtendedLog(ctx, "GRPC call: %s", info.FullMethod)
	if isReplicationRequest(req) {
		util.TraceLog(ctx, "GRPC request: %s", rp.StripReplicationSecrets(req))
	} else {
		util.TraceLog(ctx, "GRPC request: %s", protosanitizer.StripSecrets(req))
	}

	resp, err := handler(ctx, req)
	if err != nil {
		klog.Errorf(util.Log(ctx, "GRPC error: %v"), err)
	} else {
		util.TraceLog(ctx, "GRPC response: %s", protosanitizer.StripSecrets(resp))
	}
	return resp, err
}

func panicHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			klog.Errorf("panic occurred: %v", r)
			debug.PrintStack()
			err = status.Errorf(codes.Internal, "panic %v", r)
		}
	}()
	return handler(ctx, req)
}
<|endoftext|>


<commit_before>// Package scoop provides a Pipe that generates a scoop.sh App Manifest and pushes it to a bucket
package scoop

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/internal/client"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/tmpl"
	"github.com/goreleaser/goreleaser/pkg/context"
)

// ErrNoWindows when there is no build for windows (goos doesn't contain windows)
var ErrNoWindows = errors.New("scoop requires a windows build")

// ErrTokenTypeNotImplementedForScoop indicates that a new token type was not implemented for this pipe
var ErrTokenTypeNotImplementedForScoop = errors.New("token type not implemented for scoop pipe")

// Pipe for build
type Pipe struct{}

func (Pipe) String() string {
	return "scoop manifests"
}

// Publish scoop manifest
func (Pipe) Publish(ctx *context.Context) error {
	// TODO(caarlos0): split the creation of the json file and the publishing in two steps, like the brew pipe.
	client, err := client.New(ctx)
	if err != nil {
		return err
	}
	return doRun(ctx, client)
}

// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
	if ctx.Config.Scoop.Name == "" {
		ctx.Config.Scoop.Name = ctx.Config.ProjectName
	}
	if ctx.Config.Scoop.CommitAuthor.Name == "" {
		ctx.Config.Scoop.CommitAuthor.Name = "goreleaserbot"
	}
	if ctx.Config.Scoop.CommitAuthor.Email == "" {
		ctx.Config.Scoop.CommitAuthor.Email = "goreleaser@carlosbecker.com"
	}

	return nil
}

func doRun(ctx *context.Context, client client.Client) error {
	if ctx.Config.Scoop.Bucket.Name == "" {
		return pipe.Skip("scoop section is not configured")
	}

	// TODO mavogel: in another PR
	// check if release pipe is not configured!
	// if ctx.Config.Release.Disable {
	// }

	// TODO: multiple archives
	if ctx.Config.Archives[0].Format == "binary" {
		return pipe.Skip("archive format is binary")
	}

	var archives = ctx.Artifacts.Filter(
		artifact.And(
			artifact.ByGoos("windows"),
			artifact.ByType(artifact.UploadableArchive),
		),
	).List()
	if len(archives) == 0 {
		return ErrNoWindows
	}

	var path = ctx.Config.Scoop.Name + ".json"

	content, err := buildManifest(ctx, archives)
	if err != nil {
		return err
	}

	if ctx.SkipPublish {
		return pipe.ErrSkipPublishEnabled
	}
	if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "true" {
		return pipe.Skip("scoop.skip_upload is true")
	}
	if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" {
		return pipe.Skip("release is prerelease")
	}
	if ctx.Config.Release.Draft {
		return pipe.Skip("release is marked as draft")
	}
	if ctx.Config.Release.Disable {
		return pipe.Skip("release is disabled")
	}
	return client.CreateFile(
		ctx,
		ctx.Config.Scoop.CommitAuthor,
		ctx.Config.Scoop.Bucket,
		content.Bytes(),
		path,
		fmt.Sprintf("Scoop update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag),
	)
}

// Manifest represents a scoop.sh App Manifest, more info:
// https://github.com/lukesampson/scoop/wiki/App-Manifests
type Manifest struct {
	Version      string              `json:"version"`               // The version of the app that this manifest installs.
	Architecture map[string]Resource `json:"architecture"`          // `architecture`: If the app has 32- and 64-bit versions, architecture can be used to wrap the differences.
	Homepage     string              `json:"homepage,omitempty"`    // `homepage`: The home page for the program.
	License      string              `json:"license,omitempty"`     // `license`: The software license for the program. For well-known licenses, this will be a string like "MIT" or "GPL2". For custom licenses, this should be the URL of the license.
	Description  string              `json:"description,omitempty"` // Description of the app
	Persist      []string            `json:"persist,omitempty"`     // Persist data between updates
}

// Resource represents a combination of a url and a binary name for an architecture
type Resource struct {
	URL  string   `json:"url"`  // URL to the archive
	Bin  []string `json:"bin"`  // name of binary inside the archive
	Hash string   `json:"hash"` // the archive checksum
}

func buildManifest(ctx *context.Context, artifacts []*artifact.Artifact) (bytes.Buffer, error) {
	var result bytes.Buffer
	var manifest = Manifest{
		Version:      ctx.Version,
		Architecture: map[string]Resource{},
		Homepage:     ctx.Config.Scoop.Homepage,
		License:      ctx.Config.Scoop.License,
		Description:  ctx.Config.Scoop.Description,
		Persist:      ctx.Config.Scoop.Persist,
	}

	if ctx.Config.Scoop.URLTemplate == "" {
		switch ctx.TokenType {
		case context.TokenTypeGitHub:
			ctx.Config.Scoop.URLTemplate = fmt.Sprintf(
				"%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}",
				ctx.Config.GitHubURLs.Download,
				ctx.Config.Release.GitHub.Owner,
				ctx.Config.Release.GitHub.Name,
			)
		case context.TokenTypeGitLab:
			ctx.Config.Scoop.URLTemplate = fmt.Sprintf(
				"%s/%s/%s/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}",
				ctx.Config.GitLabURLs.Download,
				ctx.Config.Release.GitLab.Owner,
				ctx.Config.Release.GitLab.Name,
			)
		default:
			return result, ErrTokenTypeNotImplementedForScoop
		}
	}

	for _, artifact := range artifacts {
		var arch = "64bit"
		if artifact.Goarch == "386" {
			arch = "32bit"
		}

		url, err := tmpl.New(ctx).
			WithArtifact(artifact, map[string]string{}).
			Apply(ctx.Config.Scoop.URLTemplate)
		if err != nil {
			return result, err
		}

		sum, err := artifact.Checksum("sha256")
		if err != nil {
			return result, err
		}

		manifest.Architecture[arch] = Resource{
			URL:  url,
			Bin:  binaries(artifact),
			Hash: sum,
		}
	}

	data, err := json.MarshalIndent(manifest, "", "    ")
	if err != nil {
		return result, err
	}
	_, err = result.Write(data)
	return result, err
}

func binaries(a *artifact.Artifact) []string {
	// nolint: prealloc
	var bins []string
	var wrap = a.ExtraOr("WrappedIn", "").(string)
	for _, b := range a.ExtraOr("Builds", []*artifact.Artifact{}).([]*artifact.Artifact) {
		bins = append(bins, filepath.Join(wrap, b.Name))
	}
	return bins
}
<commit_msg>chore: add debug log to scoop<commit_after>// Package scoop provides a Pipe that generates a scoop.sh App Manifest and pushes it to a bucket
package scoop

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/apex/log"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/internal/client"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/tmpl"
	"github.com/goreleaser/goreleaser/pkg/context"
)

// ErrNoWindows when there is no build for windows (goos doesn't contain windows)
var ErrNoWindows = errors.New("scoop requires a windows build")

// ErrTokenTypeNotImplementedForScoop indicates that a new token type was not implemented for this pipe
var ErrTokenTypeNotImplementedForScoop = errors.New("token type not implemented for scoop pipe")

// Pipe for build
type Pipe struct{}

func (Pipe) String() string {
	return "scoop manifests"
}

// Publish scoop manifest
func (Pipe) Publish(ctx *context.Context) error {
	// TODO(caarlos0): split the creation of the json file and the publishing in two steps, like the brew pipe.
	client, err := client.New(ctx)
	if err != nil {
		return err
	}
	return doRun(ctx, client)
}

// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
	if ctx.Config.Scoop.Name == "" {
		ctx.Config.Scoop.Name = ctx.Config.ProjectName
	}
	if ctx.Config.Scoop.CommitAuthor.Name == "" {
		ctx.Config.Scoop.CommitAuthor.Name = "goreleaserbot"
	}
	if ctx.Config.Scoop.CommitAuthor.Email == "" {
		ctx.Config.Scoop.CommitAuthor.Email = "goreleaser@carlosbecker.com"
	}

	return nil
}

func doRun(ctx *context.Context, client client.Client) error {
	if ctx.Config.Scoop.Bucket.Name == "" {
		return pipe.Skip("scoop section is not configured")
	}

	// TODO mavogel: in another PR
	// check if release pipe is not configured!
	// if ctx.Config.Release.Disable {
	// }

	// TODO: multiple archives
	if ctx.Config.Archives[0].Format == "binary" {
		return pipe.Skip("archive format is binary")
	}

	var archives = ctx.Artifacts.Filter(
		artifact.And(
			artifact.ByGoos("windows"),
			artifact.ByType(artifact.UploadableArchive),
		),
	).List()
	if len(archives) == 0 {
		return ErrNoWindows
	}

	var path = ctx.Config.Scoop.Name + ".json"

	content, err := buildManifest(ctx, archives)
	if err != nil {
		return err
	}

	if ctx.SkipPublish {
		return pipe.ErrSkipPublishEnabled
	}
	if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "true" {
		return pipe.Skip("scoop.skip_upload is true")
	}
	if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" {
		return pipe.Skip("release is prerelease")
	}
	if ctx.Config.Release.Draft {
		return pipe.Skip("release is marked as draft")
	}
	if ctx.Config.Release.Disable {
		return pipe.Skip("release is disabled")
	}
	return client.CreateFile(
		ctx,
		ctx.Config.Scoop.CommitAuthor,
		ctx.Config.Scoop.Bucket,
		content.Bytes(),
		path,
		fmt.Sprintf("Scoop update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag),
	)
}

// Manifest represents a scoop.sh App Manifest, more info:
// https://github.com/lukesampson/scoop/wiki/App-Manifests
type Manifest struct {
	Version      string              `json:"version"`               // The version of the app that this manifest installs.
	Architecture map[string]Resource `json:"architecture"`          // `architecture`: If the app has 32- and 64-bit versions, architecture can be used to wrap the differences.
	Homepage     string              `json:"homepage,omitempty"`    // `homepage`: The home page for the program.
	License      string              `json:"license,omitempty"`     // `license`: The software license for the program. For well-known licenses, this will be a string like "MIT" or "GPL2". For custom licenses, this should be the URL of the license.
	Description  string              `json:"description,omitempty"` // Description of the app
	Persist      []string            `json:"persist,omitempty"`     // Persist data between updates
}

// Resource represents a combination of a url and a binary name for an architecture
type Resource struct {
	URL  string   `json:"url"`  // URL to the archive
	Bin  []string `json:"bin"`  // name of binary inside the archive
	Hash string   `json:"hash"` // the archive checksum
}

func buildManifest(ctx *context.Context, artifacts []*artifact.Artifact) (bytes.Buffer, error) {
	var result bytes.Buffer
	var manifest = Manifest{
		Version:      ctx.Version,
		Architecture: map[string]Resource{},
		Homepage:     ctx.Config.Scoop.Homepage,
		License:      ctx.Config.Scoop.License,
		Description:  ctx.Config.Scoop.Description,
		Persist:      ctx.Config.Scoop.Persist,
	}

	if ctx.Config.Scoop.URLTemplate == "" {
		switch ctx.TokenType {
		case context.TokenTypeGitHub:
			ctx.Config.Scoop.URLTemplate = fmt.Sprintf(
				"%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}",
				ctx.Config.GitHubURLs.Download,
				ctx.Config.Release.GitHub.Owner,
				ctx.Config.Release.GitHub.Name,
			)
		case context.TokenTypeGitLab:
			ctx.Config.Scoop.URLTemplate = fmt.Sprintf(
				"%s/%s/%s/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}",
				ctx.Config.GitLabURLs.Download,
				ctx.Config.Release.GitLab.Owner,
				ctx.Config.Release.GitLab.Name,
			)
		default:
			return result, ErrTokenTypeNotImplementedForScoop
		}
	}

	for _, artifact := range artifacts {
		var arch = "64bit"
		if artifact.Goarch == "386" {
			arch = "32bit"
		}

		url, err := tmpl.New(ctx).
			WithArtifact(artifact, map[string]string{}).
			Apply(ctx.Config.Scoop.URLTemplate)
		if err != nil {
			return result, err
		}

		sum, err := artifact.Checksum("sha256")
		if err != nil {
			return result, err
		}

		log.WithFields(log.Fields{
			"artifactExtras":   artifact.Extra,
			"fromURLTemplate":  ctx.Config.Scoop.URLTemplate,
			"templatedBrewURL": url,
			"sum":              sum,
		}).Debug("scoop url templating")

		manifest.Architecture[arch] = Resource{
			URL:  url,
			Bin:  binaries(artifact),
			Hash: sum,
		}
	}

	data, err := json.MarshalIndent(manifest, "", "    ")
	if err != nil {
		return result, err
	}
	_, err = result.Write(data)
	return result, err
}

func binaries(a *artifact.Artifact) []string {
	// nolint: prealloc
	var bins []string
	var wrap = a.ExtraOr("WrappedIn", "").(string)
	for _, b := range a.ExtraOr("Builds", []*artifact.Artifact{}).([]*artifact.Artifact) {
		bins = append(bins, filepath.Join(wrap, b.Name))
	}
	return bins
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2019 the Octant contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package terminal

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"sync"
	"unicode"

	corev1 "k8s.io/api/core/v1"

	"github.com/pkg/errors"
	"github.com/vmware-tanzu/octant/internal/cluster"
	"github.com/vmware-tanzu/octant/pkg/log"
	"github.com/vmware-tanzu/octant/pkg/store"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"
)

//go:generate mockgen -source=manager.go -destination=./fake/mock_interface.go -package=fake github.com/vmware-tanzu/octant/internal/terminal TerminalManager

// Manager defines the interface for querying terminal instance.
type Manager interface {
	List(namespace string) []Instance
	Get(id string) (Instance, bool)
	Delete(id string)
	Create(ctx context.Context, logger log.Logger, key store.Key, container string, command string, sessionID string) (Instance, error)
	Select(ctx context.Context) chan Instance
	ForceUpdate(id string)
	SendScrollback(id string) bool
	SetScrollback(id string, send bool)
	StopAll() error
}

type manager struct {
	restClient   rest.Interface
	config       *rest.Config
	objectStore  store.Store
	instances    sync.Map
	scrollback   sync.Map
	chanInstance chan Instance
}

var _ Manager = (*manager)(nil)

// NewTerminalManager creates a concrete TerminalMananger
func NewTerminalManager(ctx context.Context, client cluster.ClientInterface, objectStore store.Store) (Manager, error) {
	restClient, err := client.RESTClient()
	if err != nil {
		return nil, errors.Wrap(err, "fetching RESTClient")
	}

	tm := &manager{
		restClient:   restClient,
		config:       client.RESTConfig(),
		objectStore:  objectStore,
		chanInstance: make(chan Instance, 100),
	}
	return tm, nil
}

// SetScrollback sets the current scrollback state for a terminal instance.
func (tm *manager) SetScrollback(id string, sendScrollback bool) {
	tm.scrollback.Store(id, sendScrollback)
}

// SendScrollback returns the current scrollback state for a terminal instance.
func (tm *manager) SendScrollback(id string) bool {
	v, ok := tm.scrollback.Load(id)
	if !ok {
		return false
	}
	return v.(bool)
}

// ForceUpdate sends the instance for the given ID on to the instance work channel.
func (tm *manager) ForceUpdate(id string) {
	i, ok := tm.Get(id)
	if ok {
		tm.chanInstance <- i
	}
}

func (tm *manager) Select(ctx context.Context) chan Instance {
	return tm.chanInstance
}

func (tm *manager) Create(ctx context.Context, logger log.Logger, key store.Key, container string, command string, sessionID string) (Instance, error) {
	var t, err = tm.Find(key, sessionID)

	if t != nil {
		logger.Debugf("Using cashed terminal", t.ID(), t.Active())
		if !t.Active() {
			logger.Debugf("Deleting terminal", t.ID())
			t.ResetScrollback()
			tm.SetScrollback(t.ID(), true)
			tm.ForceUpdate(t.ID())
			tm.Delete(t.ID())
		}
		return t, err
	}

	ctx = context.WithValue(ctx, "sessionID", sessionID)
	t = NewTerminalInstance(ctx, logger, key, container, command, tm.chanInstance, sessionID)
	tm.instances.Store(t.ID(), t)
	pod, err := tm.objectStore.Get(ctx, key)
	if err != nil {
		return nil, err
	}
	if pod == nil {
		return nil, errors.New("pod not found")
	}

	logger.Debugf("NewTerminalInstance created for", pod.GetName(), pod.GetUID())

	req := tm.restClient.Post().
		Resource("pods").
		Name(pod.GetName()).
		Namespace(pod.GetNamespace()).
		SubResource("exec")

	req.VersionedParams(&corev1.PodExecOptions{
		Container: container,
		Command:   parseCommand(command),
		Stdin:     true,
		Stdout:    true,
		Stderr:    false,
		TTY:       true,
	}, scheme.ParameterCodec)

	rc, err := remotecommand.NewSPDYExecutor(tm.config, "POST", req.URL())
	if err != nil {
		t.SetExitMessage(fmt.Sprintf("%v", err))
		return nil, err
	}

	pty := t.PTY()
	opts := remotecommand.StreamOptions{
		Stdin:             pty,
		Stdout:            pty,
		Stderr:            pty,
		Tty:               true,
		TerminalSizeQueue: pty,
	}

	go func() {
		logger.Debugf("running stream command")
		err = rc.Stream(opts)
		if err != nil {
			t.SetExitMessage(fmt.Sprintf("%s", err))
			logger.Errorf("streaming: %+v", err)
		}
		t.Stop()
		logger.Debugf("stopping stream command")
	}()
	return t, nil
}

func (tm *manager) Get(id string) (Instance, bool) {
	v, ok := tm.instances.Load(id)
	if !ok {
		return nil, ok
	}
	return v.(Instance), ok
}

func (tm *manager) Find(key store.Key, sessionID string) (Instance, error) {
	for _, terminalInstance := range tm.List(key.Namespace) {
		if key == terminalInstance.Key() {
			if len(terminalInstance.SessionID()) == 0 && len(sessionID) > 0 {
				terminalInstance.SetSessionID(sessionID)
			}
			return terminalInstance, nil
		}
	}
	return nil, errors.New("terminal not found")
}

func (tm *manager) List(namespace string) []Instance {
	instances := []Instance{}
	tm.instances.Range(func(k interface{}, v interface{}) bool {
		instance := v.(Instance)
		if namespace == "" {
			instances = append(instances, instance)

		} else if instance.Key().Namespace == namespace {
			instances = append(instances, instance)
		}
		return true
	})
	sort.Slice(instances, func(i, j int) bool {
		return instances[i].CreatedAt().Before(instances[j].CreatedAt())
	})
	return instances
}

func (tm *manager) Delete(id string) {
	t, ok := tm.Get(id)
	if ok {
		t.Stop()
		tm.instances.Delete(id)
	}
}

func (tm *manager) StopAll() error {
	tm.instances.Range(func(k interface{}, v interface{}) bool {
		v.(Instance).Stop()
		return true
	})
	return nil
}

func parseCommand(command string) []string {
	lastQuote := rune(0)
	f := func(c rune) bool {
		switch {
		case c == lastQuote:
			lastQuote = rune(0)
			return false
		case lastQuote != rune(0):
			return false
		case unicode.In(c, unicode.Quotation_Mark):
			lastQuote = c
			return false
		default:
			return unicode.IsSpace(c)

		}
	}
	return strings.FieldsFunc(command, f)
}
<commit_msg>error logging should be debug to avoid spam<commit_after>/*
Copyright (c) 2019 the Octant contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package terminal

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"sync"
	"unicode"

	corev1 "k8s.io/api/core/v1"

	"github.com/pkg/errors"
	"github.com/vmware-tanzu/octant/internal/cluster"
	"github.com/vmware-tanzu/octant/pkg/log"
	"github.com/vmware-tanzu/octant/pkg/store"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"
)

//go:generate mockgen -source=manager.go -destination=./fake/mock_interface.go -package=fake github.com/vmware-tanzu/octant/internal/terminal TerminalManager

// Manager defines the interface for querying terminal instance.
type Manager interface {
	List(namespace string) []Instance
	Get(id string) (Instance, bool)
	Delete(id string)
	Create(ctx context.Context, logger log.Logger, key store.Key, container string, command string, sessionID string) (Instance, error)
	Select(ctx context.Context) chan Instance
	ForceUpdate(id string)
	SendScrollback(id string) bool
	SetScrollback(id string, send bool)
	StopAll() error
}

type manager struct {
	restClient   rest.Interface
	config       *rest.Config
	objectStore  store.Store
	instances    sync.Map
	scrollback   sync.Map
	chanInstance chan Instance
}

var _ Manager = (*manager)(nil)

// NewTerminalManager creates a concrete TerminalMananger
func NewTerminalManager(ctx context.Context, client cluster.ClientInterface, objectStore store.Store) (Manager, error) {
	restClient, err := client.RESTClient()
	if err != nil {
		return nil, errors.Wrap(err, "fetching RESTClient")
	}

	tm := &manager{
		restClient:   restClient,
		config:       client.RESTConfig(),
		objectStore:  objectStore,
		chanInstance: make(chan Instance, 100),
	}
	return tm, nil
}

// SetScrollback sets the current scrollback state for a terminal instance.
func (tm *manager) SetScrollback(id string, sendScrollback bool) {
	tm.scrollback.Store(id, sendScrollback)
}

// SendScrollback returns the current scrollback state for a terminal instance.
func (tm *manager) SendScrollback(id string) bool {
	v, ok := tm.scrollback.Load(id)
	if !ok {
		return false
	}
	return v.(bool)
}

// ForceUpdate sends the instance for the given ID on to the instance work channel.
func (tm *manager) ForceUpdate(id string) {
	i, ok := tm.Get(id)
	if ok {
		tm.chanInstance <- i
	}
}

func (tm *manager) Select(ctx context.Context) chan Instance {
	return tm.chanInstance
}

func (tm *manager) Create(ctx context.Context, logger log.Logger, key store.Key, container string, command string, sessionID string) (Instance, error) {
	var t, err = tm.Find(key, sessionID)

	if t != nil {
		logger.Debugf("Using cashed terminal", t.ID(), t.Active())
		if !t.Active() {
			logger.Debugf("Deleting terminal", t.ID())
			t.ResetScrollback()
			tm.SetScrollback(t.ID(), true)
			tm.ForceUpdate(t.ID())
			tm.Delete(t.ID())
		}
		return t, err
	}

	ctx = context.WithValue(ctx, "sessionID", sessionID)
	t = NewTerminalInstance(ctx, logger, key, container, command, tm.chanInstance, sessionID)
	tm.instances.Store(t.ID(), t)
	pod, err := tm.objectStore.Get(ctx, key)
	if err != nil {
		return nil, err
	}
	if pod == nil {
		return nil, errors.New("pod not found")
	}

	logger.Debugf("NewTerminalInstance created for", pod.GetName(), pod.GetUID())

	req := tm.restClient.Post().
		Resource("pods").
		Name(pod.GetName()).
		Namespace(pod.GetNamespace()).
		SubResource("exec")

	req.VersionedParams(&corev1.PodExecOptions{
		Container: container,
		Command:   parseCommand(command),
		Stdin:     true,
		Stdout:    true,
		Stderr:    false,
		TTY:       true,
	}, scheme.ParameterCodec)

	rc, err := remotecommand.NewSPDYExecutor(tm.config, "POST", req.URL())
	if err != nil {
		t.SetExitMessage(fmt.Sprintf("%v", err))
		return nil, err
	}

	pty := t.PTY()
	opts := remotecommand.StreamOptions{
		Stdin:             pty,
		Stdout:            pty,
		Stderr:            pty,
		Tty:               true,
		TerminalSizeQueue: pty,
	}

	go func() {
		logger.Debugf("running stream command")
		err = rc.Stream(opts)
		if err != nil {
			t.SetExitMessage(fmt.Sprintf("%s", err))
			logger.Debugf("streaming: %+v", err)
		}
		t.Stop()
		logger.Debugf("stopping stream command")
	}()
	return t, nil
}

func (tm *manager) Get(id string) (Instance, bool) {
	v, ok := tm.instances.Load(id)
	if !ok {
		return nil, ok
	}
	return v.(Instance), ok
}

func (tm *manager) Find(key store.Key, sessionID string) (Instance, error) {
	for _, terminalInstance := range tm.List(key.Namespace) {
		if key == terminalInstance.Key() {
			if len(terminalInstance.SessionID()) == 0 && len(sessionID) > 0 {
				terminalInstance.SetSessionID(sessionID)
			}
			return terminalInstance, nil
		}
	}
	return nil, errors.New("terminal not found")
}

func (tm *manager) List(namespace string) []Instance {
	instances := []Instance{}
	tm.instances.Range(func(k interface{}, v interface{}) bool {
		instance := v.(Instance)
		if namespace == "" {
			instances = append(instances, instance)

		} else if instance.Key().Namespace == namespace {
			instances = append(instances, instance)
		}
		return true
	})
	sort.Slice(instances, func(i, j int) bool {
		return instances[i].CreatedAt().Before(instances[j].CreatedAt())
	})
	return instances
}

func (tm *manager) Delete(id string) {
	t, ok := tm.Get(id)
	if ok {
		t.Stop()
		tm.instances.Delete(id)
	}
}

func (tm *manager) StopAll() error {
	tm.instances.Range(func(k interface{}, v interface{}) bool {
		v.(Instance).Stop()
		return true
	})
	return nil
}

func parseCommand(command string) []string {
	lastQuote := rune(0)
	f := func(c rune) bool {
		switch {
		case c == lastQuote:
			lastQuote = rune(0)
			return false
		case lastQuote != rune(0):
			return false
		case unicode.In(c, unicode.Quotation_Mark):
			lastQuote = c
			return false
		default:
			return unicode.IsSpace(c)

		}
	}
	return strings.FieldsFunc(command, f)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"hash/fnv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/karlseguin/ccache"
)

var cache = ccache.New(ccache.Configure())

const (
	ttl = time.Minute * 15
)

type iam struct {
	baseARN string
}

// credentials represent the security credentials response.
type credentials struct {
	Code            string
	LastUpdated     string
	Type            string
	AccessKeyID     string `json:"AccessKeyId"`
	SecretAccessKey string
	Token           string
	Expiration      string
}

func (iam *iam) roleARN(role string) string {
	return fmt.Sprintf("%s%s", iam.baseARN, role)
}

func getHash(text string) string {
	h := fnv.New32a()
	h.Write([]byte(text))
	return fmt.Sprintf("%x", h.Sum32())
}

func (iam *iam) assumeRole(roleARN, remoteIP string) (*credentials, error) {
	item, err := cache.Fetch(roleARN, ttl, func() (interface{}, error) {
		idx := strings.LastIndex(roleARN, "/")
		svc := sts.New(session.New(), &aws.Config{LogLevel: aws.LogLevel(2)})
		resp, err := svc.AssumeRole(&sts.AssumeRoleInput{
			DurationSeconds: aws.Int64(int64(ttl.Seconds() * 2)),
			RoleArn:         aws.String(roleARN),
			RoleSessionName: aws.String(fmt.Sprintf("%s-%s", roleARN[idx+1:], getHash(remoteIP))),
		})
		if err != nil {
			return nil, err
		}
		return &credentials{
			AccessKeyID:     *resp.Credentials.AccessKeyId,
			Code:            "Success",
			Expiration:      resp.Credentials.Expiration.Format("2006-01-02T15:04:05Z"),
			LastUpdated:     time.Now().Format("2006-01-02T15:04:05Z"),
			SecretAccessKey: *resp.Credentials.SecretAccessKey,
			Token:           *resp.Credentials.SessionToken,
			Type:            "AWS-HMAC",
		}, nil
	})
	if err != nil {
		return nil, err
	}
	return item.Value().(*credentials), nil
}

func newIAM(baseARN string) *iam {
	return &iam{baseARN: baseARN}
}
<commit_msg>Truncating session names for role assumption (#43)<commit_after>package cmd

import (
	"fmt"
	"hash/fnv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/karlseguin/ccache"
)

var cache = ccache.New(ccache.Configure())

const (
	ttl               = time.Minute * 15
	maxSessNameLength = 64
)

type iam struct {
	baseARN string
}

// credentials represent the security credentials response.
type credentials struct {
	Code            string
	LastUpdated     string
	Type            string
	AccessKeyID     string `json:"AccessKeyId"`
	SecretAccessKey string
	Token           string
	Expiration      string
}

func (iam *iam) roleARN(role string) string {
	return fmt.Sprintf("%s%s", iam.baseARN, role)
}

func getHash(text string) string {
	h := fnv.New32a()
	h.Write([]byte(text))
	return fmt.Sprintf("%x", h.Sum32())
}

func (iam *iam) assumeRole(roleARN, remoteIP string) (*credentials, error) {
	item, err := cache.Fetch(roleARN, ttl, func() (interface{}, error) {
		svc := sts.New(session.New(), &aws.Config{LogLevel: aws.LogLevel(2)})
		resp, err := svc.AssumeRole(&sts.AssumeRoleInput{
			DurationSeconds: aws.Int64(int64(ttl.Seconds() * 2)),
			RoleArn:         aws.String(roleARN),
			RoleSessionName: aws.String(sessionName(roleARN, remoteIP)),
		})
		if err != nil {
			return nil, err
		}

		return &credentials{
			AccessKeyID:     *resp.Credentials.AccessKeyId,
			Code:            "Success",
			Expiration:      resp.Credentials.Expiration.Format("2006-01-02T15:04:05Z"),
			LastUpdated:     time.Now().Format("2006-01-02T15:04:05Z"),
			SecretAccessKey: *resp.Credentials.SecretAccessKey,
			Token:           *resp.Credentials.SessionToken,
			Type:            "AWS-HMAC",
		}, nil
	})
	if err != nil {
		return nil, err
	}
	return item.Value().(*credentials), nil
}

func sessionName(roleARN, remoteIP string) string {
	idx := strings.LastIndex(roleARN, "/")
	name := fmt.Sprintf("%s-%s", getHash(remoteIP), roleARN[idx+1:])
	return fmt.Sprintf("%.[2]*[1]s", name, maxSessNameLength)
}

func newIAM(baseARN string) *iam {
	return &iam{baseARN: baseARN}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/howeyc/gopass"
	"github.com/spf13/cobra"

	"github.com/jpki/myna/libmyna"
)

var pinCmd = &cobra.Command{
	Use:   "pin",
	Short: "PIN関連操作",
}

var pinStatusCmd = &cobra.Command{
	Use:   "status",
	Short: "PINステータスを表示",
	RunE:  pinStatus,
}

func pinStatus(cmd *cobra.Command, args []string) error {
	status, err := libmyna.GetPinStatus()
	if err != nil {
		return err
	}

	fmt.Printf("券面事項PIN(A):\tのこり%2d回\n",
		status["card_info_pin_a"])
	fmt.Printf("券面事項PIN(B):\tのこり%2d回\n",
		status["card_info_pin_b"])
	fmt.Printf("入力補助PIN:\tのこり%2d回\n",
		status["card_input_helper_pin"])
	fmt.Printf("入力補助PIN(A):\tのこり%2d回\n",
		status["card_input_helper_pin_a"])
	fmt.Printf("入力補助PIN(B):\tのこり%2d回\n",
		status["card_input_helper_pin_b"])
	fmt.Printf("JPKI認証用PIN:\tのこり%2d回\n", status["jpki_auth"])
	fmt.Printf("JPKI署名用PIN:\tのこり%2d回\n", status["jpki_sign"])
	/*
		fmt.Printf("謎のPIN1:\tのこり%d回\n", status["unknown1"])
		fmt.Printf("謎のPIN2:\tのこり%d回\n", status["unknown2"])
	*/
	return nil
}

var pinChangeCmd = &cobra.Command{
	Use:   "change",
	Short: "各種PINを変更",
}

var pinChangeCardCmd = &cobra.Command{
	Use:   "card",
	Short: "券面入力補助用PINを変更",
	Long: `券面入力補助用PINを変更します
暗証番号は4桁の数字を入力してください
`,
	RunE: pinChangeCard,
}

func pinChangeCard(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "券面入力補助用PIN(4桁)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	newpin, err := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	err = libmyna.ChangeCardInputHelperPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

var pinChangeJPKIAuthCmd = &cobra.Command{
	Use:   "auth",
	Short: "JPKI認証用PINを変更",
	Long: `JPKI認証用PINを変更します
暗証番号は4桁の数字を入力してください
`,
	RunE: pinChangeJPKIAuth,
}

func pinChangeJPKIAuth(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "JPKI認証用PIN(4桁)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	newpin, _ := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	err = libmyna.ChangeJPKIAuthPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

var pinChangeJPKISignCmd = &cobra.Command{
	Use:   "sign",
	Short: "JPKI署名用パスワードを変更",
	Long: `JPKI署名用パスワードを変更します
パスワードに利用できる文字種は以下のとおり

ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789

文字数は6文字から16文字まで
アルファベットは大文字のみ使うことができます。
小文字を入力した場合は、大文字に変換されます。
`,
	RunE: pinChangeJPKISign,
}

func pinChangeJPKISign(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "JPKI署名用パスワード(6-16文字)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}
	newpin, err := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}
	err = libmyna.ChangeJPKISignPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

func inputPin(prompt string) (string, error) {
	fmt.Printf(prompt)
	input, err := gopass.GetPasswdMasked()
	if err != nil {
		return "", err
	}
	pin := string(input)
	return pin, err
}

func init() {
	pinCmd.AddCommand(pinStatusCmd)
	pinCmd.AddCommand(pinChangeCmd)

	pinChangeCardCmd.Flags().String("pin", "", "現在の暗証番号(4桁)")
	pinChangeCardCmd.Flags().String("newpin", "", "新しい暗証番号(4桁)")
	pinChangeCmd.AddCommand(pinChangeCardCmd)

	pinChangeJPKIAuthCmd.Flags().String("pin", "", "現在の暗証番号(4桁)")
	pinChangeJPKIAuthCmd.Flags().String("newpin", "", "新しい暗証番号(4桁)")
	pinChangeCmd.AddCommand(pinChangeJPKIAuthCmd)

	pinChangeJPKISignCmd.Flags().String("pin", "", "現在のパスワード(6-16文字)")
	pinChangeJPKISignCmd.Flags().String("newpin", "", "新しいパスワード(6-16文字)")
	pinChangeCmd.AddCommand(pinChangeJPKISignCmd)
}
<commit_msg>print prompt to stderr<commit_after>package cmd

import (
	"fmt"
	"os"

	"github.com/howeyc/gopass"
	"github.com/spf13/cobra"

	"github.com/jpki/myna/libmyna"
)

var pinCmd = &cobra.Command{
	Use:   "pin",
	Short: "PIN関連操作",
}

var pinStatusCmd = &cobra.Command{
	Use:   "status",
	Short: "PINステータスを表示",
	RunE:  pinStatus,
}

func pinStatus(cmd *cobra.Command, args []string) error {
	status, err := libmyna.GetPinStatus()
	if err != nil {
		return err
	}

	fmt.Printf("券面事項PIN(A):\tのこり%2d回\n",
		status["card_info_pin_a"])
	fmt.Printf("券面事項PIN(B):\tのこり%2d回\n",
		status["card_info_pin_b"])
	fmt.Printf("入力補助PIN:\tのこり%2d回\n",
		status["card_input_helper_pin"])
	fmt.Printf("入力補助PIN(A):\tのこり%2d回\n",
		status["card_input_helper_pin_a"])
	fmt.Printf("入力補助PIN(B):\tのこり%2d回\n",
		status["card_input_helper_pin_b"])
	fmt.Printf("JPKI認証用PIN:\tのこり%2d回\n", status["jpki_auth"])
	fmt.Printf("JPKI署名用PIN:\tのこり%2d回\n", status["jpki_sign"])
	/*
		fmt.Printf("謎のPIN1:\tのこり%d回\n", status["unknown1"])
		fmt.Printf("謎のPIN2:\tのこり%d回\n", status["unknown2"])
	*/
	return nil
}

var pinChangeCmd = &cobra.Command{
	Use:   "change",
	Short: "各種PINを変更",
}

var pinChangeCardCmd = &cobra.Command{
	Use:   "card",
	Short: "券面入力補助用PINを変更",
	Long: `券面入力補助用PINを変更します
暗証番号は4桁の数字を入力してください
`,
	RunE: pinChangeCard,
}

func pinChangeCard(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "券面入力補助用PIN(4桁)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	newpin, err := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	err = libmyna.ChangeCardInputHelperPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

var pinChangeJPKIAuthCmd = &cobra.Command{
	Use:   "auth",
	Short: "JPKI認証用PINを変更",
	Long: `JPKI認証用PINを変更します
暗証番号は4桁の数字を入力してください
`,
	RunE: pinChangeJPKIAuth,
}

func pinChangeJPKIAuth(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "JPKI認証用PIN(4桁)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	newpin, _ := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}

	err = libmyna.ChangeJPKIAuthPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

var pinChangeJPKISignCmd = &cobra.Command{
	Use:   "sign",
	Short: "JPKI署名用パスワードを変更",
	Long: `JPKI署名用パスワードを変更します
パスワードに利用できる文字種は以下のとおり

ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789

文字数は6文字から16文字まで
アルファベットは大文字のみ使うことができます。
小文字を入力した場合は、大文字に変換されます。
`,
	RunE: pinChangeJPKISign,
}

func pinChangeJPKISign(cmd *cobra.Command, args []string) error {
	fmt.Println(cmd.Long)
	pinName := "JPKI署名用パスワード(6-16文字)"
	pin, err := cmd.Flags().GetString("pin")
	if pin == "" {
		pin, err = inputPin(fmt.Sprintf("現在の%s: ", pinName))
		if err != nil {
			return nil
		}
	}
	newpin, err := cmd.Flags().GetString("newpin")
	if newpin == "" {
		newpin, err = inputPin(fmt.Sprintf("新しい%s: ", pinName))
		if err != nil {
			return nil
		}
	}
	err = libmyna.ChangeJPKISignPin(pin, newpin)
	if err != nil {
		return err
	}
	fmt.Printf("%sを変更しました", pinName)
	return nil
}

func inputPin(prompt string) (string, error) {
	input, err := gopass.GetPasswdPrompt(prompt, true, os.Stdin, os.Stderr)
	if err != nil {
		return "", err
	}
	pin := string(input)
	return pin, err
}

func init() {
	pinCmd.AddCommand(pinStatusCmd)
	pinCmd.AddCommand(pinChangeCmd)

	pinChangeCardCmd.Flags().String("pin", "", "現在の暗証番号(4桁)")
	pinChangeCardCmd.Flags().String("newpin", "", "新しい暗証番号(4桁)")
	pinChangeCmd.AddCommand(pinChangeCardCmd)

	pinChangeJPKIAuthCmd.Flags().String("pin", "", "現在の暗証番号(4桁)")
	pinChangeJPKIAuthCmd.Flags().String("newpin", "", "新しい暗証番号(4桁)")
	pinChangeCmd.AddCommand(pinChangeJPKIAuthCmd)

	pinChangeJPKISignCmd.Flags().String("pin", "", "現在のパスワード(6-16文字)")
	pinChangeJPKISignCmd.Flags().String("newpin", "", "新しいパスワード(6-16文字)")
	pinChangeCmd.AddCommand(pinChangeJPKISignCmd)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strings"
	"syscall"

	"runtime"

	"github.com/bfirsh/whalebrew/packages"
	"github.com/spf13/cobra"
	"golang.org/x/crypto/ssh/terminal"
)

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

var runCommand = &cobra.Command{
	Use:                "run PACKAGEPATH [ARGS ...]",
	Short:              "Run a package",
	DisableFlagParsing: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 1 {
			return cmd.Help()
		}

		pkg, err := packages.LoadPackageFromPath(args[0])
		if err != nil {
			return err
		}
		dockerPath, err := exec.LookPath("docker")
		if err != nil {
			return err
		}

		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		dockerArgs := []string{
			dockerPath,
			"run",
			"--interactive",
			"--rm",
			"--workdir", "/workdir",
			"-v", fmt.Sprintf("%s:/workdir", cwd),
		}
		if terminal.IsTerminal(int(os.Stdin.Fd())) {
			dockerArgs = append(dockerArgs, "--tty")
		}
		for _, volume := range pkg.Volumes {
			// special case expanding home directory
			if strings.HasPrefix(volume, "~/") {
				user, err := user.Current()
				if err != nil {
					return err
				}
				volume = user.HomeDir + volume[1:]
			}
			dockerArgs = append(dockerArgs, "-v")
			dockerArgs = append(dockerArgs, volume)
		}
		for _, envvar := range pkg.Environment {
			dockerArgs = append(dockerArgs, "-e")
			dockerArgs = append(dockerArgs, envvar)
		}
		for _, portmap := range pkg.Ports {
			dockerArgs = append(dockerArgs, "-p")
			dockerArgs = append(dockerArgs, portmap)
		}

		user, err := user.Current()
		if err != nil {
			return err
		}
		dockerArgs = append(dockerArgs, "-u")
		dockerArgs = append(dockerArgs, user.Uid + ":" + user.Gid)

		dockerArgs = append(dockerArgs, pkg.Image)
		dockerArgs = append(dockerArgs, args[1:]...)

		if runtime.GOOS == "windows" {
			dockerCmd := exec.Command(dockerPath, dockerArgs[1:]...)
			dockerCmd.Env = os.Environ()
			dockerCmd.Stdin = os.Stdin
			dockerCmd.Stdout = os.Stdout
			dockerCmd.Stderr = os.Stderr

			exitStatus := 1
			if err := dockerCmd.Run(); err != nil {
				if exitError, ok := err.(*exec.ExitError); ok {
					if ws, ok := exitError.Sys().(syscall.WaitStatus); ok {
						exitStatus = ws.ExitStatus()
					}
				}
			} else {
				ws := dockerCmd.ProcessState.Sys().(syscall.WaitStatus)
				exitStatus = ws.ExitStatus()
			}
			os.Exit(exitStatus)
		}

		return syscall.Exec(dockerPath, dockerArgs, os.Environ())
	},
}
<commit_msg>Fix run for windows<commit_after>package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strings"
	"syscall"

	"runtime"

	"github.com/bfirsh/whalebrew/packages"
	"github.com/spf13/cobra"
	"golang.org/x/crypto/ssh/terminal"
)

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

var runCommand = &cobra.Command{
	Use:                "run PACKAGEPATH [ARGS ...]",
	Short:              "Run a package",
	DisableFlagParsing: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 1 {
			return cmd.Help()
		}

		pkg, err := packages.LoadPackageFromPath(args[0])
		if err != nil {
			return err
		}
		dockerPath, err := exec.LookPath("docker")
		if err != nil {
			return err
		}

		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		dockerArgs := []string{
			dockerPath,
			"run",
			"--interactive",
			"--rm",
			"--workdir", "/workdir",
			"-v", fmt.Sprintf("%s:/workdir", cwd),
		}
		if terminal.IsTerminal(int(os.Stdin.Fd())) {
			dockerArgs = append(dockerArgs, "--tty")
		}
		for _, volume := range pkg.Volumes {
			// special case expanding home directory
			if strings.HasPrefix(volume, "~/") {
				user, err := user.Current()
				if err != nil {
					return err
				}
				volume = user.HomeDir + volume[1:]
			}
			dockerArgs = append(dockerArgs, "-v")
			dockerArgs = append(dockerArgs, volume)
		}
		for _, envvar := range pkg.Environment {
			dockerArgs = append(dockerArgs, "-e")
			dockerArgs = append(dockerArgs, envvar)
		}
		for _, portmap := range pkg.Ports {
			dockerArgs = append(dockerArgs, "-p")
			dockerArgs = append(dockerArgs, portmap)
		}

		dockerArgs = append(dockerArgs, pkg.Image)
		dockerArgs = append(dockerArgs, args[1:]...)

		if runtime.GOOS == "windows" {
			dockerCmd := exec.Command(dockerPath, dockerArgs[1:]...)
			dockerCmd.Env = os.Environ()
			dockerCmd.Stdin = os.Stdin
			dockerCmd.Stdout = os.Stdout
			dockerCmd.Stderr = os.Stderr

			exitStatus := 1
			if err := dockerCmd.Run(); err != nil {
				if exitError, ok := err.(*exec.ExitError); ok {
					if ws, ok := exitError.Sys().(syscall.WaitStatus); ok {
						exitStatus = ws.ExitStatus()
					}
				}
			} else {
				ws := dockerCmd.ProcessState.Sys().(syscall.WaitStatus)
				exitStatus = ws.ExitStatus()
			}
			os.Exit(exitStatus)
		}

		user, err := user.Current()
		if err != nil {
			return err
		}
		dockerArgs = append(dockerArgs, "-u")
		dockerArgs = append(dockerArgs, user.Uid+":"+user.Gid)

		return syscall.Exec(dockerPath, dockerArgs, os.Environ())
	},
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/sebcat/har"
)

type harRequest *har.Request

type reqCmd struct {
	V          uint       `json:"v"`
	Cmd        cmdKind    `json:"cmd"`
	Lane       lane       `json:"lane"`
	HARRequest harRequest `json:"har_req"`
}

type reqCmdRep struct {
	V        uint     `json:"v"`
	Cmd      cmdKind  `json:"cmd"`
	Lane     lane     `json:"lane"`
	Us       uint64   `json:"us"`
	HAREntry harEntry `json:"har_rep,omitempty"`
	Reason   string   `json:"reason,omitempty"`
}

func (cmd *reqCmd) Kind() cmdKind {
	return cmd.Cmd
}

func (cmd *reqCmd) Exec(cfg *ymlCfg) (rep []byte, err error) {
	lastLane = cmd.Lane
	if !isHARReady() {
		newHARTransport()
	}

	cmd.updateUserAgentHeader()
	if err = cmd.updateURL(cfg); err != nil {
		return
	}
	cmd.updateHostHeader(cfg)
	cmdRep, err := cmd.makeRequest()
	if err != nil {
		return
	}
	totalR++

	if rep, err = json.Marshal(cmdRep); err != nil {
		log.Println("[ERR]", err)
	}
	return
}

func (cmd *reqCmd) makeRequest() (rep *reqCmdRep, err error) {
	r, err := (*cmd.HARRequest).Request()
	if err != nil {
		log.Println("[ERR]", err)
		return
	}

	log.Printf("[NFO] 🡳\n  ▲  %+v\n", cmd.HARRequest)
	start := time.Now()
	_, err = clientReq.Do(r)
	us := uint64(time.Since(start) / time.Microsecond)
	log.Printf("[NFO] ❙ %dμs\n", us)
	rep = &reqCmdRep{
		V:    v,
		Cmd:  cmd.Cmd,
		Us:   us,
		Lane: cmd.Lane,
	}

	if err != nil {
		//FIXME: is there a way to describe these failures in HAR 1.2?
		rep.Reason = fmt.Sprintf("%+v", err.Error())
		log.Printf("[NFO]\n  ▼  %s\n", rep.Reason)
		err = nil
		return
	}

	//FIXME maybe: append(headers, fmt.Sprintf("Host: %v", resp.Host))
	//FIXME: make sure order is preserved github.com/golang/go/issues/21853
	rep.HAREntry = lastHAR()
	log.Printf("[NFO]\n  ▼  %+v\n", rep.HAREntry)
	return
}

func (cmd *reqCmd) updateURL(cfg *ymlCfg) (err error) {
	URL, err := url.Parse(cmd.HARRequest.URL)
	if err != nil {
		log.Println("[ERR]", err)
		return
	}

	// TODO: if host is an IPv6 then it has to be braced with []
	URL.Host = cfg.FinalHost + ":" + cfg.FinalPort
	cmd.HARRequest.URL = URL.String()
	return
}

func (cmd *reqCmd) updateUserAgentHeader() {
	for i := range cmd.HARRequest.Headers {
		if cmd.HARRequest.Headers[i].Name == "User-Agent" {
			if strings.HasPrefix("FuzzyMonkey.co/", cmd.HARRequest.Headers[i].Value) {
				cmd.HARRequest.Headers[i].Value = binTitle
				break
			}
		}
	}
}

func (cmd *reqCmd) updateHostHeader(cfg *ymlCfg) {
	for i := range cmd.HARRequest.Headers {
		if cmd.HARRequest.Headers[i].Name == "Host" {
			cmd.HARRequest.Headers[i].Value = cfg.FinalHost + ":" + cfg.FinalPort
			break
		}
	}
}
<commit_msg>recook: fix ordering of strings.HasPrefix<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/sebcat/har"
)

type harRequest *har.Request

type reqCmd struct {
	V          uint       `json:"v"`
	Cmd        cmdKind    `json:"cmd"`
	Lane       lane       `json:"lane"`
	HARRequest harRequest `json:"har_req"`
}

type reqCmdRep struct {
	V        uint     `json:"v"`
	Cmd      cmdKind  `json:"cmd"`
	Lane     lane     `json:"lane"`
	Us       uint64   `json:"us"`
	HAREntry harEntry `json:"har_rep,omitempty"`
	Reason   string   `json:"reason,omitempty"`
}

func (cmd *reqCmd) Kind() cmdKind {
	return cmd.Cmd
}

func (cmd *reqCmd) Exec(cfg *ymlCfg) (rep []byte, err error) {
	lastLane = cmd.Lane
	if !isHARReady() {
		newHARTransport()
	}

	cmd.updateUserAgentHeader()
	if err = cmd.updateURL(cfg); err != nil {
		return
	}
	cmd.updateHostHeader(cfg)
	cmdRep, err := cmd.makeRequest()
	if err != nil {
		return
	}
	totalR++

	if rep, err = json.Marshal(cmdRep); err != nil {
		log.Println("[ERR]", err)
	}
	return
}

func (cmd *reqCmd) makeRequest() (rep *reqCmdRep, err error) {
	r, err := (*cmd.HARRequest).Request()
	if err != nil {
		log.Println("[ERR]", err)
		return
	}

	log.Printf("[NFO] 🡳\n  ▲  %+v\n", cmd.HARRequest)
	start := time.Now()
	_, err = clientReq.Do(r)
	us := uint64(time.Since(start) / time.Microsecond)
	log.Printf("[NFO] ❙ %dμs\n", us)
	rep = &reqCmdRep{
		V:    v,
		Cmd:  cmd.Cmd,
		Us:   us,
		Lane: cmd.Lane,
	}

	if err != nil {
		//FIXME: is there a way to describe these failures in HAR 1.2?
		rep.Reason = fmt.Sprintf("%+v", err.Error())
		log.Printf("[NFO]\n  ▼  %s\n", rep.Reason)
		err = nil
		return
	}

	//FIXME maybe: append(headers, fmt.Sprintf("Host: %v", resp.Host))
	//FIXME: make sure order is preserved github.com/golang/go/issues/21853
	rep.HAREntry = lastHAR()
	log.Printf("[NFO]\n  ▼  %+v\n", rep.HAREntry)
	return
}

func (cmd *reqCmd) updateURL(cfg *ymlCfg) (err error) {
	URL, err := url.Parse(cmd.HARRequest.URL)
	if err != nil {
		log.Println("[ERR]", err)
		return
	}

	// TODO: if host is an IPv6 then it has to be braced with []
	URL.Host = cfg.FinalHost + ":" + cfg.FinalPort
	cmd.HARRequest.URL = URL.String()
	return
}

func (cmd *reqCmd) updateUserAgentHeader() {
	for i := range cmd.HARRequest.Headers {
		if cmd.HARRequest.Headers[i].Name == "User-Agent" {
			if strings.HasPrefix(cmd.HARRequest.Headers[i].Value, "FuzzyMonkey.co/") {
				cmd.HARRequest.Headers[i].Value = binTitle
				break
			}
		}
	}
}

func (cmd *reqCmd) updateHostHeader(cfg *ymlCfg) {
	for i := range cmd.HARRequest.Headers {
		if cmd.HARRequest.Headers[i].Name == "Host" {
			cmd.HARRequest.Headers[i].Value = cfg.FinalHost + ":" + cfg.FinalPort
			break
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"testing"

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

func TestAWSSecurityGroupRuleMigrateState(t *testing.T) {
	//   "id":"sg-4235098228", "from_port":"0", "source_security_group_id":"sg-11877275"}

	// 2015/06/16 16:04:21 terraform-provider-aws: 2015/06/16 16:04:21 [DEBUG] Attributes after migration:

	// map[string]string{"from_port":"0", "source_security_group_id":"sg-11877275", "id":"sg-3766347571", "security_group_id":"sg-13877277", "cidr_blocks.#":"0", "type":"ingress", "protocol":"-1", "self":"false", "to_port":"0"}, new id: sg-3766347571
	cases := map[string]struct {
		StateVersion int
		Attributes   map[string]string
		Expected     string
		Meta         interface{}
	}{
		"v0_1": {
			StateVersion: 0,
			Attributes: map[string]string{
				"self":                     "false",
				"to_port":                  "0",
				"security_group_id":        "sg-13877277",
				"cidr_blocks.#":            "0",
				"type":                     "ingress",
				"protocol":                 "-1",
				"id":                       "sg-4235098228",
				"from_port":                "0",
				"source_security_group_id": "sg-11877275",
			},
			Expected: "sg-3766347571",
		},
		// "v0_2": {
		// 	StateVersion: 0,
		// 	Attributes: map[string]string{
		// 		// EBS
		// 		"self": "false",
		// 	},
		// 	Expected: "sg-1235",
		// },
	}

	for tn, tc := range cases {
		is := &terraform.InstanceState{
			ID:         "sg-4235098228",
			Attributes: tc.Attributes,
		}
		is, err := resourceAwsSecurityGroupRuleMigrateState(
			tc.StateVersion, is, tc.Meta)

		if err != nil {
			t.Fatalf("bad: %s, err: %#v", tn, err)
		}

		if is.ID != tc.Expected {
			t.Fatalf("bad sg rule id: %s\n\n expected: %s", is.ID, tc.Expected)
		}
	}
}
<commit_msg>update tests with another example<commit_after>package aws

import (
	"testing"

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

func TestAWSSecurityGroupRuleMigrateState(t *testing.T) {
	//   "id":"sg-4235098228", "from_port":"0", "source_security_group_id":"sg-11877275"}

	// 2015/06/16 16:04:21 terraform-provider-aws: 2015/06/16 16:04:21 [DEBUG] Attributes after migration:

	// map[string]string{"from_port":"0", "source_security_group_id":"sg-11877275", "id":"sg-3766347571", "security_group_id":"sg-13877277", "cidr_blocks.#":"0", "type":"ingress", "protocol":"-1", "self":"false", "to_port":"0"}, new id: sg-3766347571
	cases := map[string]struct {
		StateVersion int
		ID           string
		Attributes   map[string]string
		Expected     string
		Meta         interface{}
	}{
		"v0_1": {
			StateVersion: 0,
			ID:           "sg-4235098228",
			Attributes: map[string]string{
				"self":                     "false",
				"to_port":                  "0",
				"security_group_id":        "sg-13877277",
				"cidr_blocks.#":            "0",
				"type":                     "ingress",
				"protocol":                 "-1",
				"from_port":                "0",
				"source_security_group_id": "sg-11877275",
			},
			Expected: "sg-3766347571",
		},
		"v0_2": {
			StateVersion: 0,
			ID:           "sg-1021609891",
			Attributes: map[string]string{
				"security_group_id": "sg-0981746d",
				"from_port":         "0",
				"to_port":           "0",
				"type":              "ingress",
				"self":              "false",
				"protocol":          "-1",
				"cidr_blocks.0":     "172.16.1.0/24",
				"cidr_blocks.1":     "172.16.2.0/24",
				"cidr_blocks.2":     "172.16.3.0/24",
				"cidr_blocks.3":     "172.16.4.0/24",
				"cidr_blocks.#":     "4"},
			Expected: "sg-4100229787",
		},
	}

	for tn, tc := range cases {
		is := &terraform.InstanceState{
			ID:         tc.ID,
			Attributes: tc.Attributes,
		}
		is, err := resourceAwsSecurityGroupRuleMigrateState(
			tc.StateVersion, is, tc.Meta)

		if err != nil {
			t.Fatalf("bad: %s, err: %#v", tn, err)
		}

		if is.ID != tc.Expected {
			t.Fatalf("bad sg rule id: %s\n\n expected: %s", is.ID, tc.Expected)
		}
	}
}
<|endoftext|>


<commit_before>// Package ioconn allows any combination of an io.Reader, io.Writer and io.Closer to become a net.Conn
package ioconn

import (
	"errors"
	"io"
	"net"
	"time"
)

// CloserFunc is a func that implements the io.Closer interface allowing a
// closure or other function to be io.Closer
type CloserFunc func() error

// Close simply calls the CloserFunc func
func (c CloserFunc) Close() error {
	return c()
}

// FileAddr is a net.Addr that represents a file. Should be a full path
type FileAddr string

// Network always returns "file"
func (f FileAddr) Network() string {
	return "file"
}

// String returns file://path
func (f FileAddr) String() string {
	return "file://" + string(f)
}

// Addr is a simple implementation of the net.Addr interface
type Addr struct {
	Net, Str string
}

// Network returns the Net string
func (a Addr) Network() string {
	return a.Net
}

// String returns the Str string
func (a Addr) String() string {
	return a.Str
}

// Conn implements a net.Conn
type Conn struct {
	io.Reader
	io.Writer
	io.Closer
	Local, Remote               net.Addr
	ReadDeadline, WriteDeadline time.Time
}

// Read implements the io.Reader interface
func (c *Conn) Read(p []byte) (int, error) {
	if time.Now().After(c.ReadDeadline) {
		return 0, ErrTimeout
	}
	return c.Reader.Read(p)
}

// Write implements the io.Writer interface
func (c *Conn) Write(p []byte) (int, error) {
	if time.Now().After(c.WriteDeadline) {
		return 0, ErrTimeout
	}
	return c.Writer.Write(p)
}

// LocalAddr returns the Local Address
func (c *Conn) LocalAddr() net.Addr {
	return c.Local
}

// RemoteAddr returns the Remote Address
func (c *Conn) RemoteAddr() net.Addr {
	return c.Remote
}

// SetDeadline implements the Conn SetDeadline method
func (c *Conn) SetDeadline(t time.Time) error {
	err := c.SetReadDeadline(t)
	err2 := c.SetWriteDeadline(t)
	if err != nil {
		return err
	}
	return err2
}

// SetReadDeadline implements the Conn SetReadDeadline method
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.ReadDeadline = t
	if rd, ok := c.Writer.(interface {
		SetReadDeadline(time.Time) error
	}); ok {
		return rd.SetReadDeadline(t)
	}
	return nil
}

// SetWriteDeadline implements the Conn SetWriteDeadline method
func (c *Conn) SetWriteDeadline(t time.Time) error {
	c.WriteDeadline = t
	if wd, ok := c.Writer.(interface {
		SetWriteDeadline(time.Time) error
	}); ok {
		return wd.SetWriteDeadline(t)
	}
	return nil
}

// Timeout Error
var ErrTimeout = errors.New("timeout occurred")
<commit_msg>Timeouts test for zero time<commit_after>// Package ioconn allows any combination of an io.Reader, io.Writer and io.Closer to become a net.Conn
package ioconn

import (
	"errors"
	"io"
	"net"
	"time"
)

// CloserFunc is a func that implements the io.Closer interface allowing a
// closure or other function to be io.Closer
type CloserFunc func() error

// Close simply calls the CloserFunc func
func (c CloserFunc) Close() error {
	return c()
}

// FileAddr is a net.Addr that represents a file. Should be a full path
type FileAddr string

// Network always returns "file"
func (f FileAddr) Network() string {
	return "file"
}

// String returns file://path
func (f FileAddr) String() string {
	return "file://" + string(f)
}

// Addr is a simple implementation of the net.Addr interface
type Addr struct {
	Net, Str string
}

// Network returns the Net string
func (a Addr) Network() string {
	return a.Net
}

// String returns the Str string
func (a Addr) String() string {
	return a.Str
}

// Conn implements a net.Conn
type Conn struct {
	io.Reader
	io.Writer
	io.Closer
	Local, Remote               net.Addr
	ReadDeadline, WriteDeadline time.Time
}

// Read implements the io.Reader interface
func (c *Conn) Read(p []byte) (int, error) {
	if !c.ReadDeadline.IsZero() && time.Now().After(c.ReadDeadline) {
		return 0, ErrTimeout
	}
	return c.Reader.Read(p)
}

// Write implements the io.Writer interface
func (c *Conn) Write(p []byte) (int, error) {
	if !c.ReadDeadline.IsZero() && time.Now().After(c.WriteDeadline) {
		return 0, ErrTimeout
	}
	return c.Writer.Write(p)
}

// LocalAddr returns the Local Address
func (c *Conn) LocalAddr() net.Addr {
	return c.Local
}

// RemoteAddr returns the Remote Address
func (c *Conn) RemoteAddr() net.Addr {
	return c.Remote
}

// SetDeadline implements the Conn SetDeadline method
func (c *Conn) SetDeadline(t time.Time) error {
	err := c.SetReadDeadline(t)
	err2 := c.SetWriteDeadline(t)
	if err != nil {
		return err
	}
	return err2
}

// SetReadDeadline implements the Conn SetReadDeadline method
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.ReadDeadline = t
	if rd, ok := c.Writer.(interface {
		SetReadDeadline(time.Time) error
	}); ok {
		return rd.SetReadDeadline(t)
	}
	return nil
}

// SetWriteDeadline implements the Conn SetWriteDeadline method
func (c *Conn) SetWriteDeadline(t time.Time) error {
	c.WriteDeadline = t
	if wd, ok := c.Writer.(interface {
		SetWriteDeadline(time.Time) error
	}); ok {
		return wd.SetWriteDeadline(t)
	}
	return nil
}

// Timeout Error
var ErrTimeout = errors.New("timeout occurred")
<|endoftext|>


<commit_before>package venom

import (
	"io"
)

type nopReadCloser struct {
	io.Reader
}

func (nopReadCloser) Close() error { return nil }

// NopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided Reader r.
func NopReadCloser(r io.Reader) io.ReadCloser {
	return nopReadCloser{r}
}

type nopWriteCloser struct {
	io.Writer
}

func (nopWriteCloser) Close() error { return nil }

// NopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided Writer w.
func NopWriteCloser(w io.Writer) io.WriteCloser {
	return nopWriteCloser{w}
}
<commit_msg>Fix typo<commit_after>package venom

import (
	"io"
)

type nopReadCloser struct {
	io.Reader
}

func (nopReadCloser) Close() error { return nil }

// NopCloser returns a ReadCloser with a no-op Close method wrapping
// the provided Reader r.
func NopReadCloser(r io.Reader) io.ReadCloser {
	return nopReadCloser{r}
}

type nopWriteCloser struct {
	io.Writer
}

func (nopWriteCloser) Close() error { return nil }

// NopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided Writer w.
func NopWriteCloser(w io.Writer) io.WriteCloser {
	return nopWriteCloser{w}
}
<|endoftext|>