<commit_before>package deploy

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	yaml "gopkg.in/yaml.v2"

	"github.com/ViBiOh/auth/pkg/auth"
	"github.com/ViBiOh/auth/pkg/model"
	"github.com/ViBiOh/dashboard/pkg/commons"
	"github.com/ViBiOh/dashboard/pkg/docker"
	"github.com/ViBiOh/httputils/pkg/httperror"
	"github.com/ViBiOh/httputils/pkg/httpjson"
	"github.com/ViBiOh/httputils/pkg/request"
	"github.com/ViBiOh/httputils/pkg/rollbar"
	"github.com/ViBiOh/httputils/pkg/tools"
	"github.com/ViBiOh/mailer/pkg/client"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	opentracing "github.com/opentracing/opentracing-go"
)

const (
	// DeployTimeout indicates delay for application to deploy before rollback
	DeployTimeout = 3 * time.Minute

	defaultCPUShares = 128
	minMemory        = 16777216
	maxMemory        = 805306368
	colonSeparator   = `:`
	deploySuffix     = `_deploy`
)

// App stores informations
type App struct {
	tasks         sync.Map
	dockerApp     *docker.App
	authApp       *auth.App
	network       string
	tag           string
	containerUser string
	appURL        string
	notification  string
	mailerApp     *client.App
}

// NewApp creates new App from Flags' config
func NewApp(config map[string]*string, authApp *auth.App, dockerApp *docker.App, mailerApp *client.App) *App {
	return &App{
		tasks:         sync.Map{},
		dockerApp:     dockerApp,
		authApp:       authApp,
		mailerApp:     mailerApp,
		network:       strings.TrimSpace(*config[`network`]),
		tag:           strings.TrimSpace(*config[`tag`]),
		containerUser: strings.TrimSpace(*config[`containerUser`]),
		appURL:        strings.TrimSpace(*config[`appURL`]),
		notification:  strings.TrimSpace(*config[`notification`]),
	}
}

// Flags adds flags for given prefix
func Flags(prefix string) map[string]*string {
	return map[string]*string{
		`network`:       flag.String(tools.ToCamel(fmt.Sprintf(`%sNetwork`, prefix)), `traefik`, `[deploy] Default Network`),
		`tag`:           flag.String(tools.ToCamel(fmt.Sprintf(`%sTag`, prefix)), `latest`, `[deploy] Default image tag)`),
		`containerUser`: flag.String(tools.ToCamel(fmt.Sprintf(`%sContainerUser`, prefix)), `1000`, `[deploy] Default container user`),
		`appURL`:        flag.String(tools.ToCamel(fmt.Sprintf(`%sAppURL`, prefix)), `https://dashboard.vibioh.fr`, `[deploy] Application web URL`),
		`notification`:  flag.String(tools.ToCamel(fmt.Sprintf(`%sNotification`, prefix)), `onError`, `[deploy] Send email notification when deploy ends (possibles values ares "never", "onError", "all")`),
	}
}

// CanBeGracefullyClosed indicates if application can terminate safely
func (a *App) CanBeGracefullyClosed() (canBe bool) {
	canBe = true

	a.tasks.Range(func(_ interface{}, value interface{}) bool {
		canBe = !value.(bool)
		return canBe
	})

	return
}

func (a *App) pullImage(ctx context.Context, image string) error {
	if !strings.Contains(image, colonSeparator) {
		image = fmt.Sprintf(`%s%slatest`, image, colonSeparator)
	}

	pull, err := a.dockerApp.Docker.ImagePull(ctx, image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	_, err = request.ReadBody(pull)
	return err
}

func (a *App) cleanContainers(ctx context.Context, containers []types.Container) error {
	for _, container := range containers {
		if _, err := a.dockerApp.GracefulStopContainer(ctx, container.ID, time.Minute); err != nil {
			rollbar.LogError(`Error while stopping container %s: %v`, container.Names, err)
		}
	}

	for _, container := range containers {
		if _, err := a.dockerApp.RmContainer(ctx, container.ID, nil, false); err != nil {
			return fmt.Errorf(`Error while deleting container %s: %v`, container.Names, err)
		}
	}

	return nil
}

func (a *App) renameDeployedContainers(ctx context.Context, services map[string]*deployedService) error {
	for _, service := range services {
		if err := a.dockerApp.Docker.ContainerRename(ctx, service.ContainerID, getFinalName(service.FullName)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service.Name, err)
		}
	}

	return nil
}

func (a *App) deleteServices(ctx context.Context, appName string, services map[string]*deployedService, user *model.User) {
	for _, service := range services {
		infos, err := a.dockerApp.InspectContainer(ctx, service.ContainerID)
		if err != nil {
			rollbar.LogError(`[%s] [%s] Error while inspecting service %s: %v`, user.Username, appName, service.Name, err)
		} else {
			if _, err := a.dockerApp.StopContainer(ctx, service.ContainerID, infos); err != nil {
				rollbar.LogError(`[%s] [%s] Error while stopping service %s: %v`, user.Username, appName, service.Name, err)
			}

			if _, err := a.dockerApp.RmContainer(ctx, service.ContainerID, infos, true); err != nil {
				rollbar.LogError(`[%s] [%s] Error while deleting service %s: %v`, user.Username, appName, service.Name, err)
			}
		}
	}
}

func (a *App) startServices(ctx context.Context, services map[string]*deployedService) error {
	for _, service := range services {
		if _, err := a.dockerApp.StartContainer(ctx, service.ContainerID, nil); err != nil {
			return fmt.Errorf(`Error while starting service %s: %v`, service.Name, err)
		}
	}

	return nil
}

func (a *App) inspectServices(ctx context.Context, services map[string]*deployedService, user *model.User, appName string) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for _, service := range services {
		infos, err := a.dockerApp.InspectContainer(ctx, service.ContainerID)
		if err != nil {
			rollbar.LogError(`[%s] [%s] Error while inspecting container %s: %v`, user.Username, appName, service.Name, err)
		} else {
			containers = append(containers, infos)
		}
	}

	return containers
}

func (a *App) areContainersHealthy(ctx context.Context, user *model.User, appName string, services map[string]*deployedService) bool {
	containersServices := a.inspectServices(ctx, services, user, appName)
	containersIdsWithHealthcheck := commons.GetContainersIDs(commons.FilterContainers(containersServices, hasHealthcheck))

	if len(containersIdsWithHealthcheck) == 0 {
		return true
	}

	for _, id := range containersIdsWithHealthcheck {
		if service := findServiceByContainerID(services, id); service != nil {
			service.State = `unhealthy`
		}
	}

	filtersArgs := filters.NewArgs()
	healthyStatusFilters(&filtersArgs, containersIdsWithHealthcheck)

	timeoutCtx, cancel := context.WithTimeout(ctx, DeployTimeout)
	defer cancel()

	messages, errors := a.dockerApp.Docker.Events(timeoutCtx, types.EventsOptions{Filters: filtersArgs})
	healthyContainers := make(map[string]bool, len(containersIdsWithHealthcheck))

	for {
		select {
		case <-ctx.Done():
			return false
		case message := <-messages:
			if service := findServiceByContainerID(services, message.ID); service != nil {
				service.State = `healthy`
			}

			healthyContainers[message.ID] = true
			if len(healthyContainers) == len(containersIdsWithHealthcheck) {
				return true
			}
		case err := <-errors:
			rollbar.LogError(`[%s] [%s] Error while reading healthy events: %v`, user.Username, appName, err)
			return false
		}
	}
}

func (a *App) finishDeploy(ctx context.Context, user *model.User, appName string, services map[string]*deployedService, oldContainers []types.Container, requestParams url.Values) {
	span := opentracing.SpanFromContext(ctx)
	span.SetTag(`app`, appName)
	span.SetTag(`services_count`, len(services))
	defer func() {
		defer a.tasks.Delete(appName)
		defer span.Finish()
	}()

	success := a.areContainersHealthy(ctx, user, appName, services)
	a.captureServicesOutput(ctx, user, appName, services)

	if success {
		if err := a.cleanContainers(ctx, oldContainers); err != nil {
			rollbar.LogError(`[%s] [%s] Error while cleaning old containers: %v`, user.Username, appName, err)
		}

		if err := a.renameDeployedContainers(ctx, services); err != nil {
			rollbar.LogError(`[%s] [%s] Error while renaming deployed containers: %v`, user.Username, appName, err)
		}
	} else {
		rollbar.LogWarning(`[%s] [%s] Failed to deploy: %v`, user.Username, appName, errHealthCheckFailed)
		a.deleteServices(ctx, appName, services, user)
	}

	if !success {
		for _, service := range services {
			log.Printf("[%s] [%s] Logs output for %s: \n%s\n", user.Username, appName, service.Name, strings.Join(service.Logs, "\n"))
			log.Printf("[%s] [%s] Health output for %s: \n%s\n", user.Username, appName, service.Name, strings.Join(service.HealthLogs, "\n"))
		}
	}

	if err := a.sendEmailNotification(ctx, user, appName, services, success); err != nil {
		rollbar.LogError(`[%s] [%s] Error while sending email notification: %s`, user.Username, appName, err)
	}

	if err := a.sendRollbarNotification(ctx, user, requestParams); err != nil {
		rollbar.LogError(`[%s] [%s] Error while sending rollbar notification: %s`, user.Username, appName, err)
	}
}

func (a *App) createContainer(ctx context.Context, user *model.User, appName string, serviceName string, service *dockerComposeService) (*deployedService, error) {
	imagePulled := false

	if a.tag != `` {
		imageOverride := fmt.Sprintf(`%s%s%s`, service.Image, colonSeparator, a.tag)
		if err := a.pullImage(ctx, imageOverride); err == nil {
			service.Image = imageOverride
			imagePulled = true
		}
	}

	if !imagePulled {
		if err := a.pullImage(ctx, service.Image); err != nil {
			return nil, err
		}
	}

	serviceFullName := getServiceFullName(appName, serviceName)

	config, err := a.getConfig(service, user, appName)
	if err != nil {
		return nil, fmt.Errorf(`Error while getting config: %v`, err)
	}

	createdContainer, err := a.dockerApp.Docker.ContainerCreate(ctx, config, a.getHostConfig(service, user), a.getNetworkConfig(serviceName, service), serviceFullName)
	if err != nil {
		return nil, fmt.Errorf(`Error while creating service %s: %v`, serviceName, err)
	}

	return &deployedService{
		Name:        serviceName,
		FullName:    serviceFullName,
		ContainerID: createdContainer.ID,
		ImageName:   service.Image,
	}, nil
}

func (a *App) parseCompose(ctx context.Context, user *model.User, appName string, composeFile []byte) (map[string]*deployedService, error) {
	composeFile = bytes.Replace(composeFile, []byte(`$$`), []byte(`$`), -1)

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		return nil, fmt.Errorf(`[%s] [%s] Error while unmarshalling compose file: %v`, user.Username, appName, err)
	}

	newServices := make(map[string]*deployedService)
	for serviceName, service := range compose.Services {
		if deployedService, err := a.createContainer(ctx, user, appName, serviceName, &service); err != nil {
			break
		} else {
			newServices[serviceName] = deployedService
		}
	}

	return newServices, nil
}

func composeFailed(w http.ResponseWriter, user *model.User, appName string, err error) {
	httperror.InternalServerError(w, fmt.Errorf(`[%s] [%s] Failed to deploy: %v`, user.Username, appName, err))
}

