<commit_before>package server

import (
	"fmt"
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models/affinity"
	"github.com/centurylinkcloud/clc-go-cli/models/customfields"
	"github.com/centurylinkcloud/clc-go-cli/models/group"
	"time"
)

const (
	timeFormat = "2006-01-02 15:04:05"
)

type CreateReq struct {
	Name                   string `valid:"required"`
	Description            string `json:",omitempty"`
	GroupId                string
	GroupName              string `json:",omitempty"`
	SourceServerId         string
	TemplateName           string             `json:",omitempty"`
	IsManagedOs            bool               `json:",omitempty"`
	IsManagedBackup        bool               `json:",omitempty"`
	PrimaryDns             string             `json:",omitempty"`
	SecondaryDns           string             `json:",omitempty"`
	NetworkId              string             `json:",omitempty"`
	IpAddress              string             `json:",omitempty"`
	RootPassword           string             `json:",omitempty"`
	SourceServerPassword   string             `json:",omitempty"`
	Cpu                    int64              `valid:"required"`
	CpuAutoscalePolicyId   string             `json:",omitempty"`
	MemoryGb               int64              `valid:"required"`
	Type                   string             `valid:"required" oneOf:"standard,hyperscale,bareMetal"`
	StorageType            string             `json:",omitempty" oneOf:"standard,premium,hyperscale"`
	AntiAffinityPolicyId   string             `json:",omitempty"`
	AntiAffinityPolicyName string             `json:",omitempty"`
	CustomFields           []customfields.Def `json:",omitempty"`
	AdditionalDisks        []AddDiskRequest   `json:",omitempty"`
	Ttl                    time.Time          `json:"-"`
	TtlString              string             `json:"Ttl,omitempty"`
	Packages               []PackageDef       `json:",omitempty"`
	ConfigurationId        string             `json:",omitempty"`
	OsType                 string             `json:",omitempty"`
}

func (c *CreateReq) Validate() error {
	serverIdValues := []string{c.SourceServerId, c.TemplateName}
	numNonEmpty := 0
	for _, item := range serverIdValues {
		if item != "" {
			numNonEmpty++
		}
	}
	if numNonEmpty > 1 || numNonEmpty == 0 {
		return fmt.Errorf("Exactly one parameter from the following: source-server-id, source-server-name, template-name must be specified.")
	}

	if (c.GroupName == "") == (c.GroupId == "") {
		return fmt.Errorf("Exactly one parameter from the following: group-id, group-name must be specified.")
	}

	if (c.AntiAffinityPolicyId == "") == (c.AntiAffinityPolicyName == "") {
		return fmt.Errorf("Exactly one parameter from the following: anti-affinity-policy-id, anti-affinity-policy-name must be specified.")
	}

	if c.Type == "bareMetal" {
		if c.ConfigurationId == "" {
			return fmt.Errorf("ConfigurationId: required for bare metal servers.")
		}
		if c.OsType == "" {
			return fmt.Errorf("OsType: required for bare metal servers.")
		}
	}

	return nil
}

func (c *CreateReq) ApplyDefaultBehaviour() error {
	zeroTime := time.Time{}
	if c.Ttl != zeroTime {
		c.TtlString = c.Ttl.Format(timeFormat)
	}
	return nil
}

func (c *CreateReq) InferID(cn base.Connection) error {
	if c.TemplateName != "" {
		c.SourceServerId = c.TemplateName
	}

	if c.GroupName != "" {
		g := &group.Group{GroupName: c.GroupName}
		err := g.InferID(cn)
		if err != nil {
			return err
		}
		c.GroupId = g.GroupId
	}

	if c.AntiAffinityPolicyName != "" {
		p := &affinity.Policy{PolicyName: c.AntiAffinityPolicyName}
		err := p.InferID(cn)
		if err != nil {
			return err
		}
		c.AntiAffinityPolicyId = p.PolicyId
	}
	return nil
}

func (c *CreateReq) GetNames(cn base.Connection, property string) ([]string, error) {
	switch property {
	case "TemplateName":
		return LoadTemplates(cn)
	case "GroupName":
		return group.GetNames(cn, "all")
	case "AntiAffinityPolicyName":
		p := &affinity.Policy{}
		return p.GetNames(cn, "PolicyName")
	}
	return nil, nil
}
<commit_msg>Do not require affinity policy to be specified<commit_after>package server

import (
	"fmt"
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models/affinity"
	"github.com/centurylinkcloud/clc-go-cli/models/customfields"
	"github.com/centurylinkcloud/clc-go-cli/models/group"
	"time"
)

const (
	timeFormat = "2006-01-02 15:04:05"
)

type CreateReq struct {
	Name                   string `valid:"required"`
	Description            string `json:",omitempty"`
	GroupId                string
	GroupName              string `json:",omitempty"`
	SourceServerId         string
	TemplateName           string             `json:",omitempty"`
	IsManagedOs            bool               `json:",omitempty"`
	IsManagedBackup        bool               `json:",omitempty"`
	PrimaryDns             string             `json:",omitempty"`
	SecondaryDns           string             `json:",omitempty"`
	NetworkId              string             `json:",omitempty"`
	IpAddress              string             `json:",omitempty"`
	RootPassword           string             `json:",omitempty"`
	SourceServerPassword   string             `json:",omitempty"`
	Cpu                    int64              `valid:"required"`
	CpuAutoscalePolicyId   string             `json:",omitempty"`
	MemoryGb               int64              `valid:"required"`
	Type                   string             `valid:"required" oneOf:"standard,hyperscale,bareMetal"`
	StorageType            string             `json:",omitempty" oneOf:"standard,premium,hyperscale"`
	AntiAffinityPolicyId   string             `json:",omitempty"`
	AntiAffinityPolicyName string             `json:",omitempty"`
	CustomFields           []customfields.Def `json:",omitempty"`
	AdditionalDisks        []AddDiskRequest   `json:",omitempty"`
	Ttl                    time.Time          `json:"-"`
	TtlString              string             `json:"Ttl,omitempty"`
	Packages               []PackageDef       `json:",omitempty"`
	ConfigurationId        string             `json:",omitempty"`
	OsType                 string             `json:",omitempty"`
}

func (c *CreateReq) Validate() error {
	serverIdValues := []string{c.SourceServerId, c.TemplateName}
	numNonEmpty := 0
	for _, item := range serverIdValues {
		if item != "" {
			numNonEmpty++
		}
	}
	if numNonEmpty > 1 || numNonEmpty == 0 {
		return fmt.Errorf("Exactly one parameter from the following: source-server-id, source-server-name, template-name must be specified.")
	}

	if (c.GroupName == "") == (c.GroupId == "") {
		return fmt.Errorf("Exactly one parameter from the following: group-id, group-name must be specified.")
	}

	if c.AntiAffinityPolicyId != "" && c.AntiAffinityPolicyName != "" {
		return fmt.Errorf("Only one parameter from the following: anti-affinity-policy-id, anti-affinity-policy-name may be specified.")
	}

	if c.Type == "bareMetal" {
		if c.ConfigurationId == "" {
			return fmt.Errorf("ConfigurationId: required for bare metal servers.")
		}
		if c.OsType == "" {
			return fmt.Errorf("OsType: required for bare metal servers.")
		}
	}

	return nil
}

func (c *CreateReq) ApplyDefaultBehaviour() error {
	zeroTime := time.Time{}
	if c.Ttl != zeroTime {
		c.TtlString = c.Ttl.Format(timeFormat)
	}
	return nil
}

func (c *CreateReq) InferID(cn base.Connection) error {
	if c.TemplateName != "" {
		c.SourceServerId = c.TemplateName
	}

	if c.GroupName != "" {
		g := &group.Group{GroupName: c.GroupName}
		err := g.InferID(cn)
		if err != nil {
			return err
		}
		c.GroupId = g.GroupId
	}

	if c.AntiAffinityPolicyName != "" {
		p := &affinity.Policy{PolicyName: c.AntiAffinityPolicyName}
		err := p.InferID(cn)
		if err != nil {
			return err
		}
		c.AntiAffinityPolicyId = p.PolicyId
	}
	return nil
}

func (c *CreateReq) GetNames(cn base.Connection, property string) ([]string, error) {
	switch property {
	case "TemplateName":
		return LoadTemplates(cn)
	case "GroupName":
		return group.GetNames(cn, "all")
	case "AntiAffinityPolicyName":
		p := &affinity.Policy{}
		return p.GetNames(cn, "PolicyName")
	}
	return nil, nil
}
<|endoftext|>


<commit_before>// Package archive implements the pipe interface with the intent of
// archiving and compressing the binaries, readme, and other artifacts. It
// also provides an Archive interface which represents an archiving format.
package archive

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/caarlos0/log"
	"github.com/goreleaser/goreleaser/internal/archivefiles"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/internal/ids"
	"github.com/goreleaser/goreleaser/internal/semerrgroup"
	"github.com/goreleaser/goreleaser/internal/tmpl"
	"github.com/goreleaser/goreleaser/pkg/archive"
	"github.com/goreleaser/goreleaser/pkg/config"
	"github.com/goreleaser/goreleaser/pkg/context"
)

const (
	defaultNameTemplateSuffix = `{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}`
	defaultNameTemplate       = "{{ .ProjectName }}_" + defaultNameTemplateSuffix
	defaultBinaryNameTemplate = "{{ .Binary }}_" + defaultNameTemplateSuffix
)

// ErrArchiveDifferentBinaryCount happens when an archive uses several builds which have different goos/goarch/etc sets,
// causing the archives for some platforms to have more binaries than others.
// GoReleaser breaks in these cases as it will only cause confusion to other users.
var ErrArchiveDifferentBinaryCount = errors.New("archive has different count of built binaries for each platform, which may cause your users confusion. Please make sure all builds used have the same set of goos/goarch/etc or split it into multiple archives")

// nolint: gochecknoglobals
var lock sync.Mutex

// Pipe for archive.
type Pipe struct{}

func (Pipe) String() string {
	return "archives"
}

// Default sets the pipe defaults.
func (Pipe) Default(ctx *context.Context) error {
	ids := ids.New("archives")
	if len(ctx.Config.Archives) == 0 {
		ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{})
	}
	for i := range ctx.Config.Archives {
		archive := &ctx.Config.Archives[i]
		if archive.Format == "" {
			archive.Format = "tar.gz"
		}
		if archive.ID == "" {
			archive.ID = "default"
		}
		if len(archive.Files) == 0 {
			archive.Files = []config.File{
				{Source: "license*"},
				{Source: "LICENSE*"},
				{Source: "readme*"},
				{Source: "README*"},
				{Source: "changelog*"},
				{Source: "CHANGELOG*"},
			}
		}
		if archive.NameTemplate == "" {
			archive.NameTemplate = defaultNameTemplate
			if archive.Format == "binary" {
				archive.NameTemplate = defaultBinaryNameTemplate
			}
		}
		ids.Inc(archive.ID)
	}
	return ids.Validate()
}

// Run the pipe.
func (Pipe) Run(ctx *context.Context) error {
	g := semerrgroup.New(ctx.Parallelism)
	for i, archive := range ctx.Config.Archives {
		archive := archive
		if archive.Meta {
			return createMeta(ctx, archive)
		}

		filter := []artifact.Filter{artifact.Or(
			artifact.ByType(artifact.Binary),
			artifact.ByType(artifact.UniversalBinary),
		)}
		if len(archive.Builds) > 0 {
			filter = append(filter, artifact.ByIDs(archive.Builds...))
		}
		artifacts := ctx.Artifacts.Filter(artifact.And(filter...)).GroupByPlatform()
		if err := checkArtifacts(artifacts); err != nil && !archive.AllowDifferentBinaryCount {
			return fmt.Errorf("invalid archive: %d: %w", i, ErrArchiveDifferentBinaryCount)
		}
		for group, artifacts := range artifacts {
			log.Debugf("group %s has %d binaries", group, len(artifacts))
			artifacts := artifacts
			g.Go(func() error {
				if packageFormat(archive, artifacts[0].Goos) == "binary" {
					return skip(ctx, archive, artifacts)
				}
				return create(ctx, archive, artifacts)
			})
		}
	}
	return g.Wait()
}

func checkArtifacts(artifacts map[string][]*artifact.Artifact) error {
	lens := map[int]bool{}
	for _, v := range artifacts {
		lens[len(v)] = true
	}
	if len(lens) <= 1 {
		return nil
	}
	return ErrArchiveDifferentBinaryCount
}

func createMeta(ctx *context.Context, arch config.Archive) error {
	return doCreate(ctx, arch, nil, arch.Format, tmpl.New(ctx))
}

func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact) error {
	template := tmpl.New(ctx).WithArtifact(binaries[0], arch.Replacements)
	format := packageFormat(arch, binaries[0].Goos)
	return doCreate(ctx, arch, binaries, format, template)
}

func doCreate(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact, format string, template *tmpl.Template) error {
	folder, err := template.Apply(arch.NameTemplate)
	if err != nil {
		return err
	}
	archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format)
	lock.Lock()
	if err := os.MkdirAll(filepath.Dir(archivePath), 0o755|os.ModeDir); err != nil {
		lock.Unlock()
		return err
	}
	if _, err = os.Stat(archivePath); !os.IsNotExist(err) {
		lock.Unlock()
		return fmt.Errorf("archive named %s already exists. Check your archive name template", archivePath)
	}
	archiveFile, err := os.Create(archivePath)
	if err != nil {
		lock.Unlock()
		return fmt.Errorf("failed to create directory %s: %w", archivePath, err)
	}
	lock.Unlock()
	defer archiveFile.Close()

	log := log.WithField("archive", archivePath)
	log.Info("creating")

	wrap, err := template.Apply(wrapFolder(arch))
	if err != nil {
		return err
	}
	a, err := archive.New(archiveFile, format)
	if err != nil {
		return err
	}
	a = NewEnhancedArchive(a, wrap)
	defer a.Close()

	files, err := archivefiles.Eval(template, arch.Files)
	if err != nil {
		return fmt.Errorf("failed to find files to archive: %w", err)
	}
	if arch.Meta && len(files) == 0 {
		return fmt.Errorf("no files found")
	}
	for _, f := range files {
		if err = a.Add(f); err != nil {
			return fmt.Errorf("failed to add: '%s' -> '%s': %w", f.Source, f.Destination, err)
		}
	}
	bins := []string{}
	for _, binary := range binaries {
		if err := a.Add(config.File{
			Source:      binary.Path,
			Destination: binary.Name,
		}); err != nil {
			return fmt.Errorf("failed to add: '%s' -> '%s': %w", binary.Path, binary.Name, err)
		}
		bins = append(bins, binary.Name)
	}
	art := &artifact.Artifact{
		Type: artifact.UploadableArchive,
		Name: folder + "." + format,
		Path: archivePath,
		Extra: map[string]interface{}{
			artifact.ExtraBuilds:    binaries,
			artifact.ExtraID:        arch.ID,
			artifact.ExtraFormat:    arch.Format,
			artifact.ExtraWrappedIn: wrap,
			artifact.ExtraBinaries:  bins,
		},
	}
	if len(binaries) > 0 {
		art.Goos = binaries[0].Goos
		art.Goarch = binaries[0].Goarch
		art.Goarm = binaries[0].Goarm
		art.Gomips = binaries[0].Gomips
		art.Goamd64 = binaries[0].Goamd64
		art.Extra[artifact.ExtraReplaces] = binaries[0].Extra[artifact.ExtraReplaces]
	}

	ctx.Artifacts.Add(art)
	return nil
}

func wrapFolder(a config.Archive) string {
	switch a.WrapInDirectory {
	case "true":
		return a.NameTemplate
	case "false":
		return ""
	default:
		return a.WrapInDirectory
	}
}

func skip(ctx *context.Context, archive config.Archive, binaries []*artifact.Artifact) error {
	for _, binary := range binaries {
		name, err := tmpl.New(ctx).
			WithArtifact(binary, archive.Replacements).
			Apply(archive.NameTemplate)
		if err != nil {
			return err
		}
		finalName := name + artifact.ExtraOr(*binary, artifact.ExtraExt, "")
		log.WithField("binary", binary.Name).
			WithField("name", finalName).
			Info("skip archiving")
		ctx.Artifacts.Add(&artifact.Artifact{
			Type:    artifact.UploadableBinary,
			Name:    finalName,
			Path:    binary.Path,
			Goos:    binary.Goos,
			Goarch:  binary.Goarch,
			Goarm:   binary.Goarm,
			Gomips:  binary.Gomips,
			Goamd64: binary.Goamd64,
			Extra: map[string]interface{}{
				artifact.ExtraBuilds:   []*artifact.Artifact{binary},
				artifact.ExtraID:       archive.ID,
				artifact.ExtraFormat:   archive.Format,
				artifact.ExtraBinary:   binary.Name,
				artifact.ExtraReplaces: binaries[0].Extra[artifact.ExtraReplaces],
			},
		})
	}
	return nil
}

func packageFormat(archive config.Archive, platform string) string {
	for _, override := range archive.FormatOverrides {
		if strings.HasPrefix(platform, override.Goos) {
			return override.Format
		}
	}
	return archive.Format
}

// NewEnhancedArchive enhances a pre-existing archive.Archive instance
// with this pipe specifics.
func NewEnhancedArchive(a archive.Archive, wrap string) archive.Archive {
	return EnhancedArchive{
		a:     a,
		wrap:  wrap,
		files: map[string]string{},
	}
}

// EnhancedArchive is an archive.Archive implementation which decorates an
// archive.Archive adding wrap directory support, logging and windows
// backslash fixes.
type EnhancedArchive struct {
	a     archive.Archive
	wrap  string
	files map[string]string
}

// Add adds a file.
func (d EnhancedArchive) Add(f config.File) error {
	name := strings.ReplaceAll(filepath.Join(d.wrap, f.Destination), "\\", "/")
	log.Debugf("adding file: %s as %s", f.Source, name)
	if _, ok := d.files[f.Destination]; ok {
		return fmt.Errorf("file %s already exists in the archive", f.Destination)
	}
	d.files[f.Destination] = name
	ff := config.File{
		Source:      f.Source,
		Destination: name,
		Info:        f.Info,
	}
	return d.a.Add(ff)
}

// Close closes the underlying archive.
func (d EnhancedArchive) Close() error {
	return d.a.Close()
}
<commit_msg>refactor: make it easier to merge<commit_after>// Package archive implements the pipe interface with the intent of
// archiving and compressing the binaries, readme, and other artifacts. It
// also provides an Archive interface which represents an archiving format.
package archive

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/caarlos0/log"
	"github.com/goreleaser/goreleaser/internal/archivefiles"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/internal/ids"
	"github.com/goreleaser/goreleaser/internal/semerrgroup"
	"github.com/goreleaser/goreleaser/internal/tmpl"
	"github.com/goreleaser/goreleaser/pkg/archive"
	"github.com/goreleaser/goreleaser/pkg/config"
	"github.com/goreleaser/goreleaser/pkg/context"
)

const (
	defaultNameTemplateSuffix = `{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}`
	defaultNameTemplate       = "{{ .ProjectName }}_" + defaultNameTemplateSuffix
	defaultBinaryNameTemplate = "{{ .Binary }}_" + defaultNameTemplateSuffix
)

// ErrArchiveDifferentBinaryCount happens when an archive uses several builds which have different goos/goarch/etc sets,
// causing the archives for some platforms to have more binaries than others.
// GoReleaser breaks in these cases as it will only cause confusion to other users.
var ErrArchiveDifferentBinaryCount = errors.New("archive has different count of built binaries for each platform, which may cause your users confusion. Please make sure all builds used have the same set of goos/goarch/etc or split it into multiple archives")

// nolint: gochecknoglobals
var lock sync.Mutex

// Pipe for archive.
type Pipe struct{}

func (Pipe) String() string {
	return "archives"
}

// Default sets the pipe defaults.
func (Pipe) Default(ctx *context.Context) error {
	ids := ids.New("archives")
	if len(ctx.Config.Archives) == 0 {
		ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{})
	}
	for i := range ctx.Config.Archives {
		archive := &ctx.Config.Archives[i]
		if archive.Format == "" {
			archive.Format = "tar.gz"
		}
		if archive.ID == "" {
			archive.ID = "default"
		}
		if len(archive.Files) == 0 {
			archive.Files = []config.File{
				{Source: "license*"},
				{Source: "LICENSE*"},
				{Source: "readme*"},
				{Source: "README*"},
				{Source: "changelog*"},
				{Source: "CHANGELOG*"},
			}
		}
		if archive.NameTemplate == "" {
			archive.NameTemplate = defaultNameTemplate
			if archive.Format == "binary" {
				archive.NameTemplate = defaultBinaryNameTemplate
			}
		}
		ids.Inc(archive.ID)
	}
	return ids.Validate()
}

// Run the pipe.
func (Pipe) Run(ctx *context.Context) error {
	g := semerrgroup.New(ctx.Parallelism)
	for i, archive := range ctx.Config.Archives {
		archive := archive
		if archive.Meta {
			return createMeta(ctx, archive)
		}

		filter := []artifact.Filter{artifact.Or(
			artifact.ByType(artifact.Binary),
			artifact.ByType(artifact.UniversalBinary),
		)}
		if len(archive.Builds) > 0 {
			filter = append(filter, artifact.ByIDs(archive.Builds...))
		}
		artifacts := ctx.Artifacts.Filter(artifact.And(filter...)).GroupByPlatform()
		if err := checkArtifacts(artifacts); err != nil && !archive.AllowDifferentBinaryCount {
			return fmt.Errorf("invalid archive: %d: %w", i, ErrArchiveDifferentBinaryCount)
		}
		for group, artifacts := range artifacts {
			log.Debugf("group %s has %d binaries", group, len(artifacts))
			artifacts := artifacts
			if packageFormat(archive, artifacts[0].Goos) == "binary" {
				g.Go(func() error {
					return skip(ctx, archive, artifacts)
				})
				continue
			}
			g.Go(func() error {
				if err := create(ctx, archive, artifacts); err != nil {
					return err
				}
				return nil
			})
		}
	}
	return g.Wait()
}

func checkArtifacts(artifacts map[string][]*artifact.Artifact) error {
	lens := map[int]bool{}
	for _, v := range artifacts {
		lens[len(v)] = true
	}
	if len(lens) <= 1 {
		return nil
	}
	return ErrArchiveDifferentBinaryCount
}

func createMeta(ctx *context.Context, arch config.Archive) error {
	return doCreate(ctx, arch, nil, arch.Format, tmpl.New(ctx))
}

func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact) error {
	template := tmpl.New(ctx).WithArtifact(binaries[0], arch.Replacements)
	format := packageFormat(arch, binaries[0].Goos)
	return doCreate(ctx, arch, binaries, format, template)
}

func doCreate(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact, format string, template *tmpl.Template) error {
	folder, err := template.Apply(arch.NameTemplate)
	if err != nil {
		return err
	}
	archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format)
	lock.Lock()
	if err := os.MkdirAll(filepath.Dir(archivePath), 0o755|os.ModeDir); err != nil {
		lock.Unlock()
		return err
	}
	if _, err = os.Stat(archivePath); !os.IsNotExist(err) {
		lock.Unlock()
		return fmt.Errorf("archive named %s already exists. Check your archive name template", archivePath)
	}
	archiveFile, err := os.Create(archivePath)
	if err != nil {
		lock.Unlock()
		return fmt.Errorf("failed to create directory %s: %w", archivePath, err)
	}
	lock.Unlock()
	defer archiveFile.Close()

	log := log.WithField("archive", archivePath)
	log.Info("creating")

	wrap, err := template.Apply(wrapFolder(arch))
	if err != nil {
		return err
	}
	a, err := archive.New(archiveFile, format)
	if err != nil {
		return err
	}
	a = NewEnhancedArchive(a, wrap)
	defer a.Close()

	files, err := archivefiles.Eval(template, arch.Files)
	if err != nil {
		return fmt.Errorf("failed to find files to archive: %w", err)
	}
	if arch.Meta && len(files) == 0 {
		return fmt.Errorf("no files found")
	}
	for _, f := range files {
		if err = a.Add(f); err != nil {
			return fmt.Errorf("failed to add: '%s' -> '%s': %w", f.Source, f.Destination, err)
		}
	}
	bins := []string{}
	for _, binary := range binaries {
		if err := a.Add(config.File{
			Source:      binary.Path,
			Destination: binary.Name,
		}); err != nil {
			return fmt.Errorf("failed to add: '%s' -> '%s': %w", binary.Path, binary.Name, err)
		}
		bins = append(bins, binary.Name)
	}
	art := &artifact.Artifact{
		Type: artifact.UploadableArchive,
		Name: folder + "." + format,
		Path: archivePath,
		Extra: map[string]interface{}{
			artifact.ExtraBuilds:    binaries,
			artifact.ExtraID:        arch.ID,
			artifact.ExtraFormat:    arch.Format,
			artifact.ExtraWrappedIn: wrap,
			artifact.ExtraBinaries:  bins,
		},
	}
	if len(binaries) > 0 {
		art.Goos = binaries[0].Goos
		art.Goarch = binaries[0].Goarch
		art.Goarm = binaries[0].Goarm
		art.Gomips = binaries[0].Gomips
		art.Goamd64 = binaries[0].Goamd64
		art.Extra[artifact.ExtraReplaces] = binaries[0].Extra[artifact.ExtraReplaces]
	}

	ctx.Artifacts.Add(art)
	return nil
}