func (a *App) composeHandler(w http.ResponseWriter, r *http.Request, user *model.User) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	appName, composeFile, err := checkParams(r, user)
	if err != nil {
		httperror.BadRequest(w, err)
		return
	}

	ctx := r.Context()

	oldContainers, err := a.checkRights(ctx, user, appName)
	if err != nil {
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	newServices, err := a.parseCompose(ctx, user, appName, composeFile)
	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if err = a.checkTasks(user, appName); err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if err == nil {
		err = a.startServices(ctx, newServices)
	}

	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	ctx = context.Background()
	parentSpanContext := opentracing.SpanFromContext(r.Context()).Context()
	_, ctx = opentracing.StartSpanFromContext(ctx, `Deploy`, opentracing.FollowsFrom(parentSpanContext))

	go a.finishDeploy(ctx, user, appName, newServices, oldContainers, r.URL.Query())

	if err := httpjson.ResponseArrayJSON(w, http.StatusOK, newServices, httpjson.IsPretty(r.URL.RawQuery)); err != nil {
		httperror.InternalServerError(w, err)
	}
}

// Handler for request. Should be use with net/http
func (a *App) Handler() http.Handler {
	return a.authApp.Handler(a.composeHandler)
}
<commit_msg>Restoring clean of deploy failed on success<commit_after>package deploy

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	yaml "gopkg.in/yaml.v2"

	"github.com/ViBiOh/auth/pkg/auth"
	"github.com/ViBiOh/auth/pkg/model"
	"github.com/ViBiOh/dashboard/pkg/commons"
	"github.com/ViBiOh/dashboard/pkg/docker"
	"github.com/ViBiOh/httputils/pkg/httperror"
	"github.com/ViBiOh/httputils/pkg/httpjson"
	"github.com/ViBiOh/httputils/pkg/request"
	"github.com/ViBiOh/httputils/pkg/rollbar"
	"github.com/ViBiOh/httputils/pkg/tools"
	"github.com/ViBiOh/mailer/pkg/client"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	opentracing "github.com/opentracing/opentracing-go"
)

const (
	// DeployTimeout indicates delay for application to deploy before rollback
	DeployTimeout = 3 * time.Minute

	defaultCPUShares = 128
	minMemory        = 16777216
	maxMemory        = 805306368
	colonSeparator   = `:`
	deploySuffix     = `_deploy`
)

// App stores informations
type App struct {
	tasks         sync.Map
	dockerApp     *docker.App
	authApp       *auth.App
	network       string
	tag           string
	containerUser string
	appURL        string
	notification  string
	mailerApp     *client.App
}

// NewApp creates new App from Flags' config
func NewApp(config map[string]*string, authApp *auth.App, dockerApp *docker.App, mailerApp *client.App) *App {
	return &App{
		tasks:         sync.Map{},
		dockerApp:     dockerApp,
		authApp:       authApp,
		mailerApp:     mailerApp,
		network:       strings.TrimSpace(*config[`network`]),
		tag:           strings.TrimSpace(*config[`tag`]),
		containerUser: strings.TrimSpace(*config[`containerUser`]),
		appURL:        strings.TrimSpace(*config[`appURL`]),
		notification:  strings.TrimSpace(*config[`notification`]),
	}
}

// Flags adds flags for given prefix
func Flags(prefix string) map[string]*string {
	return map[string]*string{
		`network`:       flag.String(tools.ToCamel(fmt.Sprintf(`%sNetwork`, prefix)), `traefik`, `[deploy] Default Network`),
		`tag`:           flag.String(tools.ToCamel(fmt.Sprintf(`%sTag`, prefix)), `latest`, `[deploy] Default image tag)`),
		`containerUser`: flag.String(tools.ToCamel(fmt.Sprintf(`%sContainerUser`, prefix)), `1000`, `[deploy] Default container user`),
		`appURL`:        flag.String(tools.ToCamel(fmt.Sprintf(`%sAppURL`, prefix)), `https://dashboard.vibioh.fr`, `[deploy] Application web URL`),
		`notification`:  flag.String(tools.ToCamel(fmt.Sprintf(`%sNotification`, prefix)), `onError`, `[deploy] Send email notification when deploy ends (possibles values ares "never", "onError", "all")`),
	}
}

// CanBeGracefullyClosed indicates if application can terminate safely
func (a *App) CanBeGracefullyClosed() (canBe bool) {
	canBe = true

	a.tasks.Range(func(_ interface{}, value interface{}) bool {
		canBe = !value.(bool)
		return canBe
	})

	return
}

func (a *App) pullImage(ctx context.Context, image string) error {
	if !strings.Contains(image, colonSeparator) {
		image = fmt.Sprintf(`%s%slatest`, image, colonSeparator)
	}

	pull, err := a.dockerApp.Docker.ImagePull(ctx, image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	_, err = request.ReadBody(pull)
	return err
}

func (a *App) cleanContainers(ctx context.Context, containers []types.Container) error {
	for _, container := range containers {
		if _, err := a.dockerApp.GracefulStopContainer(ctx, container.ID, time.Minute); err != nil {
			rollbar.LogError(`Error while stopping container %s: %v`, container.Names, err)
		}
	}

	for _, container := range containers {
		if _, err := a.dockerApp.RmContainer(ctx, container.ID, nil, false); err != nil {
			return fmt.Errorf(`Error while deleting container %s: %v`, container.Names, err)
		}
	}

	return nil
}

func (a *App) renameDeployedContainers(ctx context.Context, services map[string]*deployedService) error {
	for _, service := range services {
		if err := a.dockerApp.Docker.ContainerRename(ctx, service.ContainerID, getFinalName(service.FullName)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service.Name, err)
		}
	}

	return nil
}

func (a *App) deleteServices(ctx context.Context, appName string, services map[string]*deployedService, user *model.User) {
	for _, service := range services {
		infos, err := a.dockerApp.InspectContainer(ctx, service.ContainerID)
		if err != nil {
			rollbar.LogError(`[%s] [%s] Error while inspecting service %s: %v`, user.Username, appName, service.Name, err)
		} else {
			if _, err := a.dockerApp.StopContainer(ctx, service.ContainerID, infos); err != nil {
				rollbar.LogError(`[%s] [%s] Error while stopping service %s: %v`, user.Username, appName, service.Name, err)
			}

			if _, err := a.dockerApp.RmContainer(ctx, service.ContainerID, infos, true); err != nil {
				rollbar.LogError(`[%s] [%s] Error while deleting service %s: %v`, user.Username, appName, service.Name, err)
			}
		}
	}
}

func (a *App) startServices(ctx context.Context, services map[string]*deployedService) error {
	for _, service := range services {
		if _, err := a.dockerApp.StartContainer(ctx, service.ContainerID, nil); err != nil {
			return fmt.Errorf(`Error while starting service %s: %v`, service.Name, err)
		}
	}

	return nil
}

func (a *App) inspectServices(ctx context.Context, services map[string]*deployedService, user *model.User, appName string) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for _, service := range services {
		infos, err := a.dockerApp.InspectContainer(ctx, service.ContainerID)
		if err != nil {
			rollbar.LogError(`[%s] [%s] Error while inspecting container %s: %v`, user.Username, appName, service.Name, err)
		} else {
			containers = append(containers, infos)
		}
	}

	return containers
}

func (a *App) areContainersHealthy(ctx context.Context, user *model.User, appName string, services map[string]*deployedService) bool {
	containersServices := a.inspectServices(ctx, services, user, appName)
	containersIdsWithHealthcheck := commons.GetContainersIDs(commons.FilterContainers(containersServices, hasHealthcheck))

	if len(containersIdsWithHealthcheck) == 0 {
		return true
	}

	for _, id := range containersIdsWithHealthcheck {
		if service := findServiceByContainerID(services, id); service != nil {
			service.State = `unhealthy`
		}
	}

	filtersArgs := filters.NewArgs()
	healthyStatusFilters(&filtersArgs, containersIdsWithHealthcheck)

	timeoutCtx, cancel := context.WithTimeout(ctx, DeployTimeout)
	defer cancel()

	messages, errors := a.dockerApp.Docker.Events(timeoutCtx, types.EventsOptions{Filters: filtersArgs})
	healthyContainers := make(map[string]bool, len(containersIdsWithHealthcheck))

	for {
		select {
		case <-ctx.Done():
			return false
		case message := <-messages:
			if service := findServiceByContainerID(services, message.ID); service != nil {
				service.State = `healthy`
			}

			healthyContainers[message.ID] = true
			if len(healthyContainers) == len(containersIdsWithHealthcheck) {
				return true
			}
		case err := <-errors:
			rollbar.LogError(`[%s] [%s] Error while reading healthy events: %v`, user.Username, appName, err)
			return false
		}
	}
}

func (a *App) finishDeploy(ctx context.Context, user *model.User, appName string, services map[string]*deployedService, oldContainers []types.Container, requestParams url.Values) {
	span := opentracing.SpanFromContext(ctx)
	span.SetTag(`app`, appName)
	span.SetTag(`services_count`, len(services))
	defer func() {
		defer a.tasks.Delete(appName)
		defer span.Finish()
	}()

	success := a.areContainersHealthy(ctx, user, appName, services)
	a.captureServicesOutput(ctx, user, appName, services)

	if success {
		if err := a.cleanContainers(ctx, oldContainers); err != nil {
			rollbar.LogError(`[%s] [%s] Error while cleaning old containers: %v`, user.Username, appName, err)
		}

		if err := a.renameDeployedContainers(ctx, services); err != nil {
			rollbar.LogError(`[%s] [%s] Error while renaming deployed containers: %v`, user.Username, appName, err)
		}
	} else {
		rollbar.LogWarning(`[%s] [%s] Failed to deploy: %v`, user.Username, appName, errHealthCheckFailed)
		a.deleteServices(ctx, appName, services, user)
	}

	if !success {
		for _, service := range services {
			log.Printf("[%s] [%s] Logs output for %s: \n%s\n", user.Username, appName, service.Name, strings.Join(service.Logs, "\n"))
			log.Printf("[%s] [%s] Health output for %s: \n%s\n", user.Username, appName, service.Name, strings.Join(service.HealthLogs, "\n"))
		}
	}

	if err := a.sendEmailNotification(ctx, user, appName, services, success); err != nil {
		rollbar.LogError(`[%s] [%s] Error while sending email notification: %s`, user.Username, appName, err)
	}

	if err := a.sendRollbarNotification(ctx, user, requestParams); err != nil {
		rollbar.LogError(`[%s] [%s] Error while sending rollbar notification: %s`, user.Username, appName, err)
	}
}

func (a *App) createContainer(ctx context.Context, user *model.User, appName string, serviceName string, service *dockerComposeService) (*deployedService, error) {
	imagePulled := false

	if a.tag != `` {
		imageOverride := fmt.Sprintf(`%s%s%s`, service.Image, colonSeparator, a.tag)
		if err := a.pullImage(ctx, imageOverride); err == nil {
			service.Image = imageOverride
			imagePulled = true
		}
	}

	if !imagePulled {
		if err := a.pullImage(ctx, service.Image); err != nil {
			return nil, err
		}
	}

	serviceFullName := getServiceFullName(appName, serviceName)

	config, err := a.getConfig(service, user, appName)
	if err != nil {
		return nil, fmt.Errorf(`Error while getting config: %v`, err)
	}

	createdContainer, err := a.dockerApp.Docker.ContainerCreate(ctx, config, a.getHostConfig(service, user), a.getNetworkConfig(serviceName, service), serviceFullName)
	if err != nil {
		return nil, fmt.Errorf(`Error while creating service %s: %v`, serviceName, err)
	}

	return &deployedService{
		Name:        serviceName,
		FullName:    serviceFullName,
		ContainerID: createdContainer.ID,
		ImageName:   service.Image,
	}, nil
}

func (a *App) parseCompose(ctx context.Context, user *model.User, appName string, composeFile []byte) (map[string]*deployedService, error) {
	composeFile = bytes.Replace(composeFile, []byte(`$$`), []byte(`$`), -1)

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		return nil, fmt.Errorf(`[%s] [%s] Error while unmarshalling compose file: %v`, user.Username, appName, err)
	}

	newServices := make(map[string]*deployedService)
	for serviceName, service := range compose.Services {
		if deployedService, err := a.createContainer(ctx, user, appName, serviceName, &service); err != nil {
			break
		} else {
			newServices[serviceName] = deployedService
		}
	}

	return newServices, nil
}

func composeFailed(w http.ResponseWriter, user *model.User, appName string, err error) {
	httperror.InternalServerError(w, fmt.Errorf(`[%s] [%s] Failed to deploy: %v`, user.Username, appName, err))
}

func (a *App) composeHandler(w http.ResponseWriter, r *http.Request, user *model.User) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	appName, composeFile, err := checkParams(r, user)
	if err != nil {
		httperror.BadRequest(w, err)
		return
	}

	ctx := r.Context()

	oldContainers, err := a.checkRights(ctx, user, appName)
	if err != nil {
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	newServices, err := a.parseCompose(ctx, user, appName, composeFile)
	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if err = a.checkTasks(user, appName); err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if err == nil {
		err = a.startServices(ctx, newServices)
	}

	ctx = context.Background()
	parentSpanContext := opentracing.SpanFromContext(r.Context()).Context()
	_, ctx = opentracing.StartSpanFromContext(ctx, `Deploy`, opentracing.FollowsFrom(parentSpanContext))

	go a.finishDeploy(ctx, user, appName, newServices, oldContainers, r.URL.Query())

	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if err := httpjson.ResponseArrayJSON(w, http.StatusOK, newServices, httpjson.IsPretty(r.URL.RawQuery)); err != nil {
		httperror.InternalServerError(w, err)
	}
}

// Handler for request. Should be use with net/http
func (a *App) Handler() http.Handler {
	return a.authApp.Handler(a.composeHandler)
}
<|endoftext|>


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

// Package quick implements utility functions to help with black box testing.
package quick

import (
	"flag"
	"fmt"
	"math"
	"math/rand"
	"reflect"
	"strings"
)

var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")

// A Generator can generate random values of its own type.
type Generator interface {
	// Generate returns a random instance of the type on which it is a
	// method using the size as a size hint.
	Generate(rand *rand.Rand, size int) reflect.Value
}

// randFloat32 generates a random float taking the full range of a float32.
func randFloat32(rand *rand.Rand) float32 {
	f := rand.Float64() * math.MaxFloat32
	if rand.Int()&1 == 1 {
		f = -f
	}
	return float32(f)
}

// randFloat64 generates a random float taking the full range of a float64.
func randFloat64(rand *rand.Rand) float64 {
	f := rand.Float64() * math.MaxFloat64
	if rand.Int()&1 == 1 {
		f = -f
	}
	return f
}

// randInt64 returns a random integer taking half the range of an int64.
func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 }

// complexSize is the maximum length of arbitrary values that contain other
// values.
const complexSize = 50

// Value returns an arbitrary value of the given type.
// If the type implements the Generator interface, that will be used.
// Note: To create arbitrary values for structs, all the fields must be exported.
func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
	if m, ok := reflect.Zero(t).Interface().(Generator); ok {
		return m.Generate(rand, complexSize), true
	}

	v := reflect.New(t).Elem()
	switch concrete := t; concrete.Kind() {
	case reflect.Bool:
		v.SetBool(rand.Int()&1 == 0)
	case reflect.Float32:
		v.SetFloat(float64(randFloat32(rand)))
	case reflect.Float64:
		v.SetFloat(randFloat64(rand))
	case reflect.Complex64:
		v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand))))
	case reflect.Complex128:
		v.SetComplex(complex(randFloat64(rand), randFloat64(rand)))
	case reflect.Int16:
		v.SetInt(randInt64(rand))
	case reflect.Int32:
		v.SetInt(randInt64(rand))
	case reflect.Int64:
		v.SetInt(randInt64(rand))
	case reflect.Int8:
		v.SetInt(randInt64(rand))
	case reflect.Int:
		v.SetInt(randInt64(rand))
	case reflect.Uint16:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint32:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint64:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint8:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uintptr:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Map:
		numElems := rand.Intn(complexSize)
		v.Set(reflect.MakeMap(concrete))
		for i := 0; i < numElems; i++ {
			key, ok1 := Value(concrete.Key(), rand)
			value, ok2 := Value(concrete.Elem(), rand)
			if !ok1 || !ok2 {
				return reflect.Value{}, false
			}
			v.SetMapIndex(key, value)
		}
	case reflect.Ptr:
		elem, ok := Value(concrete.Elem(), rand)
		if !ok {
			return reflect.Value{}, false
		}
		v.Set(reflect.New(concrete.Elem()))
		v.Elem().Set(elem)
	case reflect.Slice:
		numElems := rand.Intn(complexSize)
		v.Set(reflect.MakeSlice(concrete, numElems, numElems))
		for i := 0; i < numElems; i++ {
			elem, ok := Value(concrete.Elem(), rand)
			if !ok {
				return reflect.Value{}, false
			}
			v.Index(i).Set(elem)
		}
	case reflect.String:
		numChars := rand.Intn(complexSize)
		codePoints := make([]rune, numChars)
		for i := 0; i < numChars; i++ {
			codePoints[i] = rune(rand.Intn(0x10ffff))
		}
		v.SetString(string(codePoints))
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			elem, ok := Value(concrete.Field(i).Type, rand)
			if !ok {
				return reflect.Value{}, false
			}
			v.Field(i).Set(elem)
		}
	default:
		return reflect.Value{}, false
	}

	return v, true
}

// A Config structure contains options for running a test.
type Config struct {
	// MaxCount sets the maximum number of iterations. If zero,
	// MaxCountScale is used.
	MaxCount int
	// MaxCountScale is a non-negative scale factor applied to the default
	// maximum. If zero, the default is unchanged.
	MaxCountScale float64
	// If non-nil, rand is a source of random numbers. Otherwise a default
	// pseudo-random source will be used.
	Rand *rand.Rand
	// If non-nil, the Values function generates a slice of arbitrary
	// reflect.Values that are congruent with the arguments to the function
	// being tested. Otherwise, the top-level Values function is used
	// to generate them.
	Values func([]reflect.Value, *rand.Rand)
}

var defaultConfig Config

// getRand returns the *rand.Rand to use for a given Config.
func (c *Config) getRand() *rand.Rand {
	if c.Rand == nil {
		return rand.New(rand.NewSource(0))
	}
	return c.Rand
}

// getMaxCount returns the maximum number of iterations to run for a given
// Config.
func (c *Config) getMaxCount() (maxCount int) {
	maxCount = c.MaxCount
	if maxCount == 0 {
		if c.MaxCountScale != 0 {
			maxCount = int(c.MaxCountScale * float64(*defaultMaxCount))
		} else {
			maxCount = *defaultMaxCount
		}
	}

	return
}

// A SetupError is the result of an error in the way that check is being
// used, independent of the functions being tested.
type SetupError string

func (s SetupError) Error() string { return string(s) }

// A CheckError is the result of Check finding an error.
type CheckError struct {
	Count int
	In    []interface{}
}

func (s *CheckError) Error() string {
	return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In))
}

// A CheckEqualError is the result CheckEqual finding an error.
type CheckEqualError struct {
	CheckError
	Out1 []interface{}
	Out2 []interface{}
}

func (s *CheckEqualError) Error() string {
	return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2))
}

// Check looks for an input to f, any function that returns bool,
// such that f returns false.  It calls f repeatedly, with arbitrary
// values for each argument.  If f returns false on a given input,
// Check returns that input as a *CheckError.
// For example:
//
// 	func TestOddMultipleOfThree(t *testing.T) {
// 		f := func(x int) bool {
// 			y := OddMultipleOfThree(x)
// 			return y%2 == 1 && y%3 == 0
// 		}
// 		if err := quick.Check(f, nil); err != nil {
// 			t.Error(err)
// 		}
// 	}
func Check(function interface{}, config *Config) (err error) {
	if config == nil {
		config = &defaultConfig
	}

	f, fType, ok := functionAndType(function)
	if !ok {
		err = SetupError("argument is not a function")
		return
	}

	if fType.NumOut() != 1 {
		err = SetupError("function returns more than one value.")
		return
	}
	if fType.Out(0).Kind() != reflect.Bool {
		err = SetupError("function does not return a bool")
		return
	}

	arguments := make([]reflect.Value, fType.NumIn())
	rand := config.getRand()
	maxCount := config.getMaxCount()

	for i := 0; i < maxCount; i++ {
		err = arbitraryValues(arguments, fType, config, rand)
		if err != nil {
			return
		}

		if !f.Call(arguments)[0].Bool() {
			err = &CheckError{i + 1, toInterfaces(arguments)}
			return
		}
	}

	return
}

// CheckEqual looks for an input on which f and g return different results.
// It calls f and g repeatedly with arbitrary values for each argument.
// If f and g return different answers, CheckEqual returns a *CheckEqualError
// describing the input and the outputs.
func CheckEqual(f, g interface{}, config *Config) (err error) {
	if config == nil {
		config = &defaultConfig
	}

	x, xType, ok := functionAndType(f)
	if !ok {
		err = SetupError("f is not a function")
		return
	}
	y, yType, ok := functionAndType(g)
	if !ok {
		err = SetupError("g is not a function")
		return
	}

	if xType != yType {
		err = SetupError("functions have different types")
		return
	}

	arguments := make([]reflect.Value, xType.NumIn())
	rand := config.getRand()
	maxCount := config.getMaxCount()

	for i := 0; i < maxCount; i++ {
		err = arbitraryValues(arguments, xType, config, rand)
		if err != nil {
			return
		}

		xOut := toInterfaces(x.Call(arguments))
		yOut := toInterfaces(y.Call(arguments))

		if !reflect.DeepEqual(xOut, yOut) {
			err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut}
			return
		}
	}

	return
}

// arbitraryValues writes Values to args such that args contains Values
// suitable for calling f.
func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err error) {
	if config.Values != nil {
		config.Values(args, rand)
		return
	}

	for j := 0; j < len(args); j++ {
		var ok bool
		args[j], ok = Value(f.In(j), rand)
		if !ok {
			err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j))
			return
		}
	}

	return
}

func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) {
	v = reflect.ValueOf(f)
	ok = v.Kind() == reflect.Func
	if !ok {
		return
	}
	t = v.Type()
	return
}

func toInterfaces(values []reflect.Value) []interface{} {
	ret := make([]interface{}, len(values))
	for i, v := range values {
		ret[i] = v.Interface()
	}
	return ret
}

func toString(interfaces []interface{}) string {
	s := make([]string, len(interfaces))
	for i, v := range interfaces {
		s[i] = fmt.Sprintf("%#v", v)
	}
	return strings.Join(s, ", ")
}
<commit_msg>testing/quick: brought Check parameter name in line with function doc<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package quick implements utility functions to help with black box testing.
package quick

import (
	"flag"
	"fmt"
	"math"
	"math/rand"
	"reflect"
	"strings"
)

var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")

// A Generator can generate random values of its own type.
type Generator interface {
	// Generate returns a random instance of the type on which it is a
	// method using the size as a size hint.
	Generate(rand *rand.Rand, size int) reflect.Value
}

// randFloat32 generates a random float taking the full range of a float32.
func randFloat32(rand *rand.Rand) float32 {
	f := rand.Float64() * math.MaxFloat32
	if rand.Int()&1 == 1 {
		f = -f
	}
	return float32(f)
}