func wrapFolder(a config.Archive) string {
	switch a.WrapInDirectory {
	case "true":
		return a.NameTemplate
	case "false":
		return ""
	default:
		return a.WrapInDirectory
	}
}

func skip(ctx *context.Context, archive config.Archive, binaries []*artifact.Artifact) error {
	for _, binary := range binaries {
		name, err := tmpl.New(ctx).
			WithArtifact(binary, archive.Replacements).
			Apply(archive.NameTemplate)
		if err != nil {
			return err
		}
		finalName := name + artifact.ExtraOr(*binary, artifact.ExtraExt, "")
		log.WithField("binary", binary.Name).
			WithField("name", finalName).
			Info("skip archiving")
		ctx.Artifacts.Add(&artifact.Artifact{
			Type:    artifact.UploadableBinary,
			Name:    finalName,
			Path:    binary.Path,
			Goos:    binary.Goos,
			Goarch:  binary.Goarch,
			Goarm:   binary.Goarm,
			Gomips:  binary.Gomips,
			Goamd64: binary.Goamd64,
			Extra: map[string]interface{}{
				artifact.ExtraBuilds:   []*artifact.Artifact{binary},
				artifact.ExtraID:       archive.ID,
				artifact.ExtraFormat:   archive.Format,
				artifact.ExtraBinary:   binary.Name,
				artifact.ExtraReplaces: binaries[0].Extra[artifact.ExtraReplaces],
			},
		})
	}
	return nil
}

func packageFormat(archive config.Archive, platform string) string {
	for _, override := range archive.FormatOverrides {
		if strings.HasPrefix(platform, override.Goos) {
			return override.Format
		}
	}
	return archive.Format
}

// NewEnhancedArchive enhances a pre-existing archive.Archive instance
// with this pipe specifics.
func NewEnhancedArchive(a archive.Archive, wrap string) archive.Archive {
	return EnhancedArchive{
		a:     a,
		wrap:  wrap,
		files: map[string]string{},
	}
}

// EnhancedArchive is an archive.Archive implementation which decorates an
// archive.Archive adding wrap directory support, logging and windows
// backslash fixes.
type EnhancedArchive struct {
	a     archive.Archive
	wrap  string
	files map[string]string
}

// Add adds a file.
func (d EnhancedArchive) Add(f config.File) error {
	name := strings.ReplaceAll(filepath.Join(d.wrap, f.Destination), "\\", "/")
	log.Debugf("adding file: %s as %s", f.Source, name)
	if _, ok := d.files[f.Destination]; ok {
		return fmt.Errorf("file %s already exists in the archive", f.Destination)
	}
	d.files[f.Destination] = name
	ff := config.File{
		Source:      f.Source,
		Destination: name,
		Info:        f.Info,
	}
	return d.a.Add(ff)
}

// Close closes the underlying archive.
func (d EnhancedArchive) Close() error {
	return d.a.Close()
}
<|endoftext|>


<commit_before>// Package publish contains the publishing pipe.
package publish

import (
	"fmt"

	"github.com/apex/log"
	"github.com/fatih/color"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/pipe/artifactory"
	"github.com/goreleaser/goreleaser/internal/pipe/brew"
	"github.com/goreleaser/goreleaser/internal/pipe/docker"
	"github.com/goreleaser/goreleaser/internal/pipe/put"
	"github.com/goreleaser/goreleaser/internal/pipe/release"
	"github.com/goreleaser/goreleaser/internal/pipe/s3"
	"github.com/goreleaser/goreleaser/internal/pipe/scoop"
	"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
	"github.com/goreleaser/goreleaser/pkg/context"
	"github.com/pkg/errors"
)

// Pipe that publishes artifacts
type Pipe struct{}

func (Pipe) String() string {
	return "publishing artifacts"
}

// Publisher should be implemented by pipes that want to publish artifacts
type Publisher interface {
	fmt.Stringer

	// Default sets the configuration defaults
	Publish(ctx *context.Context) error
}

var publishers = []Publisher{
	s3.Pipe{},
	put.Pipe{},
	artifactory.Pipe{},
	release.Pipe{},
	brew.Pipe{},
	scoop.Pipe{},
	docker.Pipe{},
	snapcraft.Pipe{},
}

var bold = color.New(color.Bold)

// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
	if ctx.SkipPublish {
		return pipe.ErrSkipPublishEnabled
	}
	for _, publisher := range publishers {
		log.Infof(bold.Sprint(publisher.String()))
		if err := handle(publisher.Publish(ctx)); err != nil {
			return errors.Wrapf(err, "%s: failed to publish artifacts", publisher.String())
		}
	}
	return nil
}

// TODO: for now this is duplicated, we should have better error handling
// eventually.
func handle(err error) error {
	if err == nil {
		return nil
	}
	if pipe.IsSkip(err) {
		log.WithField("reason", err.Error()).Warn("skipped")
		return nil
	}
	return err
}
<commit_msg>fix: Bring back docker release notes<commit_after>// Package publish contains the publishing pipe.
package publish

import (
	"fmt"

	"github.com/apex/log"
	"github.com/fatih/color"
	"github.com/goreleaser/goreleaser/internal/pipe"
	"github.com/goreleaser/goreleaser/internal/pipe/artifactory"
	"github.com/goreleaser/goreleaser/internal/pipe/brew"
	"github.com/goreleaser/goreleaser/internal/pipe/docker"
	"github.com/goreleaser/goreleaser/internal/pipe/put"
	"github.com/goreleaser/goreleaser/internal/pipe/release"
	"github.com/goreleaser/goreleaser/internal/pipe/s3"
	"github.com/goreleaser/goreleaser/internal/pipe/scoop"
	"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
	"github.com/goreleaser/goreleaser/pkg/context"
	"github.com/pkg/errors"
)

// Pipe that publishes artifacts
type Pipe struct{}

func (Pipe) String() string {
	return "publishing artifacts"
}

// Publisher should be implemented by pipes that want to publish artifacts
type Publisher interface {
	fmt.Stringer

	// Default sets the configuration defaults
	Publish(ctx *context.Context) error
}

var publishers = []Publisher{
	s3.Pipe{},
	put.Pipe{},
	artifactory.Pipe{},
	brew.Pipe{},
	scoop.Pipe{},
	docker.Pipe{},
	snapcraft.Pipe{},
	// This should be the last step in the pipeline to ensure that the
	// release notes are complete.
	release.Pipe{},
}

var bold = color.New(color.Bold)

// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
	if ctx.SkipPublish {
		return pipe.ErrSkipPublishEnabled
	}
	for _, publisher := range publishers {
		log.Infof(bold.Sprint(publisher.String()))
		if err := handle(publisher.Publish(ctx)); err != nil {
			return errors.Wrapf(err, "%s: failed to publish artifacts", publisher.String())
		}
	}
	return nil
}

// TODO: for now this is duplicated, we should have better error handling
// eventually.
func handle(err error) error {
	if err == nil {
		return nil
	}
	if pipe.IsSkip(err) {
		log.WithField("reason", err.Error()).Warn("skipped")
		return nil
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2020 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 socket

import (
	"syscall"
	"unsafe"
)

func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)
func syscall_syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)

func probeProtocolStack() int {
	return 4 // sizeof(int) on GOOS=zos GOARCH=s390x
}

func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
	l := uint32(len(b))
	_, _, errno := syscall_syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
	return int(l), errnoErr(errno)
}

func setsockopt(s uintptr, level, name int, b []byte) error {
	_, _, errno := syscall_syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
	return errnoErr(errno)
}

func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) {
	var h msghdr
	vs := make([]iovec, len(buffers))
	var sa []byte
	if network != "tcp" {
		sa = make([]byte, sizeofSockaddrInet6)
	}
	h.pack(vs, buffers, oob, sa)
	sn, _, errno := syscall_syscall(syscall.SYS___RECVMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags))
	n = int(sn)
	oobn = h.controllen()
	recvflags = h.flags()
	err = errnoErr(errno)
	if network != "tcp" {
		var err2 error
		from, err2 = parseInetAddr(sa, network)
		if err2 != nil && err == nil {
			err = err2
		}
	}
	return
}

func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) {
	var h msghdr
	vs := make([]iovec, len(buffers))
	var sa []byte
	if to != nil {
		var a [sizeofSockaddrInet6]byte
		n := marshalInetAddr(to, a[:])
		sa = a[:n]
	}
	h.pack(vs, buffers, oob, sa)
	n, _, errno := syscall_syscall(syscall.SYS___SENDMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags))
	return int(n), errnoErr(errno)
}
<commit_msg>internal/socket: add missing import to zos-s390x file<commit_after>// Copyright 2020 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 socket

import (
	"net"
	"syscall"
	"unsafe"
)

func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)
func syscall_syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)

func probeProtocolStack() int {
	return 4 // sizeof(int) on GOOS=zos GOARCH=s390x
}

func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
	l := uint32(len(b))
	_, _, errno := syscall_syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
	return int(l), errnoErr(errno)
}

func setsockopt(s uintptr, level, name int, b []byte) error {
	_, _, errno := syscall_syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
	return errnoErr(errno)
}

func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) {
	var h msghdr
	vs := make([]iovec, len(buffers))
	var sa []byte
	if network != "tcp" {
		sa = make([]byte, sizeofSockaddrInet6)
	}
	h.pack(vs, buffers, oob, sa)
	sn, _, errno := syscall_syscall(syscall.SYS___RECVMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags))
	n = int(sn)
	oobn = h.controllen()
	recvflags = h.flags()
	err = errnoErr(errno)
	if network != "tcp" {
		var err2 error
		from, err2 = parseInetAddr(sa, network)
		if err2 != nil && err == nil {
			err = err2
		}
	}
	return
}

func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) {
	var h msghdr
	vs := make([]iovec, len(buffers))
	var sa []byte
	if to != nil {
		var a [sizeofSockaddrInet6]byte
		n := marshalInetAddr(to, a[:])
		sa = a[:n]
	}
	h.pack(vs, buffers, oob, sa)
	n, _, errno := syscall_syscall(syscall.SYS___SENDMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags))
	return int(n), errnoErr(errno)
}
<|endoftext|>


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

package convert

import (
	"fmt"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
	repo_module "code.gitea.io/gitea/modules/repository"
	api "code.gitea.io/gitea/modules/structs"
)

// ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
	var (
		baseBranch *git.Branch
		headBranch *git.Branch
		baseCommit *git.Commit
		err        error
	)

	if err = pr.Issue.LoadRepo(); err != nil {
		log.Error("pr.Issue.LoadRepo[%d]: %v", pr.ID, err)
		return nil
	}

	apiIssue := ToAPIIssue(pr.Issue)
	if err := pr.LoadBaseRepo(); err != nil {
		log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
		return nil
	}

	if err := pr.LoadHeadRepo(); err != nil {
		log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
		return nil
	}

	apiPullRequest := &api.PullRequest{
		ID:        pr.ID,
		URL:       pr.Issue.HTMLURL(),
		Index:     pr.Index,
		Poster:    apiIssue.Poster,
		Title:     apiIssue.Title,
		Body:      apiIssue.Body,
		Labels:    apiIssue.Labels,
		Milestone: apiIssue.Milestone,
		Assignee:  apiIssue.Assignee,
		Assignees: apiIssue.Assignees,
		State:     apiIssue.State,
		IsLocked:  apiIssue.IsLocked,
		Comments:  apiIssue.Comments,
		HTMLURL:   pr.Issue.HTMLURL(),
		DiffURL:   pr.Issue.DiffURL(),
		PatchURL:  pr.Issue.PatchURL(),
		HasMerged: pr.HasMerged,
		MergeBase: pr.MergeBase,
		Deadline:  apiIssue.Deadline,
		Created:   pr.Issue.CreatedUnix.AsTimePtr(),
		Updated:   pr.Issue.UpdatedUnix.AsTimePtr(),

		Base: &api.PRBranchInfo{
			Name:       pr.BaseBranch,
			Ref:        pr.BaseBranch,
			RepoID:     pr.BaseRepoID,
			Repository: ToRepo(pr.BaseRepo, models.AccessModeNone),
		},
		Head: &api.PRBranchInfo{
			Name:   pr.HeadBranch,
			Ref:    fmt.Sprintf("refs/pull/%d/head", pr.Index),
			RepoID: -1,
		},
	}

	baseBranch, err = repo_module.GetBranch(pr.BaseRepo, pr.BaseBranch)
	if err != nil && !git.IsErrBranchNotExist(err) {
		log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
		return nil
	}

	if err == nil {
		baseCommit, err = baseBranch.GetCommit()
		if err != nil && !git.IsErrNotExist(err) {
			log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
			return nil
		}

		if err == nil {
			apiPullRequest.Base.Sha = baseCommit.ID.String()
		}
	}

	if pr.HeadRepo != nil {
		apiPullRequest.Head.RepoID = pr.HeadRepo.ID
		apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, models.AccessModeNone)

		headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
		if err != nil {
			log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err)
			return nil
		}
		defer headGitRepo.Close()

		headBranch, err = headGitRepo.GetBranch(pr.HeadBranch)
		if err != nil && !git.IsErrBranchNotExist(err) {
			log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
			return nil
		}

		if git.IsErrBranchNotExist(err) {
			headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
			if err != nil && !git.IsErrNotExist(err) {
				log.Error("GetCommit[%s]: %v", pr.HeadBranch, err)
				return nil
			}
			if err == nil {
				apiPullRequest.Head.Sha = headCommitID
			}
		} else {
			commit, err := headBranch.GetCommit()
			if err != nil && !git.IsErrNotExist(err) {
				log.Error("GetCommit[%s]: %v", headBranch.Name, err)
				return nil
			}
			if err == nil {
				apiPullRequest.Head.Ref = pr.HeadBranch
				apiPullRequest.Head.Sha = commit.ID.String()
			}
		}
	}

	if pr.Status != models.PullRequestStatusChecking {
		mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
		apiPullRequest.Mergeable = mergeable
	}
	if pr.HasMerged {
		apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
		apiPullRequest.MergedCommitID = &pr.MergedCommitID
		apiPullRequest.MergedBy = ToUser(pr.Merger, false, false)
	}

	return apiPullRequest
}
<commit_msg>[API] get pull, return head branch sha, even if deleted (#14931)<commit_after>// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package convert

import (
	"fmt"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
	repo_module "code.gitea.io/gitea/modules/repository"
	api "code.gitea.io/gitea/modules/structs"
)

// ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
	var (
		baseBranch *git.Branch
		headBranch *git.Branch
		baseCommit *git.Commit
		err        error
	)

	if err = pr.Issue.LoadRepo(); err != nil {
		log.Error("pr.Issue.LoadRepo[%d]: %v", pr.ID, err)
		return nil
	}

	apiIssue := ToAPIIssue(pr.Issue)
	if err := pr.LoadBaseRepo(); err != nil {
		log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
		return nil
	}

	if err := pr.LoadHeadRepo(); err != nil {
		log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
		return nil
	}

	apiPullRequest := &api.PullRequest{
		ID:        pr.ID,
		URL:       pr.Issue.HTMLURL(),
		Index:     pr.Index,
		Poster:    apiIssue.Poster,
		Title:     apiIssue.Title,
		Body:      apiIssue.Body,
		Labels:    apiIssue.Labels,
		Milestone: apiIssue.Milestone,
		Assignee:  apiIssue.Assignee,
		Assignees: apiIssue.Assignees,
		State:     apiIssue.State,
		IsLocked:  apiIssue.IsLocked,
		Comments:  apiIssue.Comments,
		HTMLURL:   pr.Issue.HTMLURL(),
		DiffURL:   pr.Issue.DiffURL(),
		PatchURL:  pr.Issue.PatchURL(),
		HasMerged: pr.HasMerged,
		MergeBase: pr.MergeBase,
		Deadline:  apiIssue.Deadline,
		Created:   pr.Issue.CreatedUnix.AsTimePtr(),
		Updated:   pr.Issue.UpdatedUnix.AsTimePtr(),

		Base: &api.PRBranchInfo{
			Name:       pr.BaseBranch,
			Ref:        pr.BaseBranch,
			RepoID:     pr.BaseRepoID,
			Repository: ToRepo(pr.BaseRepo, models.AccessModeNone),
		},
		Head: &api.PRBranchInfo{
			Name:   pr.HeadBranch,
			Ref:    fmt.Sprintf("refs/pull/%d/head", pr.Index),
			RepoID: -1,
		},
	}

	baseBranch, err = repo_module.GetBranch(pr.BaseRepo, pr.BaseBranch)
	if err != nil && !git.IsErrBranchNotExist(err) {
		log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
		return nil
	}

	if err == nil {
		baseCommit, err = baseBranch.GetCommit()
		if err != nil && !git.IsErrNotExist(err) {
			log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
			return nil
		}

		if err == nil {
			apiPullRequest.Base.Sha = baseCommit.ID.String()
		}
	}

	if pr.HeadRepo != nil {
		apiPullRequest.Head.RepoID = pr.HeadRepo.ID
		apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, models.AccessModeNone)

		headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
		if err != nil {
			log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err)
			return nil
		}
		defer headGitRepo.Close()

		headBranch, err = headGitRepo.GetBranch(pr.HeadBranch)
		if err != nil && !git.IsErrBranchNotExist(err) {
			log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
			return nil
		}

		if git.IsErrBranchNotExist(err) {
			headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
			if err != nil && !git.IsErrNotExist(err) {
				log.Error("GetCommit[%s]: %v", pr.HeadBranch, err)
				return nil
			}
			if err == nil {
				apiPullRequest.Head.Sha = headCommitID
			}
		} else {
			commit, err := headBranch.GetCommit()
			if err != nil && !git.IsErrNotExist(err) {
				log.Error("GetCommit[%s]: %v", headBranch.Name, err)
				return nil
			}
			if err == nil {
				apiPullRequest.Head.Ref = pr.HeadBranch
				apiPullRequest.Head.Sha = commit.ID.String()
			}
		}
	}

	if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
		baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
		if err != nil {
			log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err)
			return nil
		}
		defer baseGitRepo.Close()
		refs, err := baseGitRepo.GetRefsFiltered(apiPullRequest.Head.Ref)
		if err != nil {
			log.Error("GetRefsFiltered[%s]: %v", apiPullRequest.Head.Ref, err)
			return nil
		} else if len(refs) == 0 {
			log.Error("unable to resolve PR head ref")
		} else {
			apiPullRequest.Head.Sha = refs[0].Object.String()
		}
	}

	if pr.Status != models.PullRequestStatusChecking {
		mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
		apiPullRequest.Mergeable = mergeable
	}
	if pr.HasMerged {
		apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
		apiPullRequest.MergedCommitID = &pr.MergedCommitID
		apiPullRequest.MergedBy = ToUser(pr.Merger, false, false)
	}

	return apiPullRequest
}
<|endoftext|>


<commit_before>package config

import (
	"crypto/md5"
	"fmt"
	"log"
)

const (
	MAX_INDEX_RECORDS   = 1022
	INDEX_RECORD_SIZE   = 32
	INDEX_METADATA_SIZE = 32
)

const (
	INDEX_REC_STATUS_INACTIVE = 0x0
	INDEX_REC_STATUS_ACTIVE   = 0x1
	INDEX_REC_STATUS_DELETED  = 0x2
)

type indexMetaData struct {
	count   uint32
	padding [28]byte
}

// Represents an index record. The offset
type indexRecord struct {
	key        [16]byte //md5 hash of the config key
	dataOffset uint32
	dataLength uint32
	status     byte
	padding    [7]byte
}

type indexBlock struct {
	indexMetaData
	indices [MAX_INDEX_RECORDS]indexRecord
}

func (rec indexRecord) print() {
	log.Printf("[%x : %x : %d]\n", rec.key, rec.dataOffset, rec.dataLength)
}

func (iBlock *indexBlock) print() {
	log.Printf("Total Index count: [%d]\n", iBlock.count)
	//for i := uint32(0); i < iBlock.count; i++ {
	//iBlock.indices[i].print()
	//}
}

func (iBlock *indexBlock) set(
	key string,
	offset uint32,
	length uint32) (err error) {

	//log.Printf("Index size: %d\n", iBlock.count)
	if iBlock.count >= MAX_INDEX_RECORDS {
		return Error{ERR_INDEX_FULL, fmt.Sprintf("key [%s]", key)}
	}
	// TODO: Check for key existance and overwrite.
	// Add the new index record at the end.
	rec := &(iBlock.indices[iBlock.count])

	keyHash := getKeyHash(key)
	rec.key = keyHash
	rec.dataOffset = offset
	rec.dataLength = length
	rec.status = INDEX_REC_STATUS_ACTIVE

	iBlock.count++
	return nil
}

func (iBlock *indexBlock) get(
	key string) (offset uint32, length uint32, err error) {
	indexRec, err := iBlock.find(key)
	if err != nil {
		return
	}
	if indexRec.status != INDEX_REC_STATUS_ACTIVE {
		err = Error{
			ERR_INDEX_INACTIVE,
			fmt.Sprintf("key [%s], status[%d]", key, indexRec.status)}
		return
	}
	return indexRec.dataOffset, indexRec.dataLength, nil
}

func (iBlock *indexBlock) delete(key string) error {
	if iBlock.count == 0 {
		return nil
	}
	indexRecPtr, err := iBlock.find(key)
	if err != nil {
		return err
	}
	if indexRecPtr.status != INDEX_REC_STATUS_ACTIVE {
		indexRecPtr.status = INDEX_REC_STATUS_DELETED
		iBlock.count--
	}
	return nil
}

func (iBlock *indexBlock) find(key string) (*indexRecord, error) {
	inputKeyHash := getKeyHash(key)
	for i := uint32(0); i < iBlock.count; i++ {
		indexRecPtr := &(iBlock.indices[i])
		if indexRecPtr.key == inputKeyHash {
			return indexRecPtr, nil
		}
	}
	return nil, Error{ERR_INDEX_KEY_NOT_FOUND, fmt.Sprintf("key [%s]", key)}
}

func getKeyHash(key string) (ret [16]byte) {
	// md5 the key
	h := md5.New()
	h.Write([]byte(key))
	hash := h.Sum(nil)

	// TODO: Fix this copying. Make sure it copies only 16 bytes.
	copy(ret[:], hash)
	return ret
}
<commit_msg>[Config] Fix incorrect error reporting while deleting a key in an empty config<commit_after>package config

import (
	"crypto/md5"
	"fmt"
	"log"
)

const (
	MAX_INDEX_RECORDS   = 1022
	INDEX_RECORD_SIZE   = 32
	INDEX_METADATA_SIZE = 32
)

const (
	INDEX_REC_STATUS_INACTIVE = 0x0
	INDEX_REC_STATUS_ACTIVE   = 0x1
	INDEX_REC_STATUS_DELETED  = 0x2
)

type indexMetaData struct {
	count   uint32
	padding [28]byte
}

// Represents an index record. The offset
type indexRecord struct {
	key        [16]byte //md5 hash of the config key
	dataOffset uint32
	dataLength uint32
	status     byte
	padding    [7]byte
}

type indexBlock struct {
	indexMetaData
	indices [MAX_INDEX_RECORDS]indexRecord
}

func (rec indexRecord) print() {
	log.Printf("[%x : %x : %d]\n", rec.key, rec.dataOffset, rec.dataLength)
}

func (iBlock *indexBlock) print() {
	log.Printf("Total Index count: [%d]\n", iBlock.count)
	//for i := uint32(0); i < iBlock.count; i++ {
	//iBlock.indices[i].print()
	//}
}

func (iBlock *indexBlock) set(
	key string,
	offset uint32,
	length uint32) (err error) {

	//log.Printf("Index size: %d\n", iBlock.count)
	if iBlock.count >= MAX_INDEX_RECORDS {
		return Error{ERR_INDEX_FULL, fmt.Sprintf("key [%s]", key)}
	}
	// TODO: Check for key existance and overwrite.
	// Add the new index record at the end.
	rec := &(iBlock.indices[iBlock.count])

	keyHash := getKeyHash(key)
	rec.key = keyHash
	rec.dataOffset = offset
	rec.dataLength = length
	rec.status = INDEX_REC_STATUS_ACTIVE

	iBlock.count++
	return nil
}