// randFloat64 generates a random float taking the full range of a float64.
func randFloat64(rand *rand.Rand) float64 {
	f := rand.Float64() * math.MaxFloat64
	if rand.Int()&1 == 1 {
		f = -f
	}
	return f
}

// randInt64 returns a random integer taking half the range of an int64.
func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 }

// complexSize is the maximum length of arbitrary values that contain other
// values.
const complexSize = 50

// Value returns an arbitrary value of the given type.
// If the type implements the Generator interface, that will be used.
// Note: To create arbitrary values for structs, all the fields must be exported.
func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
	if m, ok := reflect.Zero(t).Interface().(Generator); ok {
		return m.Generate(rand, complexSize), true
	}

	v := reflect.New(t).Elem()
	switch concrete := t; concrete.Kind() {
	case reflect.Bool:
		v.SetBool(rand.Int()&1 == 0)
	case reflect.Float32:
		v.SetFloat(float64(randFloat32(rand)))
	case reflect.Float64:
		v.SetFloat(randFloat64(rand))
	case reflect.Complex64:
		v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand))))
	case reflect.Complex128:
		v.SetComplex(complex(randFloat64(rand), randFloat64(rand)))
	case reflect.Int16:
		v.SetInt(randInt64(rand))
	case reflect.Int32:
		v.SetInt(randInt64(rand))
	case reflect.Int64:
		v.SetInt(randInt64(rand))
	case reflect.Int8:
		v.SetInt(randInt64(rand))
	case reflect.Int:
		v.SetInt(randInt64(rand))
	case reflect.Uint16:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint32:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint64:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint8:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uint:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Uintptr:
		v.SetUint(uint64(randInt64(rand)))
	case reflect.Map:
		numElems := rand.Intn(complexSize)
		v.Set(reflect.MakeMap(concrete))
		for i := 0; i < numElems; i++ {
			key, ok1 := Value(concrete.Key(), rand)
			value, ok2 := Value(concrete.Elem(), rand)
			if !ok1 || !ok2 {
				return reflect.Value{}, false
			}
			v.SetMapIndex(key, value)
		}
	case reflect.Ptr:
		elem, ok := Value(concrete.Elem(), rand)
		if !ok {
			return reflect.Value{}, false
		}
		v.Set(reflect.New(concrete.Elem()))
		v.Elem().Set(elem)
	case reflect.Slice:
		numElems := rand.Intn(complexSize)
		v.Set(reflect.MakeSlice(concrete, numElems, numElems))
		for i := 0; i < numElems; i++ {
			elem, ok := Value(concrete.Elem(), rand)
			if !ok {
				return reflect.Value{}, false
			}
			v.Index(i).Set(elem)
		}
	case reflect.String:
		numChars := rand.Intn(complexSize)
		codePoints := make([]rune, numChars)
		for i := 0; i < numChars; i++ {
			codePoints[i] = rune(rand.Intn(0x10ffff))
		}
		v.SetString(string(codePoints))
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			elem, ok := Value(concrete.Field(i).Type, rand)
			if !ok {
				return reflect.Value{}, false
			}
			v.Field(i).Set(elem)
		}
	default:
		return reflect.Value{}, false
	}

	return v, true
}

// A Config structure contains options for running a test.
type Config struct {
	// MaxCount sets the maximum number of iterations. If zero,
	// MaxCountScale is used.
	MaxCount int
	// MaxCountScale is a non-negative scale factor applied to the default
	// maximum. If zero, the default is unchanged.
	MaxCountScale float64
	// If non-nil, rand is a source of random numbers. Otherwise a default
	// pseudo-random source will be used.
	Rand *rand.Rand
	// If non-nil, the Values function generates a slice of arbitrary
	// reflect.Values that are congruent with the arguments to the function
	// being tested. Otherwise, the top-level Values function is used
	// to generate them.
	Values func([]reflect.Value, *rand.Rand)
}

var defaultConfig Config

// getRand returns the *rand.Rand to use for a given Config.
func (c *Config) getRand() *rand.Rand {
	if c.Rand == nil {
		return rand.New(rand.NewSource(0))
	}
	return c.Rand
}

// getMaxCount returns the maximum number of iterations to run for a given
// Config.
func (c *Config) getMaxCount() (maxCount int) {
	maxCount = c.MaxCount
	if maxCount == 0 {
		if c.MaxCountScale != 0 {
			maxCount = int(c.MaxCountScale * float64(*defaultMaxCount))
		} else {
			maxCount = *defaultMaxCount
		}
	}

	return
}

// A SetupError is the result of an error in the way that check is being
// used, independent of the functions being tested.
type SetupError string

func (s SetupError) Error() string { return string(s) }

// A CheckError is the result of Check finding an error.
type CheckError struct {
	Count int
	In    []interface{}
}

func (s *CheckError) Error() string {
	return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In))
}

// A CheckEqualError is the result CheckEqual finding an error.
type CheckEqualError struct {
	CheckError
	Out1 []interface{}
	Out2 []interface{}
}

func (s *CheckEqualError) Error() string {
	return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2))
}

// Check looks for an input to f, any function that returns bool,
// such that f returns false.  It calls f repeatedly, with arbitrary
// values for each argument.  If f returns false on a given input,
// Check returns that input as a *CheckError.
// For example:
//
// 	func TestOddMultipleOfThree(t *testing.T) {
// 		f := func(x int) bool {
// 			y := OddMultipleOfThree(x)
// 			return y%2 == 1 && y%3 == 0
// 		}
// 		if err := quick.Check(f, nil); err != nil {
// 			t.Error(err)
// 		}
// 	}
func Check(f interface{}, config *Config) (err error) {
	if config == nil {
		config = &defaultConfig
	}

	fVal, fType, ok := functionAndType(f)
	if !ok {
		err = SetupError("argument is not a function")
		return
	}

	if fType.NumOut() != 1 {
		err = SetupError("function returns more than one value.")
		return
	}
	if fType.Out(0).Kind() != reflect.Bool {
		err = SetupError("function does not return a bool")
		return
	}

	arguments := make([]reflect.Value, fType.NumIn())
	rand := config.getRand()
	maxCount := config.getMaxCount()

	for i := 0; i < maxCount; i++ {
		err = arbitraryValues(arguments, fType, config, rand)
		if err != nil {
			return
		}

		if !fVal.Call(arguments)[0].Bool() {
			err = &CheckError{i + 1, toInterfaces(arguments)}
			return
		}
	}

	return
}

// CheckEqual looks for an input on which f and g return different results.
// It calls f and g repeatedly with arbitrary values for each argument.
// If f and g return different answers, CheckEqual returns a *CheckEqualError
// describing the input and the outputs.
func CheckEqual(f, g interface{}, config *Config) (err error) {
	if config == nil {
		config = &defaultConfig
	}

	x, xType, ok := functionAndType(f)
	if !ok {
		err = SetupError("f is not a function")
		return
	}
	y, yType, ok := functionAndType(g)
	if !ok {
		err = SetupError("g is not a function")
		return
	}

	if xType != yType {
		err = SetupError("functions have different types")
		return
	}

	arguments := make([]reflect.Value, xType.NumIn())
	rand := config.getRand()
	maxCount := config.getMaxCount()

	for i := 0; i < maxCount; i++ {
		err = arbitraryValues(arguments, xType, config, rand)
		if err != nil {
			return
		}

		xOut := toInterfaces(x.Call(arguments))
		yOut := toInterfaces(y.Call(arguments))

		if !reflect.DeepEqual(xOut, yOut) {
			err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut}
			return
		}
	}

	return
}

// arbitraryValues writes Values to args such that args contains Values
// suitable for calling f.
func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err error) {
	if config.Values != nil {
		config.Values(args, rand)
		return
	}

	for j := 0; j < len(args); j++ {
		var ok bool
		args[j], ok = Value(f.In(j), rand)
		if !ok {
			err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j))
			return
		}
	}

	return
}

func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) {
	v = reflect.ValueOf(f)
	ok = v.Kind() == reflect.Func
	if !ok {
		return
	}
	t = v.Type()
	return
}

func toInterfaces(values []reflect.Value) []interface{} {
	ret := make([]interface{}, len(values))
	for i, v := range values {
		ret[i] = v.Interface()
	}
	return ret
}

func toString(interfaces []interface{}) string {
	s := make([]string, len(interfaces))
	for i, v := range interfaces {
		s[i] = fmt.Sprintf("%#v", v)
	}
	return strings.Join(s, ", ")
}
<|endoftext|>


<commit_before>package envoy

import (
	"fmt"
	"kourier/pkg/config"
	"os"

	"github.com/envoyproxy/go-control-plane/pkg/conversion"
	"github.com/envoyproxy/go-control-plane/pkg/wellknown"

	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
	core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
	listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
	httpconnmanagerv2 "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubeclient "k8s.io/client-go/kubernetes"
)

const (
	envCertsSecretNamespace = "CERTS_SECRET_NAMESPACE"
	envCertsSecretName      = "CERTS_SECRET_NAME"
	certFieldInSecret       = "tls.crt"
	keyFieldInSecret        = "tls.key"
)

func newExternalEnvoyListener(https bool,
	manager *httpconnmanagerv2.HttpConnectionManager,
	kubeClient kubeclient.Interface) (*v2.Listener, error) {

	if https {
		return envoyHTTPSListener(manager, kubeClient, config.HttpsPortExternal)
	} else {
		return envoyHTTPListener(manager, config.HttpPortExternal)
	}
}

func newInternalEnvoyListener(manager *httpconnmanagerv2.HttpConnectionManager) (*v2.Listener, error) {
	return envoyHTTPListener(manager, config.HttpPortInternal)
}

func envoyHTTPListener(manager *httpconnmanagerv2.HttpConnectionManager, port uint32) (*v2.Listener, error) {
	filters, err := createFilters(manager)
	if err != nil {
		return nil, err
	}

	envoyListener := &v2.Listener{
		Name:    fmt.Sprintf("listener_%d", port),
		Address: createAddress(port),
		FilterChains: []*listener.FilterChain{
			{
				Filters: filters,
			},
		},
	}

	return envoyListener, nil
}

func envoyHTTPSListener(manager *httpconnmanagerv2.HttpConnectionManager,
	kubeClient kubeclient.Interface,
	port uint32) (*v2.Listener, error) {

	secret, err := kubeClient.CoreV1().Secrets(os.Getenv(envCertsSecretNamespace)).Get(
		os.Getenv(envCertsSecretName), metav1.GetOptions{})
	if err != nil {
		return nil, err
	}

	certificateChain := string(secret.Data[certFieldInSecret])
	privateKey := string(secret.Data[keyFieldInSecret])

	filters, err := createFilters(manager)
	if err != nil {
		return nil, err
	}

	envoyListener := v2.Listener{
		Name:    fmt.Sprintf("listener_%d", port),
		Address: createAddress(port),
		FilterChains: []*listener.FilterChain{
			{
				TlsContext: createTLSContext(certificateChain, privateKey),
				Filters:    filters,
			},
		},
	}

	return &envoyListener, nil
}

func createAddress(port uint32) *core.Address {
	return &core.Address{
		Address: &core.Address_SocketAddress{
			SocketAddress: &core.SocketAddress{
				Protocol: core.SocketAddress_TCP,
				Address:  "0.0.0.0",
				PortSpecifier: &core.SocketAddress_PortValue{
					PortValue: port,
				},
			},
		},
	}
}

func createFilters(manager *httpconnmanagerv2.HttpConnectionManager) ([]*listener.Filter, error) {
	pbst, err := conversion.MessageToStruct(manager)
	if err != nil {
		return []*listener.Filter{}, err
	}

	filters := []*listener.Filter{
		{
			Name:       wellknown.HTTPConnectionManager,
			ConfigType: &listener.Filter_Config{Config: pbst},
		},
	}

	return filters, nil
}