func (iBlock *indexBlock) get(
	key string) (offset uint32, length uint32, err error) {
	indexRec, err := iBlock.find(key)
	if err != nil {
		return
	}
	if indexRec.status != INDEX_REC_STATUS_ACTIVE {
		err = Error{
			ERR_INDEX_INACTIVE,
			fmt.Sprintf("key [%s], status[%d]", key, indexRec.status)}
		return
	}
	return indexRec.dataOffset, indexRec.dataLength, nil
}

func (iBlock *indexBlock) delete(key string) error {
	indexRecPtr, err := iBlock.find(key)
	if err != nil {
		return err
	}
	if indexRecPtr.status != INDEX_REC_STATUS_ACTIVE {
		indexRecPtr.status = INDEX_REC_STATUS_DELETED
		iBlock.count--
	}
	return nil
}

func (iBlock *indexBlock) find(key string) (*indexRecord, error) {
	inputKeyHash := getKeyHash(key)
	for i := uint32(0); i < iBlock.count; i++ {
		indexRecPtr := &(iBlock.indices[i])
		if indexRecPtr.key == inputKeyHash {
			return indexRecPtr, nil
		}
	}
	return nil, Error{ERR_INDEX_KEY_NOT_FOUND, fmt.Sprintf("key [%s]", key)}
}

func getKeyHash(key string) (ret [16]byte) {
	// md5 the key
	h := md5.New()
	h.Write([]byte(key))
	hash := h.Sum(nil)

	// TODO: Fix this copying. Make sure it copies only 16 bytes.
	copy(ret[:], hash)
	return ret
}
<|endoftext|>


<commit_before>package statsd

// In package metrics so we can stub tick.

import (
	"bytes"
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCounter(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	c := NewCounter(buf, "test_statsd_counter", time.Second)

	c.Add(1)
	c.Add(2)
	ch <- time.Now()

	for i := 0; i < 10 && buf.Len() == 0; i++ {
		time.Sleep(time.Millisecond)
	}

	if want, have := "test_statsd_counter:1|c\ntest_statsd_counter:2|c\n", buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestGauge(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	g := NewGauge(buf, "test_statsd_gauge", time.Second)

	delta := 1.0
	g.Add(delta)      // send command
	runtime.Gosched() // yield to buffer write
	ch <- time.Now()  // signal flush
	runtime.Gosched() // yield to flush
	if want, have := fmt.Sprintf("test_statsd_gauge:+%f|g\n", delta), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}

	buf.Reset()

	delta = -2.0
	g.Add(delta)
	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := fmt.Sprintf("test_statsd_gauge:%f|g\n", delta), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}

	buf.Reset()

	value := 3.0
	g.Set(value)
	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := fmt.Sprintf("test_statsd_gauge:%f|g\n", value), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestCallbackGauge(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	value := 55.55
	cb := func() float64 { return value }
	NewCallbackGauge(buf, "test_statsd_callback_gauge", time.Second, time.Nanosecond, cb)

	ch <- time.Now()  // signal emitter
	runtime.Gosched() // yield to emitter
	ch <- time.Now()  // signal flush
	runtime.Gosched() // yield to flush

	if want, have := fmt.Sprintf("test_statsd_callback_gauge:%f|g\n", value), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestHistogram(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	h := NewHistogram(buf, "test_statsd_histogram", time.Second)

	h.Observe(123)

	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := "test_statsd_histogram:123|ms\n", buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
<commit_msg>Fix flaky Travis test?<commit_after>package statsd

// In package metrics so we can stub tick.

import (
	"bytes"
	"fmt"
	"runtime"
	"testing"
	"time"
)

func TestCounter(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	c := NewCounter(buf, "test_statsd_counter", time.Second)

	c.Add(1)
	c.Add(2)
	ch <- time.Now()

	for i := 0; i < 10 && buf.Len() == 0; i++ {
		time.Sleep(time.Millisecond)
	}

	if want, have := "test_statsd_counter:1|c\ntest_statsd_counter:2|c\n", buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestGauge(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	g := NewGauge(buf, "test_statsd_gauge", time.Second)

	delta := 1.0
	g.Add(delta)      // send command
	runtime.Gosched() // yield to buffer write
	ch <- time.Now()  // signal flush
	runtime.Gosched() // yield to flush
	if want, have := fmt.Sprintf("test_statsd_gauge:+%f|g\n", delta), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}

	buf.Reset()

	delta = -2.0
	g.Add(delta)
	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := fmt.Sprintf("test_statsd_gauge:%f|g\n", delta), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}

	buf.Reset()

	value := 3.0
	g.Set(value)
	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := fmt.Sprintf("test_statsd_gauge:%f|g\n", value), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestCallbackGauge(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	value := 55.55
	cb := func() float64 { return value }
	NewCallbackGauge(buf, "test_statsd_callback_gauge", time.Second, time.Nanosecond, cb)

	ch <- time.Now()  // signal emitter
	runtime.Gosched() // yield to emitter
	ch <- time.Now()  // signal flush
	runtime.Gosched() // yield to flush

	// Travis is annoying
	deadline := time.Now().Add(100 * time.Millisecond)
	for buf.String() == "" {
		if time.Now().After(deadline) {
			t.Fatal("buffer never got write + flush")
		}
		time.Sleep(time.Millisecond)
	}

	if want, have := fmt.Sprintf("test_statsd_callback_gauge:%f|g\n", value), buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}

func TestHistogram(t *testing.T) {
	ch := make(chan time.Time)
	tick = func(time.Duration) <-chan time.Time { return ch }
	defer func() { tick = time.Tick }()

	buf := &bytes.Buffer{}
	h := NewHistogram(buf, "test_statsd_histogram", time.Second)

	h.Observe(123)

	runtime.Gosched()
	ch <- time.Now()
	runtime.Gosched()
	if want, have := "test_statsd_histogram:123|ms\n", buf.String(); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
<|endoftext|>


<commit_before>package spotcontrol

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/hashicorp/mdns"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
)

type connectInfo struct {
	DeviceID  string `json:"deviceID"`
	PublicKey string `json:"publicKey"`
}

type connectDeviceMdns struct {
	path string
	name string
}

type getInfo struct {
	Status           int    `json:"status"`
	StatusError      string `json:"statusError"`
	SpotifyError     int    `json:"spotifyError"`
	Version          string `json:"version"`
	DeviceID         string `json:"deviceID"`
	RemoteName       string `json:"remoteName"`
	ActiveUser       string `json:"activeUser"`
	PublicKey        string `json:"publicKey"`
	DeviceType       string `json:"deviceType"`
	LibraryVersion   string `json:"libraryVersion"`
	AccountReq       string `json:"accountReq"`
	BrandDisplayName string `json:"brandDisplayName"`
	ModelDisplayName string `json:"modelDisplayName"`
}

type discovery struct {
	keys        privateKeys
	cachePath   string
	loginBlob   blobInfo
	deviceId    string
	deviceName  string

	mdnsServer  *mdns.Server
	httpServer  *http.Server
	devices     []connectDeviceMdns
	devicesLock sync.RWMutex
}

func makeGetInfo(deviceId, deviceName, publicKey string) getInfo {
	return getInfo{
		Status:           101,
		StatusError:      "ERROR-OK",
		SpotifyError:     0,
		Version:          "1.3.0",
		DeviceID:         deviceId,
		RemoteName:       deviceName,
		ActiveUser:       "",
		PublicKey:        publicKey,
		DeviceType:       "UNKNOWN",
		LibraryVersion:   "0.1.0",
		AccountReq:       "PREMIUM",
		BrandDisplayName: "librespot",
		ModelDisplayName: "librespot",
	}
}

func loginFromConnect(cachePath, deviceId string, deviceName string) discovery {
	d := discovery{
		keys:        generateKeys(),
		cachePath:   cachePath,
		deviceId:    deviceId,
		deviceName:  deviceName,
	}

	done := make(chan int)

	go d.startHttp(done)
	d.startDiscoverable()

	<-done

	return d
}

func loginFromFile(cachePath, deviceId string, deviceName string) discovery {
	blob, err := blobFromFile(cachePath)
	if err != nil {
		log.Fatal("failed to get blob from file")
	}

	d := discovery{
		keys:        generateKeys(),
		cachePath:   cachePath,
		deviceId:    deviceId,
		deviceName:  deviceName,
		loginBlob:   blob,
	}

	d.FindDevices()

	return d
}

func makeAddUserRequest(userName string, blob string, key string, deviceId string, deviceName string) url.Values {
	v := url.Values{}
	v.Set("action", "addUser")
	v.Add("userName", userName)
	v.Add("blob", blob)
	v.Add("clientKey", key)
	v.Add("deviceId", deviceId)
	v.Add("deviceName", deviceName)
	return v
}

func findCpath(info []string) string {
	for _, i := range info {
		if strings.Contains(i, "CPath") {
			return strings.Split(i, "=")[1]
		}
	}
	return ""
}

func (d *discovery) FindDevices() {
	ch := make(chan *mdns.ServiceEntry, 10)

	d.devices = make([]connectDeviceMdns, 0)
	go func() {
		for entry := range ch {
			cPath := findCpath(entry.InfoFields)
			url := fmt.Sprintf("http://%v:%v%v", entry.AddrV4, entry.Port, cPath)
			fmt.Println("Found a device", entry)
			d.devicesLock.Lock()
			d.devices = append(d.devices, connectDeviceMdns{
				path: url,
				name: strings.Replace(entry.Name, "._spotify-connect._tcp.local.", "", 1),
			})
			fmt.Println("devices", d.devices)
			d.devicesLock.Unlock()
		}
		fmt.Println("closed")
	}()

	err := mdns.Lookup("_spotify-connect._tcp.", ch)
	if err != nil {
		log.Fatal("lookup error", err)
	}
}

func (d *discovery) ConnectToDevice(address string) {
	resp, err := http.Get(address + "?action=getInfo")
	resp, err = http.Get(address + "?action=resetUsers")
	resp, err = http.Get(address + "?action=getInfo")

	fmt.Println("start get")
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	info := connectInfo{}
	err = decoder.Decode(&info)
	if err != nil {
		panic("bad json")
	}
	fmt.Println("resposne", resp)

	client64 := base64.StdEncoding.EncodeToString(d.keys.pubKey())
	blob, err := d.loginBlob.makeAuthBlob(info.DeviceID,
		info.PublicKey, d.keys)
	if err != nil {
		panic("bad blob")
	}

	body := makeAddUserRequest(d.loginBlob.Username, blob, client64, d.deviceId, d.deviceName)
	resp, err = http.PostForm(address, body)
	defer resp.Body.Close()
	decoder = json.NewDecoder(resp.Body)
	var f interface{}
	err = decoder.Decode(&f)

	fmt.Println("got", f, resp, err)
}

func (d *discovery) saveLoginInfo() error {
	file, err := os.Create(d.cachePath)
	if err != nil {
		return err
	}

	defer file.Close()

	encoder := json.NewEncoder(file)
	err = encoder.Encode(d.loginBlob)
	if err != nil {
		return err
	}
	return nil
}

func (d *discovery) handleAddUser(r *http.Request) error {
	//already have login info, ignore
	if d.loginBlob.Username != "" {
		return nil
	}

	username := r.FormValue("userName")
	client64 := r.FormValue("clientKey")
	blob64 := r.FormValue("blob")

	if username == "" || client64 == "" || blob64 == "" {
		log.Println("Bad request, addUser")
		return errors.New("Bad username request")
	}

	blob, err := newBlobInfo(blob64, client64, d.keys,
		d.deviceId, username)
	if err != nil {
		return errors.New("failed to decode blob")
	}

	err = blob.saveToFile(d.cachePath)
	if err != nil {
		log.Println("failed to cache login info")
	}

	d.loginBlob = blob
	d.mdnsServer.Shutdown()
	return nil
}

func (d *discovery) startHttp(done chan int) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		action := r.FormValue("action")
		fmt.Println("got request: ", action)
		switch {
		case "getInfo" == action || "resetUsers" == action:
			client64 := base64.StdEncoding.EncodeToString(d.keys.pubKey())
			info := makeGetInfo(d.deviceId, d.deviceName, client64)

			js, err := json.Marshal(info)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		case "addUser" == action:
			err := d.handleAddUser(r)
			if err == nil {
				done <- 1
			}
		}
	})

	d.httpServer = &http.Server{Addr: ":8080"}
	log.Fatal(d.httpServer.ListenAndServe())
}

func (d *discovery) startDiscoverable() {
	fmt.Println("start discoverable")
	info := []string{"VERSION=1.0", "CPath=/"}
	service, err := mdns.NewMDNSService("spotcontrol189",
		"_spotify-connect._tcp", "", "", 8080, nil, info)
	server, err := mdns.NewServer(&mdns.Config{
		Zone: service,
	})
	if err != nil {
		log.Fatal("error starting discovery")
	}
	d.mdnsServer = server
}
<commit_msg>use mdns fork to use offical go net package<commit_after>package spotcontrol

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/badfortrains/mdns"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
)

type connectInfo struct {
	DeviceID  string `json:"deviceID"`
	PublicKey string `json:"publicKey"`
}

type connectDeviceMdns struct {
	path string
	name string
}

type getInfo struct {
	Status           int    `json:"status"`
	StatusError      string `json:"statusError"`
	SpotifyError     int    `json:"spotifyError"`
	Version          string `json:"version"`
	DeviceID         string `json:"deviceID"`
	RemoteName       string `json:"remoteName"`
	ActiveUser       string `json:"activeUser"`
	PublicKey        string `json:"publicKey"`
	DeviceType       string `json:"deviceType"`
	LibraryVersion   string `json:"libraryVersion"`
	AccountReq       string `json:"accountReq"`
	BrandDisplayName string `json:"brandDisplayName"`
	ModelDisplayName string `json:"modelDisplayName"`
}

type discovery struct {
	keys        privateKeys
	cachePath   string
	loginBlob   blobInfo
	deviceId    string
	deviceName  string

	mdnsServer  *mdns.Server
	httpServer  *http.Server
	devices     []connectDeviceMdns
	devicesLock sync.RWMutex
}

func makeGetInfo(deviceId, deviceName, publicKey string) getInfo {
	return getInfo{
		Status:           101,
		StatusError:      "ERROR-OK",
		SpotifyError:     0,
		Version:          "1.3.0",
		DeviceID:         deviceId,
		RemoteName:       deviceName,
		ActiveUser:       "",
		PublicKey:        publicKey,
		DeviceType:       "UNKNOWN",
		LibraryVersion:   "0.1.0",
		AccountReq:       "PREMIUM",
		BrandDisplayName: "librespot",
		ModelDisplayName: "librespot",
	}
}

func loginFromConnect(cachePath, deviceId string, deviceName string) discovery {
	d := discovery{
		keys:        generateKeys(),
		cachePath:   cachePath,
		deviceId:    deviceId,
		deviceName:  deviceName,
	}

	done := make(chan int)

	go d.startHttp(done)
	d.startDiscoverable()

	<-done

	return d
}

func loginFromFile(cachePath, deviceId string, deviceName string) discovery {
	blob, err := blobFromFile(cachePath)
	if err != nil {
		log.Fatal("failed to get blob from file")
	}

	d := discovery{
		keys:        generateKeys(),
		cachePath:   cachePath,
		deviceId:    deviceId,
		deviceName:  deviceName,
		loginBlob:   blob,
	}

	d.FindDevices()

	return d
}

func makeAddUserRequest(userName string, blob string, key string, deviceId string, deviceName string) url.Values {
	v := url.Values{}
	v.Set("action", "addUser")
	v.Add("userName", userName)
	v.Add("blob", blob)
	v.Add("clientKey", key)
	v.Add("deviceId", deviceId)
	v.Add("deviceName", deviceName)
	return v
}

func findCpath(info []string) string {
	for _, i := range info {
		if strings.Contains(i, "CPath") {
			return strings.Split(i, "=")[1]
		}
	}
	return ""
}

func (d *discovery) FindDevices() {
	ch := make(chan *mdns.ServiceEntry, 10)

	d.devices = make([]connectDeviceMdns, 0)
	go func() {
		for entry := range ch {
			cPath := findCpath(entry.InfoFields)
			url := fmt.Sprintf("http://%v:%v%v", entry.AddrV4, entry.Port, cPath)
			fmt.Println("Found a device", entry)
			d.devicesLock.Lock()
			d.devices = append(d.devices, connectDeviceMdns{
				path: url,
				name: strings.Replace(entry.Name, "._spotify-connect._tcp.local.", "", 1),
			})
			fmt.Println("devices", d.devices)
			d.devicesLock.Unlock()
		}
		fmt.Println("closed")
	}()

	err := mdns.Lookup("_spotify-connect._tcp.", ch)
	if err != nil {
		log.Fatal("lookup error", err)
	}
}

func (d *discovery) ConnectToDevice(address string) {
	resp, err := http.Get(address + "?action=getInfo")
	resp, err = http.Get(address + "?action=resetUsers")
	resp, err = http.Get(address + "?action=getInfo")

	fmt.Println("start get")
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	info := connectInfo{}
	err = decoder.Decode(&info)
	if err != nil {
		panic("bad json")
	}
	fmt.Println("resposne", resp)

	client64 := base64.StdEncoding.EncodeToString(d.keys.pubKey())
	blob, err := d.loginBlob.makeAuthBlob(info.DeviceID,
		info.PublicKey, d.keys)
	if err != nil {
		panic("bad blob")
	}

	body := makeAddUserRequest(d.loginBlob.Username, blob, client64, d.deviceId, d.deviceName)
	resp, err = http.PostForm(address, body)
	defer resp.Body.Close()
	decoder = json.NewDecoder(resp.Body)
	var f interface{}
	err = decoder.Decode(&f)

	fmt.Println("got", f, resp, err)
}

func (d *discovery) handleAddUser(r *http.Request) error {
	//already have login info, ignore
	if d.loginBlob.Username != "" {
		return nil
	}

	username := r.FormValue("userName")
	client64 := r.FormValue("clientKey")
	blob64 := r.FormValue("blob")

	if username == "" || client64 == "" || blob64 == "" {
		log.Println("Bad request, addUser")
		return errors.New("Bad username request")
	}

	blob, err := newBlobInfo(blob64, client64, d.keys,
		d.deviceId, username)
	if err != nil {
		return errors.New("failed to decode blob")
	}

	err = blob.saveToFile(d.cachePath)
	if err != nil {
		log.Println("failed to cache login info")
	}

	d.loginBlob = blob
	d.mdnsServer.Shutdown()
	return nil
}

func (d *discovery) startHttp(done chan int) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		action := r.FormValue("action")
		fmt.Println("got request: ", action)
		switch {
		case "getInfo" == action || "resetUsers" == action:
			client64 := base64.StdEncoding.EncodeToString(d.keys.pubKey())
			info := makeGetInfo(d.deviceId, d.deviceName, client64)

			js, err := json.Marshal(info)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		case "addUser" == action:
			err := d.handleAddUser(r)
			if err == nil {
				done <- 1
			}
		}
	})

	d.httpServer = &http.Server{Addr: ":8080"}
	log.Fatal(d.httpServer.ListenAndServe())
}

func (d *discovery) startDiscoverable() {
	fmt.Println("start discoverable")
	info := []string{"VERSION=1.0", "CPath=/"}
	service, err := mdns.NewMDNSService("spotcontrol189",
		"_spotify-connect._tcp", "", "", 8080, nil, info)
	server, err := mdns.NewServer(&mdns.Config{
		Zone: service,
	})
	if err != nil {
		log.Fatal("error starting discovery")
	}
	d.mdnsServer = server
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/foursquare/curator.go"
	"github.com/foursquare/fsgo/net/discovery"
	"github.com/foursquare/quiver/hfile"
)

type Registrations struct {
	existing []*discovery.ServiceDiscovery

	zk curator.CuratorFramework

	sync.Mutex
}

func (r *Registrations) Connect() {
	if Settings.zk == "" {
		log.Fatal("Specified discovery path but not zk?", Settings.zk)
	}

	retryPolicy := curator.NewExponentialBackoffRetry(time.Second, 3, 15*time.Second)

	r.zk = curator.NewClient(Settings.zk, retryPolicy)

	if err := r.zk.Start(); err != nil {
		log.Fatal(err)
	}
}

func (r *Registrations) Join(hostname, base string, configs []*hfile.CollectionConfig, wait time.Duration) {
	if hostname == "localhost" {
		log.Fatal("invalid hostname for service discovery registration:", hostname)
	}

	log.Println("Waiting to join service discovery", wait)
	time.Sleep(wait)
	log.Println("Joining service discovery...")

	r.Lock()
	defer r.Unlock()

	for _, i := range configs {
		sfunc := i.ShardFunction
		capacity := i.TotalPartitions

		if len(sfunc) < 1 {
			capacity = "1"
			sfunc = "_"
		}

		base := fmt.Sprintf("%s/%s/%s", i.ParentName, sfunc, capacity)

		disco := discovery.NewServiceDiscovery(r.zk, curator.JoinPath(Settings.discoveryPath, base))
		if err := disco.MaintainRegistrations(); err != nil {
			log.Fatal(err)
		}
		s := discovery.NewSimpleServiceInstance(i.Partition, hostname, Settings.port)
		disco.Register(s)
		r.existing = append(r.existing, disco)
	}
}

func (r *Registrations) Leave() {
	r.Lock()
	defer r.Unlock()
	for _, reg := range r.existing {
		reg.UnregisterAll()
	}
}

func (r *Registrations) Close() {
	r.Leave()
	r.zk.Close()
}
<commit_msg>fix zk connection crash?<commit_after>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/foursquare/curator.go"
	"github.com/foursquare/fsgo/net/discovery"
	"github.com/foursquare/quiver/hfile"
)

type Registrations struct {
	existing []*discovery.ServiceDiscovery

	zk curator.CuratorFramework

	sync.Mutex
}

func (r *Registrations) Connect() {
	if Settings.zk == "" {
		log.Fatal("Specified discovery path but not zk?", Settings.zk)
	}

	retryPolicy := curator.NewExponentialBackoffRetry(time.Second, 3, 15*time.Second)

	r.zk = curator.NewClientTimeout(Settings.zk, 15*time.Second, 15*time.Second, retryPolicy)

	if err := r.zk.Start(); err != nil {
		log.Fatal(err)
	} else if err := r.zk.ZookeeperClient().BlockUntilConnectedOrTimedOut(); err != nil {
		log.Fatal(err)
	} else {
		log.Println("Connected to zookeeper: ", r.zk.ZookeeperClient().Connected())
	}
}

func (r *Registrations) Join(hostname, base string, configs []*hfile.CollectionConfig, wait time.Duration) {
	if hostname == "localhost" {
		log.Fatal("invalid hostname for service discovery registration:", hostname)
	}

	log.Println("Waiting to join service discovery", wait)
	time.Sleep(wait)
	log.Println("Joining service discovery...")

	r.Lock()
	defer r.Unlock()

	for _, i := range configs {
		sfunc := i.ShardFunction
		capacity := i.TotalPartitions

		if len(sfunc) < 1 {
			capacity = "1"
			sfunc = "_"
		}

		base := fmt.Sprintf("%s/%s/%s", i.ParentName, sfunc, capacity)

		disco := discovery.NewServiceDiscovery(r.zk, curator.JoinPath(Settings.discoveryPath, base))
		if err := disco.MaintainRegistrations(); err != nil {
			log.Fatal(err)
		}
		s := discovery.NewSimpleServiceInstance(i.Partition, hostname, Settings.port)
		disco.Register(s)
		r.existing = append(r.existing, disco)
	}
}

func (r *Registrations) Leave() {
	r.Lock()
	defer r.Unlock()
	for _, reg := range r.existing {
		reg.UnregisterAll()
	}
}

func (r *Registrations) Close() {
	r.Leave()
	r.zk.Close()
}
<|endoftext|>


<commit_before>package optimize

import (
	"math"

	"github.com/gonum/floats"
)

// LBFGS implements the limited-memory BFGS algorithm. While the normal BFGS algorithm
// makes a full approximation to the inverse hessian, LBFGS instead approximates the
// hessian from the last Store optimization steps. The Store parameter is a tradeoff
// between cost of the method and accuracy of the hessian approximation.
// LBFGS has a cost (both in memory and time) of O(Store * inputDimension).
// Since BFGS has a cost of O(inputDimension^2), LBFGS is more appropriate
// for very large problems. This "forgetful" nature of LBFGS may also make it perform
// better than BFGS for functions with Hessians that vary rapidly spatially.
//
// If Store is 0, Store is defaulted to 15.
// A LinesearchMethod for LBFGS must satisfy the strong Wolfe conditions at every
// iteration. If LinesearchMethod == nil, an appropriate default is chosen.
type LBFGS struct {
	LinesearchMethod LinesearchMethod
	Store            int // how many past iterations to store

	linesearch *Linesearch

	dim    int
	oldest int // element of the history slices that is the oldest

	x    []float64 // location at the last major iteration
	grad []float64 // gradient at the last major iteration

	y []float64 // holds g_{k+1} - g_k
	s []float64 // holds x_{k+1} - x_k
	a []float64 // holds cache of hessian updates

	// History
	yHist   [][]float64 // last Store iterations of y
	sHist   [][]float64 // last Store iterations of s
	rhoHist []float64   // last Store iterations of rho
}