func createTLSContext(certificate string, privateKey string) *auth.DownstreamTlsContext {
	return &auth.DownstreamTlsContext{
		CommonTlsContext: &auth.CommonTlsContext{
			TlsCertificates: []*auth.TlsCertificate{
				{
					CertificateChain: &core.DataSource{
						Specifier: &core.DataSource_InlineBytes{InlineBytes: []byte(certificate)},
					},
					PrivateKey: &core.DataSource{
						Specifier: &core.DataSource_InlineBytes{InlineBytes: []byte(privateKey)},
					},
				},
			},
		},
	}
}
<commit_msg>Use typedConfig for httpManager to avoid deprecation message<commit_after>package envoy

import (
	"fmt"
	"kourier/pkg/config"
	"os"

	"github.com/golang/protobuf/ptypes"

	"github.com/envoyproxy/go-control-plane/pkg/wellknown"

	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
	core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
	listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
	httpconnmanagerv2 "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kubeclient "k8s.io/client-go/kubernetes"
)

const (
	envCertsSecretNamespace = "CERTS_SECRET_NAMESPACE"
	envCertsSecretName      = "CERTS_SECRET_NAME"
	certFieldInSecret       = "tls.crt"
	keyFieldInSecret        = "tls.key"
)

func newExternalEnvoyListener(https bool,
	manager *httpconnmanagerv2.HttpConnectionManager,
	kubeClient kubeclient.Interface) (*v2.Listener, error) {

	if https {
		return envoyHTTPSListener(manager, kubeClient, config.HttpsPortExternal)
	} else {
		return envoyHTTPListener(manager, config.HttpPortExternal)
	}
}

func newInternalEnvoyListener(manager *httpconnmanagerv2.HttpConnectionManager) (*v2.Listener, error) {
	return envoyHTTPListener(manager, config.HttpPortInternal)
}

func envoyHTTPListener(manager *httpconnmanagerv2.HttpConnectionManager, port uint32) (*v2.Listener, error) {
	filters, err := createFilters(manager)
	if err != nil {
		return nil, err
	}

	envoyListener := &v2.Listener{
		Name:    fmt.Sprintf("listener_%d", port),
		Address: createAddress(port),
		FilterChains: []*listener.FilterChain{
			{
				Filters: filters,
			},
		},
	}

	return envoyListener, nil
}

func envoyHTTPSListener(manager *httpconnmanagerv2.HttpConnectionManager,
	kubeClient kubeclient.Interface,
	port uint32) (*v2.Listener, error) {

	secret, err := kubeClient.CoreV1().Secrets(os.Getenv(envCertsSecretNamespace)).Get(
		os.Getenv(envCertsSecretName), metav1.GetOptions{})
	if err != nil {
		return nil, err
	}

	certificateChain := string(secret.Data[certFieldInSecret])
	privateKey := string(secret.Data[keyFieldInSecret])

	filters, err := createFilters(manager)
	if err != nil {
		return nil, err
	}

	envoyListener := v2.Listener{
		Name:    fmt.Sprintf("listener_%d", port),
		Address: createAddress(port),
		FilterChains: []*listener.FilterChain{
			{
				TlsContext: createTLSContext(certificateChain, privateKey),
				Filters:    filters,
			},
		},
	}

	return &envoyListener, nil
}

func createAddress(port uint32) *core.Address {
	return &core.Address{
		Address: &core.Address_SocketAddress{
			SocketAddress: &core.SocketAddress{
				Protocol: core.SocketAddress_TCP,
				Address:  "0.0.0.0",
				PortSpecifier: &core.SocketAddress_PortValue{
					PortValue: port,
				},
			},
		},
	}
}

func createFilters(manager *httpconnmanagerv2.HttpConnectionManager) ([]*listener.Filter, error) {
	managerAny, err := ptypes.MarshalAny(manager)
	if err != nil {
		return []*listener.Filter{}, err
	}

	filters := []*listener.Filter{
		{
			Name:       wellknown.HTTPConnectionManager,
			ConfigType: &listener.Filter_TypedConfig{TypedConfig: managerAny},
		},
	}

	return filters, nil
}