func (l *LBFGS) Init(loc Location, f *FunctionInfo, xNext []float64) (EvaluationType, IterationType, error) {
	if l.LinesearchMethod == nil {
		l.LinesearchMethod = &Bisection{}
	}
	if l.linesearch == nil {
		l.linesearch = &Linesearch{}
	}
	l.linesearch.Method = l.LinesearchMethod
	l.linesearch.NextDirectioner = l
	return l.linesearch.Init(loc, f, xNext)
}

func (l *LBFGS) Iterate(loc Location, xNext []float64) (EvaluationType, IterationType, error) {
	return l.linesearch.Iterate(loc, xNext)
}

func (l *LBFGS) InitDirection(loc Location, direction []float64) (stepSize float64) {
	dim := len(loc.X)
	l.dim = dim

	if l.Store == 0 {
		l.Store = 15
	}

	l.oldest = l.Store - 1 // the first vector will be put in at 0

	l.x = resize(l.x, dim)
	l.grad = resize(l.grad, dim)
	copy(l.x, loc.X)
	copy(l.grad, loc.Gradient)

	l.y = resize(l.y, dim)
	l.s = resize(l.s, dim)
	l.a = resize(l.a, l.Store)
	l.rhoHist = resize(l.rhoHist, l.Store)

	if cap(l.yHist) < l.Store {
		n := make([][]float64, l.Store-cap(l.yHist))
		l.yHist = append(l.yHist, n...)
	}
	if cap(l.sHist) < l.Store {
		n := make([][]float64, l.Store-cap(l.sHist))
		l.sHist = append(l.sHist, n...)
	}
	l.yHist = l.yHist[:l.Store]
	l.sHist = l.sHist[:l.Store]
	for i := range l.sHist {
		l.sHist[i] = resize(l.sHist[i], dim)
		for j := range l.sHist[i] {
			l.sHist[i][j] = 0
		}
	}
	for i := range l.yHist {
		l.yHist[i] = resize(l.yHist[i], dim)
		for j := range l.yHist[i] {
			l.yHist[i][j] = 0
		}
	}

	copy(direction, loc.Gradient)
	floats.Scale(-1, direction)

	// see BFGS for comment
	floats.Scale(1/math.Sqrt(floats.Norm(direction, 2)), direction)
	return 1
}

func (l *LBFGS) NextDirection(loc Location, direction []float64) (stepSize float64) {
	if len(loc.X) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}
	if len(loc.Gradient) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}
	if len(direction) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}

	// Update direction. Uses two-loop correction as described in
	// Nocedal, Wright (2006), Numerical Optimization (2nd ed.). Chapter 7, page 178.
	floats.SubTo(l.y, loc.Gradient, l.grad)
	floats.SubTo(l.s, loc.X, l.x)
	rho := 1 / floats.Dot(l.y, l.s)
	copy(direction, loc.Gradient)

	// Compute gamma for the initial Hessian.
	gamma := floats.Dot(l.s, l.y) / floats.Dot(l.y, l.y)

	// two loop update. First loop starts with the most recent element
	// and goes backward, second starts with the oldest element and goes
	// forward. At the end have computed H^-1 * g, so flip the direction for
	// minimization.
	for i := 0; i < l.Store; i++ {
		idx := l.oldest - i - 1
		if idx < 0 {
			idx += l.Store
		}
		l.a[idx] = l.rhoHist[idx] * floats.Dot(l.sHist[idx], direction)
		floats.AddScaled(direction, -l.a[idx], l.yHist[idx])
	}
	floats.Scale(gamma, direction)
	for i := 0; i < l.Store; i++ {
		idx := i + l.oldest
		if idx >= l.Store {
			idx -= l.Store
		}
		beta := l.rhoHist[idx] * floats.Dot(l.yHist[idx], direction)
		floats.AddScaled(direction, l.a[idx]-beta, l.sHist[idx])
	}
	floats.Scale(-1, direction)

	// store the information from this iteration to prepare for the next one
	copy(l.x, loc.X)
	copy(l.grad, loc.Gradient)
	l.oldest++
	l.oldest = l.oldest % l.Store
	copy(l.sHist[l.oldest], l.s)
	copy(l.yHist[l.oldest], l.y)
	l.rhoHist[l.oldest] = rho
	return 1
}
<commit_msg>Moved computations to where they are used<commit_after>package optimize

import (
	"math"

	"github.com/gonum/floats"
)

// LBFGS implements the limited-memory BFGS algorithm. While the normal BFGS algorithm
// makes a full approximation to the inverse hessian, LBFGS instead approximates the
// hessian from the last Store optimization steps. The Store parameter is a tradeoff
// between cost of the method and accuracy of the hessian approximation.
// LBFGS has a cost (both in memory and time) of O(Store * inputDimension).
// Since BFGS has a cost of O(inputDimension^2), LBFGS is more appropriate
// for very large problems. This "forgetful" nature of LBFGS may also make it perform
// better than BFGS for functions with Hessians that vary rapidly spatially.
//
// If Store is 0, Store is defaulted to 15.
// A LinesearchMethod for LBFGS must satisfy the strong Wolfe conditions at every
// iteration. If LinesearchMethod == nil, an appropriate default is chosen.
type LBFGS struct {
	LinesearchMethod LinesearchMethod
	Store            int // how many past iterations to store

	linesearch *Linesearch

	dim    int
	oldest int // element of the history slices that is the oldest

	x    []float64 // location at the last major iteration
	grad []float64 // gradient at the last major iteration

	y []float64 // holds g_{k+1} - g_k
	s []float64 // holds x_{k+1} - x_k
	a []float64 // holds cache of hessian updates

	// History
	yHist   [][]float64 // last Store iterations of y
	sHist   [][]float64 // last Store iterations of s
	rhoHist []float64   // last Store iterations of rho
}

func (l *LBFGS) Init(loc Location, f *FunctionInfo, xNext []float64) (EvaluationType, IterationType, error) {
	if l.LinesearchMethod == nil {
		l.LinesearchMethod = &Bisection{}
	}
	if l.linesearch == nil {
		l.linesearch = &Linesearch{}
	}
	l.linesearch.Method = l.LinesearchMethod
	l.linesearch.NextDirectioner = l
	return l.linesearch.Init(loc, f, xNext)
}

func (l *LBFGS) Iterate(loc Location, xNext []float64) (EvaluationType, IterationType, error) {
	return l.linesearch.Iterate(loc, xNext)
}

func (l *LBFGS) InitDirection(loc Location, direction []float64) (stepSize float64) {
	dim := len(loc.X)
	l.dim = dim

	if l.Store == 0 {
		l.Store = 15
	}

	l.oldest = l.Store - 1 // the first vector will be put in at 0

	l.x = resize(l.x, dim)
	l.grad = resize(l.grad, dim)
	copy(l.x, loc.X)
	copy(l.grad, loc.Gradient)

	l.y = resize(l.y, dim)
	l.s = resize(l.s, dim)
	l.a = resize(l.a, l.Store)
	l.rhoHist = resize(l.rhoHist, l.Store)

	if cap(l.yHist) < l.Store {
		n := make([][]float64, l.Store-cap(l.yHist))
		l.yHist = append(l.yHist, n...)
	}
	if cap(l.sHist) < l.Store {
		n := make([][]float64, l.Store-cap(l.sHist))
		l.sHist = append(l.sHist, n...)
	}
	l.yHist = l.yHist[:l.Store]
	l.sHist = l.sHist[:l.Store]
	for i := range l.sHist {
		l.sHist[i] = resize(l.sHist[i], dim)
		for j := range l.sHist[i] {
			l.sHist[i][j] = 0
		}
	}
	for i := range l.yHist {
		l.yHist[i] = resize(l.yHist[i], dim)
		for j := range l.yHist[i] {
			l.yHist[i][j] = 0
		}
	}

	copy(direction, loc.Gradient)
	floats.Scale(-1, direction)

	// see BFGS for comment
	floats.Scale(1/math.Sqrt(floats.Norm(direction, 2)), direction)
	return 1
}

func (l *LBFGS) NextDirection(loc Location, direction []float64) (stepSize float64) {
	if len(loc.X) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}
	if len(loc.Gradient) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}
	if len(direction) != l.dim {
		panic("lbfgs: unexpected size mismatch")
	}

	// Update direction. Uses two-loop correction as described in
	// Nocedal, Wright (2006), Numerical Optimization (2nd ed.). Chapter 7, page 178.
	copy(direction, loc.Gradient)

	// two loop update. First loop starts with the most recent element
	// and goes backward, second starts with the oldest element and goes
	// forward. At the end have computed H^-1 * g, so flip the direction for
	// minimization.
	for i := 0; i < l.Store; i++ {
		idx := l.oldest - i - 1
		if idx < 0 {
			idx += l.Store
		}
		l.a[idx] = l.rhoHist[idx] * floats.Dot(l.sHist[idx], direction)
		floats.AddScaled(direction, -l.a[idx], l.yHist[idx])
	}

	// Scale the initial Hessian.
	floats.SubTo(l.y, loc.Gradient, l.grad)
	floats.SubTo(l.s, loc.X, l.x)
	gamma := floats.Dot(l.s, l.y) / floats.Dot(l.y, l.y)
	floats.Scale(gamma, direction)

	for i := 0; i < l.Store; i++ {
		idx := i + l.oldest
		if idx >= l.Store {
			idx -= l.Store
		}
		beta := l.rhoHist[idx] * floats.Dot(l.yHist[idx], direction)
		floats.AddScaled(direction, l.a[idx]-beta, l.sHist[idx])
	}
	floats.Scale(-1, direction)

	// store the information from this iteration to prepare for the next one
	copy(l.x, loc.X)
	copy(l.grad, loc.Gradient)
	l.oldest++
	l.oldest = l.oldest % l.Store
	copy(l.sHist[l.oldest], l.s)
	copy(l.yHist[l.oldest], l.y)
	l.rhoHist[l.oldest] = 1 / floats.Dot(l.y, l.s)
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"fmt"
	"io"
	"text/scanner"
	"unicode"
)

const eof int = -1

type Lexer struct {
	name      string
	scanner   scanner.Scanner
	lookahead *list.List
}

var keywords = map[string]bool{
	"auto":   true,
	"break":  true,
	"case":   true,
	"else":   true,
	"extrn":  true,
	"goto":   true,
	"if":     true,
	"switch": true,
	"while":  true,
}

type LexError struct {
	pos scanner.Position
	msg string
}

func (l *LexError) Error() string {
	return fmt.Sprintf("Lex error on line: %d, character: %d: %s",
		l.pos.Line, l.pos.Column, l.msg)

}

func NewLexError(pos scanner.Position, msg string) error {
	return &LexError{pos, msg}
}

func NewLexer(name string, input io.Reader) *Lexer {
	lex := &Lexer{
		name:      name,
		lookahead: list.New(),
	}

	lex.scanner.Init(input)
	lex.scanner.Mode = scanner.ScanIdents | scanner.ScanInts |
		scanner.ScanStrings

	return lex
}

func (lex *Lexer) PeekToken() (Token, error) {
	tok, err := lex.lexToken()

	if err != nil {
		return tok.Error(), err
	}

	lex.lookahead.PushBack(tok)
	return tok, nil
}

func (lex *Lexer) NextToken() (Token, error) {
	if lex.lookahead.Front() != nil {
		node := lex.lookahead.Front()
		tok := node.Value.(Token)

		lex.lookahead.Remove(node)

		return tok, nil
	}

	return lex.lexToken()
}

func (lex *Lexer) lexToken() (tok Token, err error) {
	tok = Token{
		start: lex.scanner.Pos(),
	}

	// Remove error handler
	defer func() { lex.scanner.Error = nil }()

	// TODO: this is probably horrible style
	defer func() { recover() }()

	errorHandle := func(s *scanner.Scanner, msg string) {
		tok = tok.Error()
		err = NewLexError(lex.scanner.Pos(), msg)

		// Panic to get ourselves out of the parent func, this is
		// probably terrible form
		panic("ScanErrorHandle")
	}

	lex.scanner.Error = errorHandle

	scan := lex.scanner.Scan()

	tok.value = lex.scanner.TokenText()

	switch scan {
	case scanner.EOF:
		tok.kind = tkEof

	case scanner.Ident:
		if keywords[tok.value] {
			tok.kind = tkKeyword
		} else {
			tok.kind = tkIdent
		}

	case scanner.Int:
		tok.kind = tkNumber
		// TODO: this isn't all inclusive
		if next := lex.scanner.Peek(); unicode.IsLetter(next) {
			lex.scanner.Scan() // run until end of token

			err = NewLexError(
				lex.scanner.Pos(),
				fmt.Sprintf("bad number: %s%s", tok.value,
					lex.scanner.TokenText()))

			return tok.Error(), err
		}

	case scanner.String:
		tok.kind = tkString
		// cut out leading/trailing "
		tok.value = tok.value[1 : len(tok.value)-1]

	case '{':
		tok.kind = tkOpenBrace

	case '}':
		tok.kind = tkCloseBrace

	case '[':
		tok.kind = tkOpenBracket

	case ']':
		tok.kind = tkCloseBracket

	case '(':
		tok.kind = tkOpenParen

	case ')':
		tok.kind = tkCloseParen

	case ';':
		tok.kind = tkSemicolon

	case ',':
		tok.kind = tkComma

	case '\'':
		tok.kind = tkCharacter
		tok.value = ""

	endstring:
		for {
			switch char := lex.scanner.Next(); char {
			case '\n', scanner.EOF:
				return tok.Error(), NewLexError(lex.scanner.Pos(),
					fmt.Sprintf("unterminated character: %s",
						tok.value))
			case '\'':
				break endstring
			default:
				tok.value = fmt.Sprintf("%s%c", tok.value,
					char)
			}
		}

		if len(tok.value) > 4 {
			return tok.Error(), NewLexError(lex.scanner.Pos(),
				fmt.Sprintf("oversized character literal: %s",
					tok.value))
		}

	case '/':
		if lex.scanner.Peek() == '*' {
			lex.scanner.Next() // eat '*'
		endcomment:
			for {
				switch char := lex.scanner.Next(); char {
				case scanner.EOF:
					return tok.Error(),
						NewLexError(lex.scanner.Pos(),
							"unterminated comment")
				case '*':
					if lex.scanner.Peek() == '/' {
						lex.scanner.Next()
						break endcomment
					}
				}
			}

			return lex.NextToken()
		} else {
			tok.kind = tkOperator
		}

	case '*':
		if lex.scanner.Peek() == '/' {
			lex.scanner.Next() // eat '/'
			return tok.Error(), NewLexError(lex.scanner.Pos(),
				"unexpected end of comment")
		} else {
			tok.kind = tkOperator
		}

		// XXX: some other operators still unhandled
	case '=', '>', '<', '!':
		tok.kind = tkOperator
		if lex.scanner.Peek() == '=' {
			tok.value = fmt.Sprintf("%s%c", tok.value,
				lex.scanner.Next())

		}

	case '+', '-':
		tok.kind = tkOperator

		// ++ or --
		if tok.value == fmt.Sprintf("%c", lex.scanner.Peek()) {
			lex.scanner.Next()
			tok.value += tok.value

		}

	case '%', '&':
		tok.kind = tkOperator

	default:
		return tok.Error(), NewLexError(lex.scanner.Pos(),
			fmt.Sprintf("unexpected character: %c", scan))
	}

	tok.end = lex.scanner.Pos()

	return tok, nil
}
<commit_msg>Remove a few useless fmt.Sprintfs<commit_after>package main

import (
	"container/list"
	"fmt"
	"io"
	"text/scanner"
	"unicode"
)

const eof int = -1

type Lexer struct {
	name      string
	scanner   scanner.Scanner
	lookahead *list.List
}

var keywords = map[string]bool{
	"auto":   true,
	"break":  true,
	"case":   true,
	"else":   true,
	"extrn":  true,
	"goto":   true,
	"if":     true,
	"switch": true,
	"while":  true,
}

type LexError struct {
	pos scanner.Position
	msg string
}

func (l *LexError) Error() string {
	return fmt.Sprintf("Lex error on line: %d, character: %d: %s",
		l.pos.Line, l.pos.Column, l.msg)

}

func NewLexError(pos scanner.Position, msg string) error {
	return &LexError{pos, msg}
}

func NewLexer(name string, input io.Reader) *Lexer {
	lex := &Lexer{
		name:      name,
		lookahead: list.New(),
	}

	lex.scanner.Init(input)
	lex.scanner.Mode = scanner.ScanIdents | scanner.ScanInts |
		scanner.ScanStrings

	return lex
}

func (lex *Lexer) PeekToken() (Token, error) {
	tok, err := lex.lexToken()

	if err != nil {
		return tok.Error(), err
	}

	lex.lookahead.PushBack(tok)
	return tok, nil
}

func (lex *Lexer) NextToken() (Token, error) {
	if lex.lookahead.Front() != nil {
		node := lex.lookahead.Front()
		tok := node.Value.(Token)

		lex.lookahead.Remove(node)

		return tok, nil
	}

	return lex.lexToken()
}

func (lex *Lexer) lexToken() (tok Token, err error) {
	tok = Token{
		start: lex.scanner.Pos(),
	}

	// Remove error handler
	defer func() { lex.scanner.Error = nil }()

	// TODO: this is probably horrible style
	defer func() { recover() }()

	errorHandle := func(s *scanner.Scanner, msg string) {
		tok = tok.Error()
		err = NewLexError(lex.scanner.Pos(), msg)

		// Panic to get ourselves out of the parent func, this is
		// probably terrible form
		panic("ScanErrorHandle")
	}

	lex.scanner.Error = errorHandle

	scan := lex.scanner.Scan()

	tok.value = lex.scanner.TokenText()

	switch scan {
	case scanner.EOF:
		tok.kind = tkEof

	case scanner.Ident:
		if keywords[tok.value] {
			tok.kind = tkKeyword
		} else {
			tok.kind = tkIdent
		}

	case scanner.Int:
		tok.kind = tkNumber
		// TODO: this isn't all inclusive
		if next := lex.scanner.Peek(); unicode.IsLetter(next) {
			lex.scanner.Scan() // run until end of token

			err = NewLexError(
				lex.scanner.Pos(),
				fmt.Sprintf("bad number: %s%s", tok.value,
					lex.scanner.TokenText()))

			return tok.Error(), err
		}

	case scanner.String:
		tok.kind = tkString
		// cut out leading/trailing "
		tok.value = tok.value[1 : len(tok.value)-1]

	case '{':
		tok.kind = tkOpenBrace

	case '}':
		tok.kind = tkCloseBrace

	case '[':
		tok.kind = tkOpenBracket

	case ']':
		tok.kind = tkCloseBracket

	case '(':
		tok.kind = tkOpenParen

	case ')':
		tok.kind = tkCloseParen

	case ';':
		tok.kind = tkSemicolon

	case ',':
		tok.kind = tkComma

	case '\'':
		tok.kind = tkCharacter
		tok.value = ""

	endstring:
		for {
			switch char := lex.scanner.Next(); char {
			case '\n', scanner.EOF:
				return tok.Error(), NewLexError(lex.scanner.Pos(),
					fmt.Sprintf("unterminated character: %s",
						tok.value))
			case '\'':
				break endstring
			default:
				tok.value = fmt.Sprintf("%s%c", tok.value,
					char)
			}
		}

		if len(tok.value) > 4 {
			return tok.Error(), NewLexError(lex.scanner.Pos(),
				fmt.Sprintf("oversized character literal: %s",
					tok.value))
		}

	case '/':
		if lex.scanner.Peek() == '*' {
			lex.scanner.Next() // eat '*'
		endcomment:
			for {
				switch char := lex.scanner.Next(); char {
				case scanner.EOF:
					return tok.Error(),
						NewLexError(lex.scanner.Pos(),
							"unterminated comment")
				case '*':
					if lex.scanner.Peek() == '/' {
						lex.scanner.Next()
						break endcomment
					}
				}
			}

			return lex.NextToken()
		} else {
			tok.kind = tkOperator
		}

	case '*':
		if lex.scanner.Peek() == '/' {
			lex.scanner.Next() // eat '/'
			return tok.Error(), NewLexError(lex.scanner.Pos(),
				"unexpected end of comment")
		} else {
			tok.kind = tkOperator
		}

		// XXX: some other operators still unhandled
	case '=', '>', '<', '!':
		tok.kind = tkOperator
		if lex.scanner.Peek() == '=' {
			tok.value += string(lex.scanner.Next())

		}

	case '+', '-':
		tok.kind = tkOperator

		// ++ or --
		if tok.value == string(lex.scanner.Peek()) {
			lex.scanner.Next()
			tok.value += tok.value

		}

	case '%', '&':
		tok.kind = tkOperator

	default:
		return tok.Error(), NewLexError(lex.scanner.Pos(),
			fmt.Sprintf("unexpected character: %c", scan))
	}

	tok.end = lex.scanner.Pos()

	return tok, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/abh/dns"
	"log"
	"os"
	"os/signal"
	"strings"
)

// Program version
var VERSION = "0.0.3"

var (
	flagdomain   = flag.String("domain", "example.com", "base domain for the dnsmapper")
	flagip       = flag.String("ip", "127.0.0.1", "set the IP address")
	flagdnsport  = flag.String("dnsport", "53", "Set the DNS port")
	flaghttpport = flag.String("httpport", "80", "Set the HTTP port")
	flaglog      = flag.Bool("log", false, "be more verbose")

	flagPrimaryNs = flag.String("ns", "ns.example.com", "nameserver names (comma separated)")
)

var baseLength int

var primaryNsList []string

func getUuidFromDomain(name string) string {
	lx := dns.SplitLabels(name)
	if len(lx) <= baseLength {
		return ""
	}
	ql := lx[0 : len(lx)-baseLength]
	return strings.ToLower(strings.Join(ql, "."))
}

func setup() {
	baseLength = dns.LenLabels(*flagdomain)

	primaryNsList = strings.Split(*flagPrimaryNs, ",")

	log.Println("Listening for requests to", *flagdomain)
}

func main() {
	flag.Parse()
	log.Printf("Starting dnsmapper %s\n", VERSION)

	setup()

	dns.HandleFunc(*flagdomain, setupServerFunc())

	redisConnect()

	go httpHandler()
	go listenAndServeDNS(*flagip + ":" + *flagdnsport)

	terminate := make(chan os.Signal)
	signal.Notify(terminate, os.Interrupt)

	<-terminate
	log.Printf("dnsmapper: signal received, stopping")

}
<commit_msg>Tweaks for changes in dns package<commit_after>package main

import (
	"flag"
	"github.com/abh/dns"
	"log"
	"os"
	"os/signal"
	"strings"
)

var VERSION = "0.0.4"

var (
	flagdomain   = flag.String("domain", "example.com", "base domain for the dnsmapper")
	flagip       = flag.String("ip", "127.0.0.1", "set the IP address")
	flagdnsport  = flag.String("dnsport", "53", "Set the DNS port")
	flaghttpport = flag.String("httpport", "80", "Set the HTTP port")
	flaglog      = flag.Bool("log", false, "be more verbose")

	flagPrimaryNs = flag.String("ns", "ns.example.com", "nameserver names (comma separated)")
)

var baseLength int

var primaryNsList []string

func getUuidFromDomain(name string) string {
	lx := dns.SplitDomainName(name)
	if len(lx) <= baseLength {
		return ""
	}
	ql := lx[0 : len(lx)-baseLength]
	return strings.ToLower(strings.Join(ql, "."))
}

func setup() {
	baseLength = dns.CountLabel(*flagdomain)

	primaryNsList = strings.Split(*flagPrimaryNs, ",")

	log.Println("Listening for requests to", *flagdomain)
}

func main() {
	flag.Parse()
	log.Printf("Starting dnsmapper %s\n", VERSION)

	setup()

	dns.HandleFunc(*flagdomain, setupServerFunc())

	redisConnect()

	go httpHandler()
	go listenAndServeDNS(*flagip + ":" + *flagdnsport)

	terminate := make(chan os.Signal)
	signal.Notify(terminate, os.Interrupt)

	<-terminate
	log.Printf("dnsmapper: signal received, stopping")

}
<|endoftext|>