func createTLSContext(certificate string, privateKey string) *auth.DownstreamTlsContext {
	return &auth.DownstreamTlsContext{
		CommonTlsContext: &auth.CommonTlsContext{
			TlsCertificates: []*auth.TlsCertificate{
				{
					CertificateChain: &core.DataSource{
						Specifier: &core.DataSource_InlineBytes{InlineBytes: []byte(certificate)},
					},
					PrivateKey: &core.DataSource{
						Specifier: &core.DataSource_InlineBytes{InlineBytes: []byte(privateKey)},
					},
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package executor

import (
	"bytes"
	"errors"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"strings"
	"sync"
	"syscall"
	"time"
)

// Local provisioning is responsible for providing the execution environment
// on local machine via exec.Command.
// It runs command as current user.
type Local struct {
}

// NewLocal returns a Local instance.
func NewLocal() Local {
	return Local{}
}

// Execute runs the command given as input.
// Returned Task is able to stop & monitor the provisioned process.
func (l Local) Execute(command string) (Task, error) {
	log.Debug("Starting ", command)

	cmd := exec.Command("sh", "-c", command)

	// It is important to set additional Process Group ID for parent process and his children
	// to have ability to kill all the children processes.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	// Setting Buffer as io.Writer for Command output.
	// TODO: Write to temporary files instead of keeping in memory.
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Start()
	if err != nil {
		return nil, err
	}

	log.Debug("Started with PID ", cmd.Process.Pid)

	// Wait Err channel is for gethering error potential error messages from cmd.Wait function.
	waitErrChannel := make(chan error, 1)
	// Wait End channel is for checking the status of the Wait. If this channel is closed,
	// it means that the wait is completed (either with error or not)
	// This channel will not be used for passing any message.
	waitEndChannel := make(chan struct{})

	// Wait for local task in goroutine.
	go func() {
		// Wait for task completion.
		// NOTE: Wait() returns an error. We grab the process state in any case
		// (success or failure) below, so the error object matters less in the
		// status handling for now.
		if err := cmd.Wait(); err != nil {
			if _, ok := err.(*exec.ExitError); !ok {
				// In case of NON Exit Errors we are not sure if task does
				// terminate so return error.
				log.Error("Waiting for task failed. ", err)
				waitErrChannel <- err

				close(waitErrChannel)
				close(waitEndChannel)
				return
			}
		}

		log.Debug(
			"Ended ", strings.Join(cmd.Args, " "),
			" with output in file: ", stdout.String(),
			" with err output in file: ", stderr.String(),
			" with status code: ",
			(cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus())

		waitErrChannel <- nil

		close(waitErrChannel)
		close(waitEndChannel)
	}()

	return newlocalTask(cmd, &stdout, &stderr, waitErrChannel, waitEndChannel), nil
}

const killTimeout = 5 * time.Second

// localTask implements Task interface.
type localTask struct {
	waitMutex      sync.Mutex
	cmdHandler     *exec.Cmd
	stdout         *bytes.Buffer
	stderr         *bytes.Buffer
	waitErrChannel chan error
	waitEndChannel chan struct{}
	killTimeout    time.Duration
}

// newlocalTask returns a localTask instance.
func newlocalTask(cmdHandler *exec.Cmd, stdout *bytes.Buffer,
	stderr *bytes.Buffer, waitErrChannel chan error, waitEndChannel chan struct{}) *localTask {
	t := &localTask{
		cmdHandler:     cmdHandler,
		stdout:         stdout,
		stderr:         stderr,
		waitErrChannel: waitErrChannel,
		waitEndChannel: waitEndChannel,
		killTimeout:    killTimeout,
	}
	return t
}

// isTerminated checks if waitEndChannel is closed. If it is closed, it means
// that wait ends, task is in terminated state and ProcessState is not nil.
// ProcessState contains information about an exited process,
// available after call to Wait or Run.
func (task *localTask) isTerminated() bool {
	select {
	case <-task.waitEndChannel:
		// If waitEndChannel is closed then task is terminated.
		return true
	default:
		return false
	}
}

func (task *localTask) getPid() int {
	return task.cmdHandler.Process.Pid
}

func (task *localTask) createStatus() *Status {
	if !task.isTerminated() {
		return nil
	}

	return &Status{
		(task.cmdHandler.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus(),
		task.stdout.String(),
		task.stderr.String(),
	}
}

func (task *localTask) killTask(sig syscall.Signal) error {
	// We signal the entire process group.
	// The kill syscall interprets a negated PID N as the process group N belongs to.
	log.Debug("Sending ", sig, " to PID ", -task.getPid())
	return syscall.Kill(-task.getPid(), sig)
}

// Stop terminates the local task.
func (task *localTask) Stop() error {
	if task.isTerminated() {
		return nil
	}

	// Sending SIGKILL signal to local task.
	// TODO: Add PID namespace to handle orphan tasks properly.
	err := task.killTask(syscall.SIGKILL)
	if err != nil {
		log.Error(err)
		return err
	}

	// Checking if kill was succesful.
	isTerminated, taskErr := task.Wait(killTimeout)
	if taskErr != nil {
		log.Error(taskErr.Error())
		return taskErr
	}

	if !isTerminated {
		return errors.New("Cannot kill -9 task")
	}

	// No error, task terminated.
	return nil
}

// Status returns a state of the task. If task is terminated it returns the Status as a
// second item in tuple. Otherwise returns nil.
func (task *localTask) Status() (TaskState, *Status) {
	if !task.isTerminated() {
		return RUNNING, nil
	}

	return TERMINATED, task.createStatus()
}

// Wait waits for the command to finish with the given timeout time.
// In case of timeout == 0 there is no timeout for that.
// It returns true if task is terminated.
func (task *localTask) Wait(timeout time.Duration) (bool, error) {
	if task.isTerminated() {
		return true, nil
	}

	var timeoutChannel <-chan time.Time
	if timeout != 0 {
		// In case of wait with timeout set the timeout channel.
		timeoutChannel = time.After(timeout)
	}

	select {
	case err, ok := <-task.waitErrChannel:
		if err != nil && ok {
			// If channel is not closed and there is error return false and error.
			return false, err
		}

		// If channel is closed or there is no error return true and nil.
		return true, nil
	case <-timeoutChannel:
		// If timeout time exceeded return false and nil.
		return false, nil
	}
}
<commit_msg>Issues addressed.<commit_after>package executor

import (
	"bytes"
	"errors"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"strings"
	"sync"
	"syscall"
	"time"
)

// Local provisioning is responsible for providing the execution environment
// on local machine via exec.Command.
// It runs command as current user.
type Local struct {
}

// NewLocal returns a Local instance.
func NewLocal() Local {
	return Local{}
}

// Execute runs the command given as input.
// Returned Task is able to stop & monitor the provisioned process.
func (l Local) Execute(command string) (Task, error) {
	log.Debug("Starting ", command)

	cmd := exec.Command("sh", "-c", command)

	// It is important to set additional Process Group ID for parent process and his children
	// to have ability to kill all the children processes.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	// Setting Buffer as io.Writer for Command output.
	// TODO: Write to temporary files instead of keeping in memory.
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Start()
	if err != nil {
		return nil, err
	}

	log.Debug("Started with PID ", cmd.Process.Pid)

	// Wait Err channel is for gethering error potential error messages from cmd.Wait function.
	waitErrChannel := make(chan error, 1)
	// Wait End channel is for checking the status of the Wait. If this channel is closed,
	// it means that the wait is completed (either with error or not)
	// This channel will not be used for passing any message.
	waitEndChannel := make(chan struct{})

	// Wait for local task in goroutine.
	go func() {
		defer close(waitErrChannel)
		defer close(waitEndChannel)

		// Wait for task completion.
		// NOTE: Wait() returns an error. We grab the process state in any case
		// (success or failure) below, so the error object matters less in the
		// status handling for now.
		if err := cmd.Wait(); err != nil {
			if _, ok := err.(*exec.ExitError); !ok {
				// In case of NON Exit Errors we are not sure if task does
				// terminate so return error.
				log.Error("Waiting for task failed. ", err)
				waitErrChannel <- err
				return
			}
		}

		log.Debug(
			"Ended ", strings.Join(cmd.Args, " "),
			" with output in file: ", stdout.String(),
			" with err output in file: ", stderr.String(),
			" with status code: ",
			(cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus())
	}()

	return newlocalTask(cmd, &stdout, &stderr, waitErrChannel, waitEndChannel), nil
}

const killTimeout = 5 * time.Second

// localTask implements Task interface.
type localTask struct {
	waitMutex      sync.Mutex
	cmdHandler     *exec.Cmd
	stdout         *bytes.Buffer
	stderr         *bytes.Buffer
	waitErrChannel chan error
	waitEndChannel chan struct{}
	killTimeout    time.Duration
}

// newlocalTask returns a localTask instance.
func newlocalTask(cmdHandler *exec.Cmd, stdout *bytes.Buffer,
	stderr *bytes.Buffer, waitErrChannel chan error, waitEndChannel chan struct{}) *localTask {
	t := &localTask{
		cmdHandler:     cmdHandler,
		stdout:         stdout,
		stderr:         stderr,
		waitErrChannel: waitErrChannel,
		waitEndChannel: waitEndChannel,
		killTimeout:    killTimeout,
	}
	return t
}

// isTerminated checks if waitEndChannel is closed. If it is closed, it means
// that wait ended and task is in terminated state.
// NOTE: If it's true then ProcessState is not nil. ProcessState contains information
// about an exited process available after call to Wait or Run.
func (task *localTask) isTerminated() bool {
	select {
	case <-task.waitEndChannel:
		// If waitEndChannel is closed then task is terminated.
		return true
	default:
		return false
	}
}

func (task *localTask) getPid() int {
	return task.cmdHandler.Process.Pid
}

func (task *localTask) createStatus() *Status {
	if !task.isTerminated() {
		return nil
	}

	return &Status{
		(task.cmdHandler.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus(),
		task.stdout.String(),
		task.stderr.String(),
	}
}

func (task *localTask) killTask(sig syscall.Signal) error {
	// We signal the entire process group.
	// The kill syscall interprets a negated PID N as the process group N belongs to.
	log.Debug("Sending ", sig, " to PID ", -task.getPid())
	return syscall.Kill(-task.getPid(), sig)
}

// Stop terminates the local task.
func (task *localTask) Stop() error {
	if task.isTerminated() {
		return nil
	}

	// Sending SIGKILL signal to local task.
	// TODO: Add PID namespace to handle orphan tasks properly.
	err := task.killTask(syscall.SIGKILL)
	if err != nil {
		log.Error(err)
		return err
	}

	// Checking if kill was succesful.
	isTerminated, taskErr := task.Wait(killTimeout)
	if taskErr != nil {
		log.Error(taskErr.Error())
		return taskErr
	}

	if !isTerminated {
		return errors.New("Cannot kill -9 task")
	}

	// No error, task terminated.
	return nil
}

// Status returns a state of the task. If task is terminated it returns the Status as a
// second item in tuple. Otherwise returns nil.
func (task *localTask) Status() (TaskState, *Status) {
	if !task.isTerminated() {
		return RUNNING, nil
	}

	return TERMINATED, task.createStatus()
}

// Wait waits for the command to finish with the given timeout time.
// In case of timeout == 0 there is no timeout for that.
// It returns true if task is terminated.
func (task *localTask) Wait(timeout time.Duration) (bool, error) {
	if task.isTerminated() {
		return true, nil
	}

	var timeoutChannel <-chan time.Time
	if timeout != 0 {
		// In case of wait with timeout set the timeout channel.
		timeoutChannel = time.After(timeout)
	}

	select {
	case err, ok := <-task.waitErrChannel:
		if err != nil && ok {
			// If channel is not closed and there is error return false and error.
			return false, err
		}

		// If channel is closed or there is no error return true and nil.
		return true, nil
	case <-timeoutChannel:
		// If timeout time exceeded return false and nil.
		return false, nil
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 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 notes

import (
	"fmt"
	"strings"
	"sync"

	"github.com/pkg/errors"

	"k8s.io/release/pkg/notes/options"

	"github.com/cheggaaa/pb/v3"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	gitobject "github.com/go-git/go-git/v5/plumbing/object"
	"github.com/nozzle/throttler"
	"github.com/sirupsen/logrus"
)

type commitPrPair struct {
	Commit *gitobject.Commit
	PrNum  int
}

type releaseNotesAggregator struct {
	releaseNotes *ReleaseNotes
	sync.RWMutex
}

func (g *Gatherer) ListReleaseNotesV2() (*ReleaseNotes, error) {
	// left parent of Git commits is always the main branch parent
	pairs, err := g.listLeftParentCommits(g.options)
	if err != nil {
		return nil, errors.Wrap(err, "listing offline commits")
	}

	// load map providers specified in options
	mapProviders := []MapProvider{}
	for _, initString := range g.options.MapProviderStrings {
		provider, err := NewProviderFromInitString(initString)
		if err != nil {
			return nil, errors.Wrap(err, "while getting release notes map providers")
		}
		mapProviders = append(mapProviders, provider)
	}

	t := throttler.New(maxParallelRequests, len(pairs))

	aggregator := releaseNotesAggregator{
		releaseNotes: NewReleaseNotes(),
	}

	pairsCount := len(pairs)
	logrus.Infof("processing release notes for %d commits", pairsCount)
	bar := pb.Full.Start(pairsCount)

	for _, pair := range pairs {
		noteMaps := []*ReleaseNotesMap{}

		for _, provider := range mapProviders {
			noteMaps, err = provider.GetMapsForPR(pair.PrNum)
			if err != nil {
				logrus.Errorf("[ignored] pr: %d err: %v", pair.PrNum, err)
				noteMaps = []*ReleaseNotesMap{}
			}
		}

		go func() {
			releaseNote, err := g.buildReleaseNote(pair)
			if err == nil && releaseNote != nil {
				for _, noteMap := range noteMaps {
					if err := releaseNote.ApplyMap(noteMap); err != nil {
						logrus.Errorf("[ignored] pr: %d err: %v", pair.PrNum, err)
					}
				}
				aggregator.Lock()
				aggregator.releaseNotes.Set(pair.PrNum, releaseNote)
				aggregator.Unlock()
			} else if err != nil {
				logrus.Errorf("sha: %s pr: %d err: %v", pair.Commit.Hash.String(), pair.PrNum, err)
			}
			bar.Increment()
			t.Done(nil)
		}()

		if t.Throttle() > 0 {
			break
		}
	}

	if err := t.Err(); err != nil {
		return nil, err
	}

	bar.Finish()

	return aggregator.releaseNotes, nil
}

func (g *Gatherer) buildReleaseNote(pair *commitPrPair) (*ReleaseNote, error) {
	pr, _, err := g.client.GetPullRequest(g.context, g.options.GithubOrg, g.options.GithubRepo, pair.PrNum)
	if err != nil {
		return nil, err
	}

	prBody := pr.GetBody()

	text, err := noteTextFromString(prBody)
	if err != nil {
		logrus.Debugf("sha: %s pr: %d err: %v", pair.Commit.Hash.String(), pair.PrNum, err)
		return nil, nil
	}

	documentation := DocumentationFromString(prBody)

	author := pr.GetUser().GetLogin()
	authorURL := pr.GetUser().GetHTMLURL()
	prURL := pr.GetHTMLURL()
	isFeature := hasString(labelsWithPrefix(pr, "kind"), "feature")
	noteSuffix := prettifySIGList(labelsWithPrefix(pr, "sig"))

	isDuplicateSIG := false
	if len(labelsWithPrefix(pr, "sig")) > 1 {
		isDuplicateSIG = true
	}

	isDuplicateKind := false
	if len(labelsWithPrefix(pr, "kind")) > 1 {
		isDuplicateKind = true
	}

	// TODO(wilsonehusin): extract / follow original in ReleasenoteFromCommit
	indented := strings.ReplaceAll(text, "\n", "\n  ")
	markdown := fmt.Sprintf("%s ([#%d](%s), [@%s](%s))",
		indented, pr.GetNumber(), prURL, author, authorURL)

	if noteSuffix != "" {
		markdown = fmt.Sprintf("%s [%s]", markdown, noteSuffix)
	}

	// Uppercase the first character of the markdown to make it look uniform
	markdown = strings.ToUpper(string(markdown[0])) + markdown[1:]

	return &ReleaseNote{
		Commit:         pair.Commit.Hash.String(),
		Text:           text,
		Markdown:       markdown,
		Documentation:  documentation,
		Author:         author,
		AuthorURL:      authorURL,
		PrURL:          prURL,
		PrNumber:       pr.GetNumber(),
		SIGs:           labelsWithPrefix(pr, "sig"),
		Kinds:          labelsWithPrefix(pr, "kind"),
		Areas:          labelsWithPrefix(pr, "area"),
		Feature:        isFeature,
		Duplicate:      isDuplicateSIG,
		DuplicateKind:  isDuplicateKind,
		ActionRequired: labelExactMatch(pr, "release-note-action-required"),
		DoNotPublish:   labelExactMatch(pr, "release-note-none"),
	}, nil
}

func (g *Gatherer) listLeftParentCommits(opts *options.Options) ([]*commitPrPair, error) {
	localRepository, err := git.PlainOpen(opts.RepoPath)
	if err != nil {
		return nil, err
	}

	// opts.StartSHA points to a tag (e.g. 1.20.0) which is on a release branch (e.g. release-1.20)
	// this means traveling through commit history from opts.EndSHA will never reach opts.StartSHA

	// the stopping point to be set should be the last shared commit between release branch and primary (master) branch
	// usually, following the left / first parents, it would be
	// * tag: v1.20.0, some merge commit
	// |
	// * Anago GCB release commit (begin branch out of release-1.20)
	// |
	// * last shared commit

	// this means the stopping point is 2 commits behind the tag pointed by opts.StartSHA

	stopHash := plumbing.NewHash(opts.StartSHA)
	for i := 0; i < 2; i++ {
		commitObject, err := localRepository.CommitObject(stopHash)
		if err != nil {
			return nil, errors.Wrap(err, "finding last shared commit")
		}
		stopHash = commitObject.ParentHashes[0]
	}

	logrus.Infof("will stop at %s", stopHash)

	currentTagHash := plumbing.NewHash(opts.EndSHA)

	pairs := []*commitPrPair{}
	hashPointer := currentTagHash
	for hashPointer != stopHash {
		hashString := hashPointer.String()

		// Find and collect commit objects
		commitPointer, err := localRepository.CommitObject(hashPointer)
		if err != nil {
			return nil, errors.Wrap(err, "finding CommitObject")
		}

		// Find and collect PR number from commit message
		prNums, err := prsNumForCommitFromMessage(commitPointer.Message)
		if err == errNoPRIDFoundInCommitMessage {
			logrus.Debugf("sha: %s prs: []", hashString)

			// Advance pointer based on left parent
			hashPointer = commitPointer.ParentHashes[0]
			continue
		}
		if err != nil {
			logrus.Warnf("sha: %s err: %s (silenced)", hashString, err.Error())

			// Advance pointer based on left parent
			hashPointer = commitPointer.ParentHashes[0]
			continue
		}
		logrus.Debugf("sha: %s prs: %v", hashString, prNums)

		// Only taking the first one, assuming they are merged by Prow
		pairs = append(pairs, &commitPrPair{Commit: commitPointer, PrNum: prNums[0]})

		// Advance pointer based on left parent
		hashPointer = commitPointer.ParentHashes[0]
	}

	return pairs, nil
}
<commit_msg>Apply MatchesExcludeFilter on PR (v1 parity)<commit_after>/*
Copyright 2021 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 notes

import (
	"fmt"
	"os"
	"strings"
	"sync"

	"github.com/pkg/errors"

	"k8s.io/release/pkg/notes/options"

	"github.com/cheggaaa/pb/v3"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	gitobject "github.com/go-git/go-git/v5/plumbing/object"
	"github.com/nozzle/throttler"
	"github.com/sirupsen/logrus"
)

type commitPrPair struct {
	Commit *gitobject.Commit
	PrNum  int
}

type releaseNotesAggregator struct {
	releaseNotes *ReleaseNotes
	sync.RWMutex
}

func (g *Gatherer) ListReleaseNotesV2() (*ReleaseNotes, error) {
	// left parent of Git commits is always the main branch parent
	pairs, err := g.listLeftParentCommits(g.options)
	if err != nil {
		return nil, errors.Wrap(err, "listing offline commits")
	}

	// load map providers specified in options
	mapProviders := []MapProvider{}
	for _, initString := range g.options.MapProviderStrings {
		provider, err := NewProviderFromInitString(initString)
		if err != nil {
			return nil, errors.Wrap(err, "while getting release notes map providers")
		}
		mapProviders = append(mapProviders, provider)
	}

	t := throttler.New(maxParallelRequests, len(pairs))

	aggregator := releaseNotesAggregator{
		releaseNotes: NewReleaseNotes(),
	}

	pairsCount := len(pairs)
	logrus.Infof("processing release notes for %d commits", pairsCount)
	bar := pb.New(pairsCount).SetWriter(os.Stdout).Start()

	for _, pair := range pairs {
		go func(pair *commitPrPair) {
			noteMaps := []*ReleaseNotesMap{}
			for _, provider := range mapProviders {
				noteMaps, err = provider.GetMapsForPR(pair.PrNum)
				if err != nil {
					logrus.WithFields(logrus.Fields{
						"pr": pair.PrNum,
					}).Errorf("ignore err: %v", err)
					noteMaps = []*ReleaseNotesMap{}
				}
			}

			releaseNote, err := g.buildReleaseNote(pair)
			if err == nil {
				if releaseNote != nil {
					for _, noteMap := range noteMaps {
						if err := releaseNote.ApplyMap(noteMap); err != nil {
							logrus.WithFields(logrus.Fields{
								"pr": pair.PrNum,
							}).Errorf("ignore err: %v", err)
						}
					}
					logrus.WithFields(logrus.Fields{
						"pr":   pair.PrNum,
						"note": releaseNote.Text,
					}).Debugf("finalized release note")
					aggregator.Lock()
					aggregator.releaseNotes.Set(pair.PrNum, releaseNote)
					aggregator.Unlock()
				} else {
					logrus.WithFields(logrus.Fields{
						"pr": pair.PrNum,
					}).Debugf("skip: empty release note")
				}
			} else {
				logrus.WithFields(logrus.Fields{
					"sha": pair.Commit.Hash.String(),
					"pr":  pair.PrNum,
				}).Errorf("err: %v", err)
			}
			bar.Increment()
			t.Done(nil)
		}(pair)

		if t.Throttle() > 0 {
			break
		}
	}

	if err := t.Err(); err != nil {
		return nil, err
	}

	bar.Finish()

	return aggregator.releaseNotes, nil
}

func (g *Gatherer) buildReleaseNote(pair *commitPrPair) (*ReleaseNote, error) {
	pr, _, err := g.client.GetPullRequest(g.context, g.options.GithubOrg, g.options.GithubRepo, pair.PrNum)
	if err != nil {
		return nil, err
	}

	prBody := pr.GetBody()

	if MatchesExcludeFilter(prBody) {
		return nil, nil
	}

	text, err := noteTextFromString(prBody)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"sha": pair.Commit.Hash.String(),
			"pr":  pair.PrNum,
		}).Debugf("ignore err: %v", err)
		return nil, nil
	}

	documentation := DocumentationFromString(prBody)

	author := pr.GetUser().GetLogin()
	authorURL := pr.GetUser().GetHTMLURL()
	prURL := pr.GetHTMLURL()
	isFeature := hasString(labelsWithPrefix(pr, "kind"), "feature")
	noteSuffix := prettifySIGList(labelsWithPrefix(pr, "sig"))

	isDuplicateSIG := false
	if len(labelsWithPrefix(pr, "sig")) > 1 {
		isDuplicateSIG = true
	}

	isDuplicateKind := false
	if len(labelsWithPrefix(pr, "kind")) > 1 {
		isDuplicateKind = true
	}

	// TODO(wilsonehusin): extract / follow original in ReleasenoteFromCommit
	indented := strings.ReplaceAll(text, "\n", "\n  ")
	markdown := fmt.Sprintf("%s ([#%d](%s), [@%s](%s))",
		indented, pr.GetNumber(), prURL, author, authorURL)

	if noteSuffix != "" {
		markdown = fmt.Sprintf("%s [%s]", markdown, noteSuffix)
	}

	// Uppercase the first character of the markdown to make it look uniform
	markdown = strings.ToUpper(string(markdown[0])) + markdown[1:]

	return &ReleaseNote{
		Commit:         pair.Commit.Hash.String(),
		Text:           text,
		Markdown:       markdown,
		Documentation:  documentation,
		Author:         author,
		AuthorURL:      authorURL,
		PrURL:          prURL,
		PrNumber:       pr.GetNumber(),
		SIGs:           labelsWithPrefix(pr, "sig"),
		Kinds:          labelsWithPrefix(pr, "kind"),
		Areas:          labelsWithPrefix(pr, "area"),
		Feature:        isFeature,
		Duplicate:      isDuplicateSIG,
		DuplicateKind:  isDuplicateKind,
		ActionRequired: labelExactMatch(pr, "release-note-action-required"),
		DoNotPublish:   labelExactMatch(pr, "release-note-none"),
	}, nil
}

func (g *Gatherer) listLeftParentCommits(opts *options.Options) ([]*commitPrPair, error) {
	localRepository, err := git.PlainOpen(opts.RepoPath)
	if err != nil {
		return nil, err
	}

	// opts.StartSHA points to a tag (e.g. 1.20.0) which is on a release branch (e.g. release-1.20)
	// this means traveling through commit history from opts.EndSHA will never reach opts.StartSHA

	// the stopping point to be set should be the last shared commit between release branch and primary (master) branch
	// usually, following the left / first parents, it would be
	// * tag: v1.20.0, some merge commit
	// |
	// * Anago GCB release commit (begin branch out of release-1.20)
	// |
	// * last shared commit

	// this means the stopping point is 2 commits behind the tag pointed by opts.StartSHA

	stopHash := plumbing.NewHash(opts.StartSHA)
	for i := 0; i < 2; i++ {
		commitObject, err := localRepository.CommitObject(stopHash)
		if err != nil {
			return nil, errors.Wrap(err, "finding last shared commit")
		}
		stopHash = commitObject.ParentHashes[0]
	}

	logrus.Infof("will stop at %s", stopHash)

	currentTagHash := plumbing.NewHash(opts.EndSHA)

	pairs := []*commitPrPair{}
	hashPointer := currentTagHash
	for hashPointer != stopHash {
		hashString := hashPointer.String()

		// Find and collect commit objects
		commitPointer, err := localRepository.CommitObject(hashPointer)
		if err != nil {
			return nil, errors.Wrap(err, "finding CommitObject")
		}

		// Find and collect PR number from commit message
		prNums, err := prsNumForCommitFromMessage(commitPointer.Message)
		if err == errNoPRIDFoundInCommitMessage {
			logrus.WithFields(logrus.Fields{
				"sha": hashString,
			}).Debug("no associated PR found")

			// Advance pointer based on left parent
			hashPointer = commitPointer.ParentHashes[0]
			continue
		}
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"sha": hashString,
			}).Warnf("ignore err: %v", err)

			// Advance pointer based on left parent
			hashPointer = commitPointer.ParentHashes[0]
			continue
		}
		logrus.WithFields(logrus.Fields{
			"sha": hashString,
			"prs": prNums,
		}).Debug("found PR from commit")

		// Only taking the first one, assuming they are merged by Prow
		pairs = append(pairs, &commitPrPair{Commit: commitPointer, PrNum: prNums[0]})

		// Advance pointer based on left parent
		hashPointer = commitPointer.ParentHashes[0]
	}

	return pairs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"time"

	"github.com/emembrives/dispotrains/dispotrains.webapp/src/storage"

	"github.com/eknkc/dateformat"
	"github.com/gorilla/mux"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

const (
	mongoDbHost = "localhost"
)

var (
	session = createSessionOrDie()
)

type Line struct {
	Network      string
	ID           string
	GoodStations []*storage.Station
	BadStations  []*storage.Station
	LastUpdate   time.Time
}

type LineSlice []Line

type DisplayStation struct {
	Name         string
	DisplayName  string
	City         string
	Position     storage.Coordinates
	OsmID        string
	Elevators    []*LocElevator
	LastUpdate   time.Time
	BadElevators int
}

type LocElevator storage.Elevator

type dataStatus struct {
	Elevator   string
	State      string
	Lastupdate time.Time
}

func (e *LocElevator) LocalStatusDate() string {
	return dateformat.FormatLocale(e.Status.LastUpdate, "ddd D MMM à HH:MM", dateformat.French)
}

func createSessionOrDie() *mgo.Session {
	session, err := mgo.Dial(mongoDbHost)
	if err != nil {
		panic(err)
	}
	return session
}

// VoronoiHandler sends historical data for the Voronoi map.
func VoronoiHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")

	cStatistics := session.DB("dispotrains").C("statistics")

	var stats []bson.M = make([]bson.M, 0)
	if err := cStatistics.Find(nil).All(&stats); err != nil {
		log.Println(err)
	}
	var jsonData []bson.M = make([]bson.M, 0)
	for _, stat := range stats {
		delete(stat, "_id")
		jsonData = append(jsonData, stat)
	}
	if err := json.NewEncoder(w).Encode(&jsonData); err != nil {
		log.Println(err)
	}
}

func GetLinesHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	w.Header().Set("Cache-control", "public, max-age=86400")

	c := session.DB("dispotrains").C("lines")
	var lines = make(LineSlice, 0)
	if err := c.Find(nil).Sort("network", "id").All(&lines); err != nil {
		log.Println(err)
	}
	json.NewEncoder(w).Encode(&lines)
}

func GetStationsHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")

	c := session.DB("dispotrains").C("stations")
	var stations []bson.M
	if err := c.Find(nil).All(&stations); err != nil {
		log.Println(err)
	}
	var jsonStations []bson.M
	for _, station := range stations {
		delete(station, "_id")
		jsonStations = append(jsonStations, station)
	}
	json.NewEncoder(w).Encode(&jsonStations)
}

func CacheRequest(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-control", "public, max-age=259200")
		h.ServeHTTP(w, r)
	})
}

func main() {
	defer session.Close()
	r := mux.NewRouter()
	r.HandleFunc("/app/GetLines/", GetLinesHandler)
	r.HandleFunc("/app/GetStations/", GetStationsHandler)
	r.HandleFunc("/app/AllStats/", VoronoiHandler)
	r.PathPrefix("/static/").Handler(CacheRequest(http.StripPrefix("/static/", http.FileServer(http.Dir("static")))))
	r.PathPrefix("/").Handler(CacheRequest(http.FileServer(http.Dir("dist"))))
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe("0.0.0.0:9000", nil))
}
<commit_msg>Fix server name<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"time"

	"github.com/emembrives/dispotrains/dispotrains.webapp/src/storage"

	"github.com/eknkc/dateformat"
	"github.com/gorilla/mux"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

const (
	mongoDbHost = "db"
)

var (
	session = createSessionOrDie()
)

type Line struct {
	Network      string
	ID           string
	GoodStations []*storage.Station
	BadStations  []*storage.Station
	LastUpdate   time.Time
}

type LineSlice []Line

type DisplayStation struct {
	Name         string
	DisplayName  string
	City         string
	Position     storage.Coordinates
	OsmID        string
	Elevators    []*LocElevator
	LastUpdate   time.Time
	BadElevators int
}

type LocElevator storage.Elevator

type dataStatus struct {
	Elevator   string
	State      string
	Lastupdate time.Time
}

func (e *LocElevator) LocalStatusDate() string {
	return dateformat.FormatLocale(e.Status.LastUpdate, "ddd D MMM à HH:MM", dateformat.French)
}

func createSessionOrDie() *mgo.Session {
	session, err := mgo.Dial(mongoDbHost)
	if err != nil {
		panic(err)
	}
	return session
}

// VoronoiHandler sends historical data for the Voronoi map.
func VoronoiHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")

	cStatistics := session.DB("dispotrains").C("statistics")

	var stats []bson.M = make([]bson.M, 0)
	if err := cStatistics.Find(nil).All(&stats); err != nil {
		log.Println(err)
	}
	var jsonData []bson.M = make([]bson.M, 0)
	for _, stat := range stats {
		delete(stat, "_id")
		jsonData = append(jsonData, stat)
	}
	if err := json.NewEncoder(w).Encode(&jsonData); err != nil {
		log.Println(err)
	}
}

func GetLinesHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	w.Header().Set("Cache-control", "public, max-age=86400")

	c := session.DB("dispotrains").C("lines")
	var lines = make(LineSlice, 0)
	if err := c.Find(nil).Sort("network", "id").All(&lines); err != nil {
		log.Println(err)
	}
	json.NewEncoder(w).Encode(&lines)
}

func GetStationsHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "GET")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type")

	c := session.DB("dispotrains").C("stations")
	var stations []bson.M
	if err := c.Find(nil).All(&stations); err != nil {
		log.Println(err)
	}
	var jsonStations []bson.M
	for _, station := range stations {
		delete(station, "_id")
		jsonStations = append(jsonStations, station)
	}
	json.NewEncoder(w).Encode(&jsonStations)
}

func CacheRequest(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-control", "public, max-age=259200")
		h.ServeHTTP(w, r)
	})
}

func main() {
	defer session.Close()
	r := mux.NewRouter()
	r.HandleFunc("/app/GetLines/", GetLinesHandler)
	r.HandleFunc("/app/GetStations/", GetStationsHandler)
	r.HandleFunc("/app/AllStats/", VoronoiHandler)
	r.PathPrefix("/static/").Handler(CacheRequest(http.StripPrefix("/static/", http.FileServer(http.Dir("static")))))
	r.PathPrefix("/").Handler(CacheRequest(http.FileServer(http.Dir("dist"))))
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe("0.0.0.0:9000", nil))
}
<|endoftext|>


<commit_before>package cache

import "gopkg.in/src-d/go-git.v4/plumbing"

const (
	initialQueueSize = 20
	MaxSize          = 10 * MiByte
)

type ObjectFIFO struct {
	objects map[plumbing.Hash]plumbing.EncodedObject
	order   *queue

	maxSize    int64
	actualSize int64
}

// NewObjectFIFO returns an Object cache that keeps the newest objects that fit
// into the specific memory size
func NewObjectFIFO(size int64) *ObjectFIFO {
	return &ObjectFIFO{
		objects: make(map[plumbing.Hash]plumbing.EncodedObject),
		order:   newQueue(initialQueueSize),
		maxSize: size,
	}
}

// Add adds a new object to the cache. If the object size is greater than the
// cache size, the object is not added.
func (c *ObjectFIFO) Add(o plumbing.EncodedObject) {
	// if the size of the object is bigger or equal than the cache size,
	// skip it
	if o.Size() >= c.maxSize {
		return
	}

	// if the object is into the cache, do not add it again
	if _, ok := c.objects[o.Hash()]; ok {
		return
	}

	// delete the oldest object if cache is full
	if c.actualSize >= c.maxSize {
		h := c.order.Pop()
		o := c.objects[h]
		if o != nil {
			c.actualSize -= o.Size()
			delete(c.objects, h)
		}
	}

	c.objects[o.Hash()] = o
	c.order.Push(o.Hash())
	c.actualSize += o.Size()
}

// Get returns an object by his hash. If the object is not into the cache, it
// returns nil
func (c *ObjectFIFO) Get(k plumbing.Hash) plumbing.EncodedObject {
	return c.objects[k]
}

// Clear the content of this cache object
func (c *ObjectFIFO) Clear() {
	c.objects = make(map[plumbing.Hash]plumbing.EncodedObject)
	c.order = newQueue(initialQueueSize)
	c.actualSize = 0
}
<commit_msg>Fix typos in cache pkg (#235)<commit_after>package cache

import "gopkg.in/src-d/go-git.v4/plumbing"

const (
	initialQueueSize = 20
	MaxSize          = 10 * MiByte
)

type ObjectFIFO struct {
	objects map[plumbing.Hash]plumbing.EncodedObject
	order   *queue

	maxSize    int64
	actualSize int64
}

// NewObjectFIFO returns an Object cache that keeps the newest objects that fit
// into the specific memory size
func NewObjectFIFO(size int64) *ObjectFIFO {
	return &ObjectFIFO{
		objects: make(map[plumbing.Hash]plumbing.EncodedObject),
		order:   newQueue(initialQueueSize),
		maxSize: size,
	}
}

// Add adds a new object to the cache. If the object size is greater than the
// cache size, the object is not added.
func (c *ObjectFIFO) Add(o plumbing.EncodedObject) {
	// if the size of the object is bigger or equal than the cache size,
	// skip it
	if o.Size() >= c.maxSize {
		return
	}

	// if the object is into the cache, do not add it again
	if _, ok := c.objects[o.Hash()]; ok {
		return
	}

	// delete the oldest object if cache is full
	if c.actualSize >= c.maxSize {
		h := c.order.Pop()
		o := c.objects[h]
		if o != nil {
			c.actualSize -= o.Size()
			delete(c.objects, h)
		}
	}

	c.objects[o.Hash()] = o
	c.order.Push(o.Hash())
	c.actualSize += o.Size()
}

// Get returns an object by his hash. If the object is not found in the cache, it
// returns nil
func (c *ObjectFIFO) Get(k plumbing.Hash) plumbing.EncodedObject {
	return c.objects[k]
}

// Clear the content of this object cache
func (c *ObjectFIFO) Clear() {
	c.objects = make(map[plumbing.Hash]plumbing.EncodedObject)
	c.order = newQueue(initialQueueSize)
	c.actualSize = 0
}
<|endoftext|>


<commit_before>package venuelib

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"

	"github.com/golang/glog"
	"github.com/howeyc/gopass"
	"github.com/kward/venue/codes"
)

// venueError defines the status of a Venue call.
type venueError struct {
	code codes.Code
	desc string
}

func (e *venueError) Error() string {
	return fmt.Sprintf("venue error: %s: %s", e.code, e.desc)
}

// Code returns the error code for `err` if it was produced by Venue.
// Otherwise, it returns codes.Unknown.
func Code(err error) codes.Code {
	if err == nil {
		return codes.OK
	}
	if e, ok := err.(*venueError); ok {
		return e.code
	}
	return codes.Unknown
}

// ErrorDesc returns the error description of `err` if it was produced by Venue.
// Otherwise, it returns err.Error(), or an empty string when `err` is nil.
func ErrorDesc(err error) string {
	if err == nil {
		return ""
	}
	if e, ok := err.(*venueError); ok {
		return e.desc
	}
	return err.Error()
}

// Errorf returns an error containing an error code and a description.
// Errorf returns nil if `c` is OK.
func Errorf(c codes.Code, format string, a ...interface{}) error {
	if c == codes.OK {
		return nil
	}
	return &venueError{
		code: c,
		desc: fmt.Sprintf(format, a...),
	}
}

// GetPasswd requests the user for a masked password.
func GetPasswd() string {
	fmt.Printf("Password: ")
	p, err := gopass.GetPasswdMasked()
	if err != nil {
		glog.Fatal(err)
	}
	return string(p)
}

// ToInt converts a string to an int.
func ToInt(s string) int {
	var i int
	_, err := fmt.Fscanf(bytes.NewBufferString(s), "%d", &i)
	if err != nil {
		return 0
	}
	return i
}

// FnName returns the calling function name, e.g. "SomeFunction()". This is
// useful for logging the function name with glog.
func FnName() string {
	pc := make([]uintptr, 10) // At least 1 entry needed.
	runtime.Callers(2, pc)
	name := runtime.FuncForPC(pc[0]).Name()
	return name[strings.LastIndex(name, ".")+1:] + "()"
}
<commit_msg>added package comment<commit_after>// Package venuelib provides utility functions.
package venuelib

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"

	"github.com/golang/glog"
	"github.com/howeyc/gopass"
	"github.com/kward/venue/codes"
)

// venueError defines the status of a Venue call.
type venueError struct {
	code codes.Code
	desc string
}

func (e *venueError) Error() string {
	return fmt.Sprintf("venue error: %s: %s", e.code, e.desc)
}

// Code returns the error code for `err` if it was produced by Venue.
// Otherwise, it returns codes.Unknown.
func Code(err error) codes.Code {
	if err == nil {
		return codes.OK
	}
	if e, ok := err.(*venueError); ok {
		return e.code
	}
	return codes.Unknown
}

// ErrorDesc returns the error description of `err` if it was produced by Venue.
// Otherwise, it returns err.Error(), or an empty string when `err` is nil.
func ErrorDesc(err error) string {
	if err == nil {
		return ""
	}
	if e, ok := err.(*venueError); ok {
		return e.desc
	}
	return err.Error()
}

// Errorf returns an error containing an error code and a description.
// Errorf returns nil if `c` is OK.
func Errorf(c codes.Code, format string, a ...interface{}) error {
	if c == codes.OK {
		return nil
	}
	return &venueError{
		code: c,
		desc: fmt.Sprintf(format, a...),
	}
}

// GetPasswd requests the user for a masked password.
func GetPasswd() string {
	fmt.Printf("Password: ")
	p, err := gopass.GetPasswdMasked()
	if err != nil {
		glog.Fatal(err)
	}
	return string(p)
}

// ToInt converts a string to an int.
func ToInt(s string) int {
	var i int
	_, err := fmt.Fscanf(bytes.NewBufferString(s), "%d", &i)
	if err != nil {
		return 0
	}
	return i
}

// FnName returns the calling function name, e.g. "SomeFunction()". This is
// useful for logging the function name with glog.
func FnName() string {
	pc := make([]uintptr, 10) // At least 1 entry needed.
	runtime.Callers(2, pc)
	name := runtime.FuncForPC(pc[0]).Name()
	return name[strings.LastIndex(name, ".")+1:] + "()"
}
<|endoftext|>