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

package commands

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/digitalocean/doctl"
	"github.com/digitalocean/doctl/commands/displayers"
	"github.com/digitalocean/doctl/do"
	"github.com/digitalocean/godo"
	"github.com/spf13/cobra"
	k8sapiv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
)

type dockerConfig struct {
	Auths map[string]struct {
		Auth string `json:"auth,omitempty"`
	} `json:"auths"`
}

// Registry creates the registry command
func Registry() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "registry",
			Aliases: []string{"reg", "r"},
			Short:   "[EA] Display commands for working with container registries",
			Long:    "[EA] The subcommands of `doctl registry` create, manage, and allow access to your private container registry.",
		},
	}

	cmd.AddCommand(Repository())

	createRegDesc := "This command creates a new private container registry with the provided name."
	CmdBuilder(cmd, RunRegistryCreate, "create <registry-name>",
		"Create a private container registry", createRegDesc, Writer)

	getRegDesc := "This command retrieves details about a private container registry including its name and the endpoint used to access it."
	CmdBuilder(cmd, RunRegistryGet, "get", "Retrieve details about a container registry",
		getRegDesc, Writer, aliasOpt("g"), displayerType(&displayers.Registry{}))

	deleteRegDesc := "This command permanently deletes a private container registry and all of its contents."
	cmdRunRegistryDelete := CmdBuilder(cmd, RunRegistryDelete, "delete",
		"Delete a container registry", deleteRegDesc, Writer, aliasOpt("d", "del", "rm"))
	AddBoolFlag(cmdRunRegistryDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force registry delete")

	loginRegDesc := "This command logs in Docker so that pull and push commands to your private container registry will be authenticated."
	CmdBuilder(cmd, RunRegistryLogin, "login", "Log in Docker to a container registry",
		loginRegDesc, Writer)

	logoutRegDesc := "This command logs Docker out of the private container registry, revoking access to it."
	CmdBuilder(cmd, RunRegistryLogout, "logout", "Log out Docker from a container registry",
		logoutRegDesc, Writer)

	kubeManifestDesc := `This command outputs a YAML-formated Kubernetes secret manifest that can be used to grant a Kubernetes cluster pull access to your private container registry.

Redirect the command's output to a file to save the manifest for later use or pipe it directly to kubectl to create the secret in your cluster:

    doctl registry kubernetes-manifest | kubectl apply -f -
`
	cmdRunKubernetesManifest := CmdBuilder(cmd, RunKubernetesManifest, "kubernetes-manifest",
		"Generate a Kubernetes secret manifest for a registry",
		kubeManifestDesc, Writer, aliasOpt("k8s"))
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectName, "", "",
		"The secret name to create. Defaults to the registry name prefixed with \"registry-\"")
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectNamespace, "",
		"default", "The Kubernetes namespace to hold the secret")

	return cmd
}

// Repository creates the repository sub-command
func Repository() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "repository",
			Aliases: []string{"repo", "r"},
			Short:   "[EA] Display commands for working with repositories in a container registry",
			Long:    "[EA] The subcommands of `doctl registry repository` help you command actions related to a repository.",
		},
	}

	listRepositoriesDesc := `
	This command retrieves information about repositories in a registry, including:
	- The repository name
	- The latest tag of the repository
	- The compressed size for the latest tag
	- The manifest digest for the latest tag
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositories, "list",
		"List repositories for a container registry", listRepositoriesDesc,
		Writer, aliasOpt("ls"), displayerType(&displayers.Repository{}),
	)

	listRepositoryTagsDesc := `
	This command retrieves information about tags in a repository, including:
	- The tag name
	- The compressed size
	- The manifest digest
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositoryTags, "list-tags <repository>",
		"List tags for a repository in a container registry", listRepositoryTagsDesc,
		Writer, aliasOpt("lt"), displayerType(&displayers.RepositoryTag{}),
	)

	deleteTagDesc := "This command permanently deletes one or more repository tags."
	cmdRunRepositoryDeleteTag := CmdBuilder(
		cmd,
		RunRepositoryDeleteTag,
		"delete-tag <repository> <tag>...",
		"Delete one or more container repository tags",
		deleteTagDesc,
		Writer,
		aliasOpt("dt"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteTag, doctl.ArgForce, doctl.ArgShortForce, false, "Force tag deletion")

	deleteManifestDesc := "This command permanently deletes one or more repository manifests by digest."
	cmdRunRepositoryDeleteManifest := CmdBuilder(
		cmd,
		RunRepositoryDeleteManifest,
		"delete-manifest <repository> <manifest-digest>...",
		"Delete one or more container repository manifests by digest",
		deleteManifestDesc,
		Writer,
		aliasOpt("dm"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteManifest, doctl.ArgForce, doctl.ArgShortForce, false, "Force manifest deletion")

	return cmd
}

// Registry Run Commands

// RunRegistryCreate creates a registry
func RunRegistryCreate(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	name := c.Args[0]
	rs := c.Registry()

	rcr := &godo.RegistryCreateRequest{Name: name}
	r, err := rs.Create(rcr)
	if err != nil {
		return err
	}

	return displayRegistries(c, *r)
}

// RunRegistryGet returns the registry
func RunRegistryGet(c *CmdConfig) error {
	reg, err := c.Registry().Get()
	if err != nil {
		return err
	}

	return displayRegistries(c, *reg)
}

// RunRegistryDelete delete the registry
func RunRegistryDelete(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if !force && AskForConfirm("delete registry") != nil {
		return fmt.Errorf("operation aborted")
	}

	return c.Registry().Delete()
}

// store execCommand in a variable. Lets us override it while testing
var execCommand = exec.Command

// RunRegistryLogin logs in Docker to the registry
func RunRegistryLogin(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	fmt.Printf("Logging Docker in to %s\n", c.Registry().Endpoint())

	creds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: true,
	})
	if err != nil {
		return err
	}

	var dc dockerConfig
	err = json.Unmarshal(creds.DockerConfigJSON, &dc)
	if err != nil {
		return err
	}

	// read the login credentials from the docker config
	for host, conf := range dc.Auths {
		// decode and split into username + password
		creds, err := base64.StdEncoding.DecodeString(conf.Auth)
		if err != nil {
			return err
		}

		splitCreds := strings.Split(string(creds), ":")
		if len(splitCreds) != 2 {
			return fmt.Errorf("got invalid docker credentials")
		}
		user, pass := splitCreds[0], splitCreds[1]

		// log in via the docker cli
		args := []string{
			"login", host,
			"-u", user,
			"--password-stdin",
		}
		cmd := execCommand("docker", args...)
		cmd.Stdin = strings.NewReader(pass)
		cmd.Stdout = c.Out
		cmd.Stderr = c.Out

		err = cmd.Run()
		if err != nil {
			return err
		}
	}

	return nil
}

// RunKubernetesManifest prints a Kubernetes manifest that provides read/pull access to the registry
func RunKubernetesManifest(c *CmdConfig) error {
	secretName, err := c.Doit.GetString(c.NS, doctl.ArgObjectName)
	if err != nil {
		return err
	}
	secretNamespace, err := c.Doit.GetString(c.NS, doctl.ArgObjectNamespace)
	if err != nil {
		return err
	}

	// if no secret name supplied, use the registry name
	if secretName == "" {
		reg, err := c.Registry().Get()
		if err != nil {
			return err
		}
		secretName = "registry-" + reg.Name
	}

	// fetch docker config
	dockerCreds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: false,
	})
	if err != nil {
		return err
	}

	// create the manifest for the secret
	secret := &k8sapiv1.Secret{
		TypeMeta: k8smetav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: "v1",
		},
		ObjectMeta: k8smetav1.ObjectMeta{
			Name:      secretName,
			Namespace: secretNamespace,
		},
		Type: k8sapiv1.SecretTypeDockerConfigJson,
		Data: map[string][]byte{
			".dockerconfigjson": dockerCreds.DockerConfigJSON,
		},
	}

	serializer := k8sjson.NewSerializerWithOptions(
		k8sjson.DefaultMetaFactory, nil, nil,
		k8sjson.SerializerOptions{
			Yaml:   true,
			Pretty: true,
			Strict: true,
		},
	)

	return serializer.Encode(secret, c.Out)
}

// RunRegistryLogout logs Docker out of the registry
func RunRegistryLogout(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	cmd := execCommand("docker", "logout", c.Registry().Endpoint())
	cmd.Stdout = c.Out
	cmd.Stderr = c.Out

	return cmd.Run()
}

// Repository Run Commands

// RunListRepositories lists repositories for the registry
func RunListRepositories(c *CmdConfig) error {
	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repositories, err := c.Registry().ListRepositories(registry.Name)
	if err != nil {
		return err
	}

	return displayRepositories(c, repositories...)
}

// RunListRepositoryTags lists tags for the repository in a registry
func RunListRepositoryTags(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	tags, err := c.Registry().ListRepositoryTags(registry.Name, c.Args[0])
	if err != nil {
		return err
	}

	return displayRepositoryTags(c, tags...)
}

// RunRepositoryDeleteTag deletes one or more repository tags
func RunRepositoryDeleteTag(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	tags := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository tag(s)", len(tags))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, tag := range tags {
		if err := c.Registry().DeleteTag(registry.Name, repository, tag); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository tags: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

// RunRepositoryDeleteManifest deletes one or more repository manifests by digest
func RunRepositoryDeleteManifest(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	digests := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository manifest(s) by digest (including associated tags)", len(digests))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, digest := range digests {
		if err := c.Registry().DeleteManifest(registry.Name, repository, digest); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository manifests: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

func displayRegistries(c *CmdConfig, registries ...do.Registry) error {
	item := &displayers.Registry{
		Registries: registries,
	}
	return c.Display(item)
}

func displayRepositories(c *CmdConfig, repositories ...do.Repository) error {
	item := &displayers.Repository{
		Repositories: repositories,
	}
	return c.Display(item)
}

func displayRepositoryTags(c *CmdConfig, tags ...do.RepositoryTag) error {
	item := &displayers.RepositoryTag{
		Tags: tags,
	}
	return c.Display(item)
}
<commit_msg>Revert "Update Container Registry to EA status"<commit_after>/*
Copyright 2018 The Doctl Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package commands

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/digitalocean/doctl"
	"github.com/digitalocean/doctl/commands/displayers"
	"github.com/digitalocean/doctl/do"
	"github.com/digitalocean/godo"
	"github.com/spf13/cobra"
	k8sapiv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
)

type dockerConfig struct {
	Auths map[string]struct {
		Auth string `json:"auth,omitempty"`
	} `json:"auths"`
}

// Registry creates the registry command
func Registry() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "registry",
			Aliases: []string{"reg", "r"},
			Short:   "[Beta] Display commands for working with container registries",
			Long:    "[Beta] The subcommands of `doctl registry` create, manage, and allow access to your private container registry.",
			Hidden:  true,
		},
	}

	cmd.AddCommand(Repository())

	createRegDesc := "This command creates a new private container registry with the provided name."
	CmdBuilder(cmd, RunRegistryCreate, "create <registry-name>",
		"Create a private container registry", createRegDesc, Writer)

	getRegDesc := "This command retrieves details about a private container registry including its name and the endpoint used to access it."
	CmdBuilder(cmd, RunRegistryGet, "get", "Retrieve details about a container registry",
		getRegDesc, Writer, aliasOpt("g"), displayerType(&displayers.Registry{}))

	deleteRegDesc := "This command permanently deletes a private container registry and all of its contents."
	cmdRunRegistryDelete := CmdBuilder(cmd, RunRegistryDelete, "delete",
		"Delete a container registry", deleteRegDesc, Writer, aliasOpt("d", "del", "rm"))
	AddBoolFlag(cmdRunRegistryDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force registry delete")

	loginRegDesc := "This command logs in Docker so that pull and push commands to your private container registry will be authenticated."
	CmdBuilder(cmd, RunRegistryLogin, "login", "Log in Docker to a container registry",
		loginRegDesc, Writer)

	logoutRegDesc := "This command logs Docker out of the private container registry, revoking access to it."
	CmdBuilder(cmd, RunRegistryLogout, "logout", "Log out Docker from a container registry",
		logoutRegDesc, Writer)

	kubeManifestDesc := `This command outputs a YAML-formated Kubernetes secret manifest that can be used to grant a Kubernetes cluster pull access to your private container registry.

Redirect the command's output to a file to save the manifest for later use or pipe it directly to kubectl to create the secret in your cluster:

    doctl registry kubernetes-manifest | kubectl apply -f -
`
	cmdRunKubernetesManifest := CmdBuilder(cmd, RunKubernetesManifest, "kubernetes-manifest",
		"Generate a Kubernetes secret manifest for a registry",
		kubeManifestDesc, Writer, aliasOpt("k8s"))
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectName, "", "",
		"The secret name to create. Defaults to the registry name prefixed with \"registry-\"")
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectNamespace, "",
		"default", "The Kubernetes namespace to hold the secret")

	return cmd
}

// Repository creates the repository sub-command
func Repository() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "repository",
			Aliases: []string{"repo", "r"},
			Short:   "[Beta] Display commands for working with repositories in a container registry",
			Long:    "[Beta] The subcommands of `doctl registry repository` help you command actions related to a repository.",
			Hidden:  true,
		},
	}

	listRepositoriesDesc := `
	This command retrieves information about repositories in a registry, including:
	- The repository name
	- The latest tag of the repository
	- The compressed size for the latest tag
	- The manifest digest for the latest tag
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositories, "list",
		"List repositories for a container registry", listRepositoriesDesc,
		Writer, aliasOpt("ls"), displayerType(&displayers.Repository{}),
	)

	listRepositoryTagsDesc := `
	This command retrieves information about tags in a repository, including:
	- The tag name
	- The compressed size
	- The manifest digest
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositoryTags, "list-tags <repository>",
		"List tags for a repository in a container registry", listRepositoryTagsDesc,
		Writer, aliasOpt("lt"), displayerType(&displayers.RepositoryTag{}),
	)

	deleteTagDesc := "This command permanently deletes one or more repository tags."
	cmdRunRepositoryDeleteTag := CmdBuilder(
		cmd,
		RunRepositoryDeleteTag,
		"delete-tag <repository> <tag>...",
		"Delete one or more container repository tags",
		deleteTagDesc,
		Writer,
		aliasOpt("dt"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteTag, doctl.ArgForce, doctl.ArgShortForce, false, "Force tag deletion")

	deleteManifestDesc := "This command permanently deletes one or more repository manifests by digest."
	cmdRunRepositoryDeleteManifest := CmdBuilder(
		cmd,
		RunRepositoryDeleteManifest,
		"delete-manifest <repository> <manifest-digest>...",
		"Delete one or more container repository manifests by digest",
		deleteManifestDesc,
		Writer,
		aliasOpt("dm"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteManifest, doctl.ArgForce, doctl.ArgShortForce, false, "Force manifest deletion")

	return cmd
}

// Registry Run Commands

// RunRegistryCreate creates a registry
func RunRegistryCreate(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	name := c.Args[0]
	rs := c.Registry()

	rcr := &godo.RegistryCreateRequest{Name: name}
	r, err := rs.Create(rcr)
	if err != nil {
		return err
	}

	return displayRegistries(c, *r)
}

// RunRegistryGet returns the registry
func RunRegistryGet(c *CmdConfig) error {
	reg, err := c.Registry().Get()
	if err != nil {
		return err
	}

	return displayRegistries(c, *reg)
}

// RunRegistryDelete delete the registry
func RunRegistryDelete(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if !force && AskForConfirm("delete registry") != nil {
		return fmt.Errorf("operation aborted")
	}

	return c.Registry().Delete()
}

// store execCommand in a variable. Lets us override it while testing
var execCommand = exec.Command

// RunRegistryLogin logs in Docker to the registry
func RunRegistryLogin(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	fmt.Printf("Logging Docker in to %s\n", c.Registry().Endpoint())

	creds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: true,
	})
	if err != nil {
		return err
	}

	var dc dockerConfig
	err = json.Unmarshal(creds.DockerConfigJSON, &dc)
	if err != nil {
		return err
	}

	// read the login credentials from the docker config
	for host, conf := range dc.Auths {
		// decode and split into username + password
		creds, err := base64.StdEncoding.DecodeString(conf.Auth)
		if err != nil {
			return err
		}

		splitCreds := strings.Split(string(creds), ":")
		if len(splitCreds) != 2 {
			return fmt.Errorf("got invalid docker credentials")
		}
		user, pass := splitCreds[0], splitCreds[1]

		// log in via the docker cli
		args := []string{
			"login", host,
			"-u", user,
			"--password-stdin",
		}
		cmd := execCommand("docker", args...)
		cmd.Stdin = strings.NewReader(pass)
		cmd.Stdout = c.Out
		cmd.Stderr = c.Out

		err = cmd.Run()
		if err != nil {
			return err
		}
	}

	return nil
}

// RunKubernetesManifest prints a Kubernetes manifest that provides read/pull access to the registry
func RunKubernetesManifest(c *CmdConfig) error {
	secretName, err := c.Doit.GetString(c.NS, doctl.ArgObjectName)
	if err != nil {
		return err
	}
	secretNamespace, err := c.Doit.GetString(c.NS, doctl.ArgObjectNamespace)
	if err != nil {
		return err
	}

	// if no secret name supplied, use the registry name
	if secretName == "" {
		reg, err := c.Registry().Get()
		if err != nil {
			return err
		}
		secretName = "registry-" + reg.Name
	}

	// fetch docker config
	dockerCreds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: false,
	})
	if err != nil {
		return err
	}

	// create the manifest for the secret
	secret := &k8sapiv1.Secret{
		TypeMeta: k8smetav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: "v1",
		},
		ObjectMeta: k8smetav1.ObjectMeta{
			Name:      secretName,
			Namespace: secretNamespace,
		},
		Type: k8sapiv1.SecretTypeDockerConfigJson,
		Data: map[string][]byte{
			".dockerconfigjson": dockerCreds.DockerConfigJSON,
		},
	}

	serializer := k8sjson.NewSerializerWithOptions(
		k8sjson.DefaultMetaFactory, nil, nil,
		k8sjson.SerializerOptions{
			Yaml:   true,
			Pretty: true,
			Strict: true,
		},
	)

	return serializer.Encode(secret, c.Out)
}

// RunRegistryLogout logs Docker out of the registry
func RunRegistryLogout(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	cmd := execCommand("docker", "logout", c.Registry().Endpoint())
	cmd.Stdout = c.Out
	cmd.Stderr = c.Out

	return cmd.Run()
}

// Repository Run Commands

// RunListRepositories lists repositories for the registry
func RunListRepositories(c *CmdConfig) error {
	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repositories, err := c.Registry().ListRepositories(registry.Name)
	if err != nil {
		return err
	}

	return displayRepositories(c, repositories...)
}

// RunListRepositoryTags lists tags for the repository in a registry
func RunListRepositoryTags(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	tags, err := c.Registry().ListRepositoryTags(registry.Name, c.Args[0])
	if err != nil {
		return err
	}

	return displayRepositoryTags(c, tags...)
}

// RunRepositoryDeleteTag deletes one or more repository tags
func RunRepositoryDeleteTag(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	tags := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository tag(s)", len(tags))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, tag := range tags {
		if err := c.Registry().DeleteTag(registry.Name, repository, tag); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository tags: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

// RunRepositoryDeleteManifest deletes one or more repository manifests by digest
func RunRepositoryDeleteManifest(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	digests := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository manifest(s) by digest (including associated tags)", len(digests))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, digest := range digests {
		if err := c.Registry().DeleteManifest(registry.Name, repository, digest); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository manifests: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

func displayRegistries(c *CmdConfig, registries ...do.Registry) error {
	item := &displayers.Registry{
		Registries: registries,
	}
	return c.Display(item)
}

func displayRepositories(c *CmdConfig, repositories ...do.Repository) error {
	item := &displayers.Repository{
		Repositories: repositories,
	}
	return c.Display(item)
}

func displayRepositoryTags(c *CmdConfig, tags ...do.RepositoryTag) error {
	item := &displayers.RepositoryTag{
		Tags: tags,
	}
	return c.Display(item)
}
<|endoftext|>


<commit_before>package stan_output

import (
	"fmt"
	"log"
	"os"

	"github.com/google/uuid"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/nats-io/nats.go"
	"github.com/nats-io/stan.go"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	stanDefaultTimeout      = 10
	stanDefaultPingInterval = 5
	stanDefaultPingRetry    = 2
)

func init() {
	outputs.Register("stan", func() outputs.Output {
		return &StanOutput{
			Cfg: &Config{},
		}
	})
}

// StanOutput //
type StanOutput struct {
	Cfg      *Config
	conn     stan.Conn
	metrics  []prometheus.Collector
	logger   *log.Logger
	stopChan chan struct{}
}

// Config //
type Config struct {
	Name         string
	Address      string
	Subject      string
	Username     string
	Password     string
	ClusterName  string
	Timeout      int
	PingInterval int
	PingRetry    int
}

// Init //
func (s *StanOutput) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, s.Cfg)
	if err != nil {
		return err
	}
	if s.Cfg.Name == "" {
		s.Cfg.Name = "gnmiclient-" + uuid.New().String()
	}
	if s.Cfg.ClusterName == "" {
		return fmt.Errorf("clusterName is mandatory")
	}
	s.conn, err = createSTANConn(s.Cfg)
	if err != nil {
		return err
	}
	s.logger = log.New(os.Stderr, "stan_output ", log.LstdFlags|log.Lmicroseconds)
	if logger != nil {
		s.logger.SetOutput(logger.Writer())
		s.logger.SetFlags(logger.Flags())
	}
	s.stopChan = make(chan struct{})
	s.logger.Printf("initialized stan producer")
	return nil
}

// Write //
func (s *StanOutput) Write(b []byte, meta outputs.Meta) {
	err := s.conn.Publish(s.Cfg.Subject, b)
	if err != nil {
		log.Printf("failed to write to stan subject '%s': %v", s.Cfg.Subject, err)
		return
	}
	//s.logger.Printf("wrote %d bytes to stan_subject=%s", len(b), s.Cfg.Subject)
}

// Metrics //
func (s *StanOutput) Metrics() []prometheus.Collector { return s.metrics }

// Close //
func (s *StanOutput) Close() error {
	close(s.stopChan)
	return s.conn.Close()
}

func createSTANConn(c *Config) (stan.Conn, error) {
	if c.Timeout == 0 {
		c.Timeout = stanDefaultTimeout
	}
	if c.PingInterval == 0 {
		c.PingInterval = stanDefaultPingInterval
	}
	if c.PingRetry == 0 {
		c.PingRetry = stanDefaultPingRetry
	}
	opts := []nats.Option{
		nats.Name(c.Name),
		// nats.Timeout(time.Duration(c.Timeout) * time.Second),
		// nats.PingInterval(time.Duration(c.PingInterval) * time.Second),
		// nats.MaxPingsOutstanding(c.PingRetry),
	}
	if c.Username != "" && c.Password != "" {
		opts = append(opts, nats.UserInfo(c.Username, c.Password))
	}
	nc, err := nats.Connect(c.Address, opts...)
	if err != nil {
		return nil, err
	}
	sc, err := stan.Connect(c.ClusterName, c.Name,
		stan.NatsConn(nc),
		stan.Pings(c.PingInterval, c.PingRetry),
		stan.SetConnectionLostHandler(func(_ stan.Conn, err error) {
			log.Fatalf("STAN connection lost, reason: %v", err)
		}))
	if err != nil {
		return nil, fmt.Errorf("cannot connect to %s: %v", c.Address, err)
	}
	return sc, nil
}
<commit_msg>edit createConn func<commit_after>package stan_output

import (
	"fmt"
	"log"
	"os"

	"github.com/google/uuid"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/nats-io/nats.go"
	"github.com/nats-io/stan.go"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	stanDefaultTimeout      = 10
	stanDefaultPingInterval = 5
	stanDefaultPingRetry    = 2
)

func init() {
	outputs.Register("stan", func() outputs.Output {
		return &StanOutput{
			Cfg: &Config{},
		}
	})
}

// StanOutput //
type StanOutput struct {
	Cfg      *Config
	conn     stan.Conn
	metrics  []prometheus.Collector
	logger   *log.Logger
	stopChan chan struct{}
}

// Config //
type Config struct {
	Name         string
	Address      string
	Subject      string
	Username     string
	Password     string
	ClusterName  string
	Timeout      int
	PingInterval int
	PingRetry    int
}

// Init //
func (s *StanOutput) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, s.Cfg)
	if err != nil {
		return err
	}
	if s.Cfg.Name == "" {
		s.Cfg.Name = "gnmiclient-" + uuid.New().String()
	}
	if s.Cfg.ClusterName == "" {
		return fmt.Errorf("clusterName is mandatory")
	}
	s.conn, err = s.createSTANConn(s.Cfg)
	if err != nil {
		return err
	}
	s.logger = log.New(os.Stderr, "stan_output ", log.LstdFlags|log.Lmicroseconds)
	if logger != nil {
		s.logger.SetOutput(logger.Writer())
		s.logger.SetFlags(logger.Flags())
	}
	s.stopChan = make(chan struct{})
	s.logger.Printf("initialized stan producer")
	return nil
}

// Write //
func (s *StanOutput) Write(b []byte, meta outputs.Meta) {
	err := s.conn.Publish(s.Cfg.Subject, b)
	if err != nil {
		log.Printf("failed to write to stan subject '%s': %v", s.Cfg.Subject, err)
		return
	}
	//s.logger.Printf("wrote %d bytes to stan_subject=%s", len(b), s.Cfg.Subject)
}

// Metrics //
func (s *StanOutput) Metrics() []prometheus.Collector { return s.metrics }

// Close //
func (s *StanOutput) Close() error {
	close(s.stopChan)
	return s.conn.Close()
}

func (s *StanOutput) createSTANConn(c *Config) (stan.Conn, error) {
	if c.Timeout == 0 {
		c.Timeout = stanDefaultTimeout
	}
	if c.PingInterval == 0 {
		c.PingInterval = stanDefaultPingInterval
	}
	if c.PingRetry == 0 {
		c.PingRetry = stanDefaultPingRetry
	}
	opts := []nats.Option{
		nats.Name(c.Name),
		// nats.Timeout(time.Duration(c.Timeout) * time.Second),
		// nats.PingInterval(time.Duration(c.PingInterval) * time.Second),
		// nats.MaxPingsOutstanding(c.PingRetry),
	}
	if c.Username != "" && c.Password != "" {
		opts = append(opts, nats.UserInfo(c.Username, c.Password))
	}
	nc, err := nats.Connect(c.Address, opts...)
	if err != nil {
		return nil, err
	}
	sc, err := stan.Connect(c.ClusterName, c.Name,
		stan.NatsConn(nc),
		stan.Pings(c.PingInterval, c.PingRetry),
		stan.SetConnectionLostHandler(func(_ stan.Conn, err error) {
			log.Fatalf("STAN connection lost, reason: %v", err)
		}),
	)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to %s: %v", c.Address, err)
	}
	return sc, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	plus "github.com/google/google-api-go-client/plus/v1"
	"github.com/gorilla/securecookie"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

var (
	cfg      *oauth2.Config
	hashKey  = []byte("very-secret")      // TODO extract to env
	blockKey = []byte("a-lot-secret-key") // TODO extract to env
	sc       = securecookie.New(hashKey, blockKey)
)

func main() {
	sc.SetSerializer(securecookie.JSONEncoder{})

	// read oauth2 config
	env := "GOOGLE_OAUTH2_CONFIG"
	if os.Getenv(env) == "" {
		panic(errors.New(env + " is not set"))
	}
	b, err := ioutil.ReadFile(os.Getenv(env))
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	authConf, err := google.ConfigFromJSON(b)
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	cfg = authConf

	// set up server
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "We log coffee!")
	})
	mux.HandleFunc("/login", login)
	mux.HandleFunc("/oauth2callback", oauth2Callback)
	srv := http.Server{
		Addr:    "127.0.0.1:8000",
		Handler: mux,
	}
	log.Printf("listening at %s", srv.Addr)
	log.Fatal(errors.Wrap(srv.ListenAndServe(), "failed to listen/serve"))
}

func login(w http.ResponseWriter, r *http.Request) {
	url := cfg.AuthCodeURL("todo_rand_state",
		oauth2.SetAuthURLParam("access_type", "offline"),
		oauth2.SetAuthURLParam("scope", "profile"))
	w.Header().Set("Location", url)
	w.WriteHeader(http.StatusFound)
}

func oauth2Callback(w http.ResponseWriter, r *http.Request) {
	if state := r.URL.Query().Get("state"); state != "todo_rand_state" {
		badRequest(w, errors.New("wrong oauth2 state"))
		return
	}

	code := r.URL.Query().Get("code")
	if code == "" {
		badRequest(w, errors.New("missing oauth2 grant code"))
		return
	}

	tok, err := cfg.Exchange(oauth2.NoContext, code)
	if err != nil {
		badRequest(w, errors.Wrap(err, "oauth2 token exchange failed"))
		return
	}

	tokenSource := cfg.TokenSource(oauth2.NoContext, tok)
	svc, err := plus.New(oauth2.NewClient(oauth2.NoContext, tokenSource))
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to construct g+ client"))
		return
	}
	me, err := plus.NewPeopleService(svc).Get("me").Do()
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to query user g+ profile"))
		return
	}

	// encrypt the cookie
	newToken, err := tokenSource.Token()
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to extract token from tokensource"))
		return
	}
	tokEncoded, err := sc.Encode("oauth2_token", newToken)
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to encode the token"))
		return
	}

	// TODO save cookies
	http.SetCookie(w, &http.Cookie{
		Name:  "oauth2_token",
		Path:  "/",
		Value: tokEncoded,
	})

	log.Printf("Logged in user id: %s", me.Id)
	fmt.Fprintf(w, "Hello "+me.DisplayName+" ("+fmt.Sprintf("%v", me.IsPlusUser)+")")
	// TODO redirect to somewhere meaningful
}

func badRequest(w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprint(w, errors.Wrap(err, "bad request"))
}
<commit_msg>web: /logout and parse cookies in /<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	plus "github.com/google/google-api-go-client/plus/v1"
	"github.com/gorilla/securecookie"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

var (
	cfg      *oauth2.Config
	hashKey  = []byte("very-secret")      // TODO extract to env
	blockKey = []byte("a-lot-secret-key") // TODO extract to env
	sc       = securecookie.New(hashKey, blockKey)
)

func main() {
	sc.SetSerializer(securecookie.JSONEncoder{})

	// read oauth2 config
	env := "GOOGLE_OAUTH2_CONFIG"
	if os.Getenv(env) == "" {
		panic(errors.New(env + " is not set"))
	}
	b, err := ioutil.ReadFile(os.Getenv(env))
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	authConf, err := google.ConfigFromJSON(b)
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	cfg = authConf

	// set up server
	mux := http.NewServeMux()
	mux.HandleFunc("/", home)
	mux.HandleFunc("/login", login)
	mux.HandleFunc("/logout", logout)
	mux.HandleFunc("/oauth2callback", oauth2Callback)
	srv := http.Server{
		Addr:    "127.0.0.1:8000",
		Handler: mux,
	}
	log.Printf("listening at %s", srv.Addr)
	log.Fatal(errors.Wrap(srv.ListenAndServe(), "failed to listen/serve"))
}

func home(w http.ResponseWriter, r *http.Request) {

	c, err := r.Cookie("oauth2_token")
	if err == http.ErrNoCookie {
		fmt.Fprint(w, "We log coffee!")
		return
	}

	var token *oauth2.Token
	if err := sc.Decode("oauth2_token", c.Value, &token); err != nil {
		badRequest(w, errors.Wrap(err, "failed to decode cookie"))
		return
	}

	tokenSource := cfg.TokenSource(oauth2.NoContext, token)
	svc, err := plus.New(oauth2.NewClient(oauth2.NoContext, tokenSource))
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to construct g+ client"))
		return
	}
	me, err := plus.NewPeopleService(svc).Get("me").Do()
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to query user g+ profile"))
		return
	}
	fmt.Fprint(w, "Home page for "+me.DisplayName)
}

func login(w http.ResponseWriter, r *http.Request) {
	url := cfg.AuthCodeURL("todo_rand_state",
		oauth2.SetAuthURLParam("access_type", "offline"),
		oauth2.SetAuthURLParam("scope", "profile"))
	w.Header().Set("Location", url)
	w.WriteHeader(http.StatusFound)
}

func logout(w http.ResponseWriter, r *http.Request) {
	for _, c := range r.Cookies() {
		log.Printf("clearing cookie: %q", c.Name)
		c.Expires = time.Unix(1, 0)
		http.SetCookie(w, c)
	}
	w.Header().Set("Location", "/")
	w.WriteHeader(http.StatusFound)
}

func oauth2Callback(w http.ResponseWriter, r *http.Request) {
	if state := r.URL.Query().Get("state"); state != "todo_rand_state" {
		badRequest(w, errors.New("wrong oauth2 state"))
		return
	}

	code := r.URL.Query().Get("code")
	if code == "" {
		badRequest(w, errors.New("missing oauth2 grant code"))
		return
	}

	tok, err := cfg.Exchange(oauth2.NoContext, code)
	if err != nil {
		badRequest(w, errors.Wrap(err, "oauth2 token exchange failed"))
		return
	}

	tokenSource := cfg.TokenSource(oauth2.NoContext, tok)
	svc, err := plus.New(oauth2.NewClient(oauth2.NoContext, tokenSource))
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to construct g+ client"))
		return
	}
	me, err := plus.NewPeopleService(svc).Get("me").Do()
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to query user g+ profile"))
		return
	}

	// encrypt the cookie
	newToken, err := tokenSource.Token()
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to extract token from tokensource"))
		return
	}
	tokEncoded, err := sc.Encode("oauth2_token", newToken)
	if err != nil {
		badRequest(w, errors.Wrap(err, "failed to encode the token"))
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:  "oauth2_token",
		Path:  "/",
		Value: tokEncoded,
	})

	log.Printf("Authenticated as user id: %s", me.Id)
	w.Header().Set("Location", "/")
	w.WriteHeader(http.StatusFound)
}

func badRequest(w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprint(w, errors.Wrap(err, "bad request"))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
)

var (
	apiServerBaseURL       = flag.String("api-server-base-url", "https://kubernetes.default", "Kubernetes API server base URL")
	apiServerUser          = flag.String("api-server-username", "admin", "Kubernetes API server username to use if no service acccount API token is present.")
	apiServerPassword      = flag.String("api-server-password", "admin123", "Kubernetes API server password to use if no service acccount API token is present.")
	awsKey                 = flag.String("aws-access-key", "", "Default decap AWS access key.  /etc/secrets/aws-key in the cluster overrides this.")
	awsSecret              = flag.String("aws-secret-key", "", "Default decap AWS access secret.  /etc/secrets/aws-secret in the cluster overrides this.")
	awsRegion              = flag.String("aws-region", "us-west-1", "Default decap AWS region.  /etc/secrets/aws-region in the cluster overrides this.")
	githubClientID         = flag.String("github-client-id", "", "Default Github ClientID for quering Github repos.  /etc/secrets/github-client-id in the cluster overrides this.")
	githubClientSecret     = flag.String("github-client-secret", "", "Default Github Client Secret for quering Github repos.  /etc/secrets/github-client-secret in the cluster overrides this.")
	buildScriptsRepo       = flag.String("build-scripts-repo", "https://github.com/ae6rt/decap-build-scripts.git", "Git repo where userland build scripts are held.")
	buildScriptsRepoBranch = flag.String("build-scripts-repo-branch", "master", "Branch or revision to use on git repo where userland build scripts are held.")
	noWebsocket            = flag.Bool("no-websocket", false, "Do not start websocket client that watches pods.")
	versionFlag            = flag.Bool("version", false, "Print version info and exit.")

	Log *log.Logger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)

	buildVersion string
	buildCommit  string
	buildDate    string
	buildGoSDK   string
)

func init() {
	flag.Parse()
	Log.Printf("Version: %s, Commit: %s, Date: %s, Go SDK: %s\n", buildVersion, buildCommit, buildDate, buildGoSDK)
	if *versionFlag {
		os.Exit(0)
	}

	*awsKey = kubeSecret("/etc/secrets/aws-key", *awsKey)
	*awsSecret = kubeSecret("/etc/secrets/aws-secret", *awsSecret)
	*awsRegion = kubeSecret("/etc/secrets/aws-region", *awsRegion)
	*githubClientID = kubeSecret("/etc/secrets/github-client-id", *githubClientID)
	*githubClientSecret = kubeSecret("/etc/secrets/github-client-secret", *githubClientSecret)
}

func writeCorsHeaders(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "http://localhost:9000")
	w.Header().Set("Access-Control-Allow-Headers", "DECAP-APP-NAME, DECAP-API-TOKEN, Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With")
	w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
}

func handleOptions(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	writeCorsHeaders(w)
}

func main() {
	locker := NewDefaultLock([]string{"http://localhost:2379"})
	buildLauncher := NewBuilder(*apiServerBaseURL, *apiServerUser, *apiServerPassword, *awsKey, *awsSecret, *awsRegion, locker, *buildScriptsRepo, *buildScriptsRepoBranch)
	awsStorageService := NewAWSStorageService(*awsKey, *awsSecret, *awsRegion)
	scmManagers := map[string]SCMClient{
		"github": NewGithubClient("https://api.github.com", *githubClientID, *githubClientSecret),
	}

	router := httprouter.New()
	router.ServeFiles("/decap/*filepath", http.Dir("./static"))
	router.GET("/api/v1/version", VersionHandler)
	router.GET("/api/v1/projects", ProjectsHandler)
	router.GET("/api/v1/projects/:team/:project/refs", ProjectRefsHandler(scmManagers))
	router.GET("/api/v1/builds/:team/:project", BuildsHandler(awsStorageService))
	router.DELETE("/api/v1/builds/:id", StopBuildHandler(buildLauncher))
	router.POST("/api/v1/builds/:team/:project", ExecuteBuildHandler(buildLauncher))
	router.GET("/api/v1/teams", TeamsHandler)
	router.GET("/api/v1/logs/:id", LogHandler(awsStorageService))
	router.GET("/api/v1/artifacts/:id", ArtifactsHandler(awsStorageService))
	router.POST("/api/v1/shutdown/:state", ShutdownHandler)
	router.POST("/hooks/:repomanager", HooksHandler(*buildScriptsRepo, *buildScriptsRepoBranch, buildLauncher))
	router.OPTIONS("/api/v1/*filepath", handleOptions)

	var err error
	projects, err = assembleProjects(*buildScriptsRepo, *buildScriptsRepoBranch)
	if err != nil {
		Log.Printf("Cannot clone build scripts repository: %v\n", err)
	}
	for _, v := range projects {
		Log.Printf("Project: %+v\n", v)
	}

	if !*noWebsocket {
		go buildLauncher.Websock()
	}

	go buildLauncher.LaunchDeferred()

	corsWrapper := func(handler http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			writeCorsHeaders(w)
			handler.ServeHTTP(w, r)
		})
	}

	Log.Println("decap ready on port 9090...")
	http.ListenAndServe(":9090", corsWrapper(router))
}
<commit_msg>simplified cors handling<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
)

var (
	apiServerBaseURL       = flag.String("api-server-base-url", "https://kubernetes.default", "Kubernetes API server base URL")
	apiServerUser          = flag.String("api-server-username", "admin", "Kubernetes API server username to use if no service acccount API token is present.")
	apiServerPassword      = flag.String("api-server-password", "admin123", "Kubernetes API server password to use if no service acccount API token is present.")
	awsKey                 = flag.String("aws-access-key", "", "Default decap AWS access key.  /etc/secrets/aws-key in the cluster overrides this.")
	awsSecret              = flag.String("aws-secret-key", "", "Default decap AWS access secret.  /etc/secrets/aws-secret in the cluster overrides this.")
	awsRegion              = flag.String("aws-region", "us-west-1", "Default decap AWS region.  /etc/secrets/aws-region in the cluster overrides this.")
	githubClientID         = flag.String("github-client-id", "", "Default Github ClientID for quering Github repos.  /etc/secrets/github-client-id in the cluster overrides this.")
	githubClientSecret     = flag.String("github-client-secret", "", "Default Github Client Secret for quering Github repos.  /etc/secrets/github-client-secret in the cluster overrides this.")
	buildScriptsRepo       = flag.String("build-scripts-repo", "https://github.com/ae6rt/decap-build-scripts.git", "Git repo where userland build scripts are held.")
	buildScriptsRepoBranch = flag.String("build-scripts-repo-branch", "master", "Branch or revision to use on git repo where userland build scripts are held.")
	noWebsocket            = flag.Bool("no-websocket", false, "Do not start websocket client that watches pods.")
	versionFlag            = flag.Bool("version", false, "Print version info and exit.")

	Log *log.Logger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)

	buildVersion string
	buildCommit  string
	buildDate    string
	buildGoSDK   string
)

func init() {
	flag.Parse()
	Log.Printf("Version: %s, Commit: %s, Date: %s, Go SDK: %s\n", buildVersion, buildCommit, buildDate, buildGoSDK)
	if *versionFlag {
		os.Exit(0)
	}

	*awsKey = kubeSecret("/etc/secrets/aws-key", *awsKey)
	*awsSecret = kubeSecret("/etc/secrets/aws-secret", *awsSecret)
	*awsRegion = kubeSecret("/etc/secrets/aws-region", *awsRegion)
	*githubClientID = kubeSecret("/etc/secrets/github-client-id", *githubClientID)
	*githubClientSecret = kubeSecret("/etc/secrets/github-client-secret", *githubClientSecret)
}

func handleOptions(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Access-Control-Allow-Origin", "http://localhost:9000")
	w.Header().Set("Access-Control-Allow-Headers", "DECAP-APP-NAME, DECAP-API-TOKEN, Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With")
	w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
}

func main() {
	locker := NewDefaultLock([]string{"http://localhost:2379"})
	buildLauncher := NewBuilder(*apiServerBaseURL, *apiServerUser, *apiServerPassword, *awsKey, *awsSecret, *awsRegion, locker, *buildScriptsRepo, *buildScriptsRepoBranch)
	awsStorageService := NewAWSStorageService(*awsKey, *awsSecret, *awsRegion)
	scmManagers := map[string]SCMClient{
		"github": NewGithubClient("https://api.github.com", *githubClientID, *githubClientSecret),
	}

	router := httprouter.New()
	router.ServeFiles("/decap/*filepath", http.Dir("./static"))
	router.GET("/api/v1/version", VersionHandler)
	router.GET("/api/v1/projects", ProjectsHandler)
	router.GET("/api/v1/projects/:team/:project/refs", ProjectRefsHandler(scmManagers))
	router.GET("/api/v1/builds/:team/:project", BuildsHandler(awsStorageService))
	router.DELETE("/api/v1/builds/:id", StopBuildHandler(buildLauncher))
	router.POST("/api/v1/builds/:team/:project", ExecuteBuildHandler(buildLauncher))
	router.GET("/api/v1/teams", TeamsHandler)
	router.GET("/api/v1/logs/:id", LogHandler(awsStorageService))
	router.GET("/api/v1/artifacts/:id", ArtifactsHandler(awsStorageService))
	router.POST("/api/v1/shutdown/:state", ShutdownHandler)
	router.POST("/hooks/:repomanager", HooksHandler(*buildScriptsRepo, *buildScriptsRepoBranch, buildLauncher))
	router.OPTIONS("/api/v1/*filepath", handleOptions)

	var err error
	projects, err = assembleProjects(*buildScriptsRepo, *buildScriptsRepoBranch)
	if err != nil {
		Log.Printf("Cannot clone build scripts repository: %v\n", err)
	}
	for _, v := range projects {
		Log.Printf("Project: %+v\n", v)
	}

	if !*noWebsocket {
		go buildLauncher.Websock()
	}

	go buildLauncher.LaunchDeferred()

	Log.Println("decap ready on port 9090...")
	http.ListenAndServe(":9090", router)
}
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package player

import (
	"encoding/json"
	"fmt"
)

// RepFn is a type which represents a command function which will be called
// with a standardised command structure when Player methods are used.
type RepFn func(interface{})

// RepAction is a representation of a player action as it would be transmitted.
type RepAction struct {
	Action string      `json:"action"`
	Value  interface{} `json:",omitempty"`
}

type rep struct {
	key string
	fn  RepFn
}

// NewRep creates a Player p which will call fn with a standardised representation for each
// call to methods of p (eee RepActions for the Action mapping).
// FIXME: this is possibly a unhelpful and overly generic mess.
func NewRep(key string, fn RepFn) Player {
	return &rep{
		key: key,
		fn:  fn,
	}
}

func (r rep) sendAction(action string) error {
	r.fn(RepAction{
		Action: action,
	})
	return nil
}

func (r rep) sendActionValue(action string, value interface{}) error {
	r.fn(RepAction{
		Action: action,
		Value:  value,
	})
	return nil
}

func (r rep) Key() string { return r.key }

var RepActions = map[Action]string{
	ActionPlay:            "PLAY",
	ActionPause:           "PAUSE",
	ActionNext:            "NEXT",
	ActionPrev:            "PREV",
	ActionTogglePlayPause: "TOGGLE_PLAY_PAUSE",
	ActionToggleMute:      "TOGGLE_MUTE",

	ActionSetVolume: "SET_VOLUME",
	ActionSetMute:   "SET_MUTE",
	ActionSetTime:   "SET_TIME",
}

func RepActionToAction(a string) (Action, bool) {
	for k, v := range RepActions {
		if v == a {
			return k, true
		}
	}
	return Action(""), false
}

type InvalidActionError string

func (i InvalidActionError) Error() string {
	return fmt.Sprintf("invalid player action: '%s'", string(i))
}

func (r rep) Do(a Action) error {
	s, ok := RepActions[a]
	if !ok {
		return InvalidActionError(a)
	}
	return r.sendAction(s)
}

func (r rep) SetMute(b bool) error      { return r.sendActionValue("mute", b) }
func (r rep) SetVolume(f float64) error { return r.sendActionValue("volume", f) }
func (r rep) SetTime(f float64) error   { return r.sendActionValue("time", f) }

func (r rep) MarshalJSON() ([]byte, error) {
	rep := struct {
		Key string `json:"key"`
	}{
		Key: r.key,
	}
	return json.Marshal(rep)
}
<commit_msg>Added Apply method to RepAction to apply action to Player.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package player

import (
	"encoding/json"
	"fmt"
)

// RepFn is a type which represents a command function which will be called
// with a standardised command structure when Player methods are used.
type RepFn func(interface{})

// RepAction is a representation of a player action as it would be transmitted.
type RepAction struct {
	Action string      `json:"action"`
	Value  interface{} `json:",omitempty"`
}

// Apply applies the action in RepAction to the Player.
func (r RepAction) Apply(p Player) (err error) {
	a := Action(r.Action)
	switch a {
	case ActionPlay, ActionPause, ActionNext, ActionPrev, ActionTogglePlayPause, ActionToggleMute:
		err = p.Do(a)

	case ActionSetVolume, ActionSetMute, ActionSetTime:
		if r.Value == nil {
			err = InvalidValueError("value required")
			break
		}

		switch a {
		case ActionSetVolume:
			f, ok := r.Value.(float64)
			if !ok {
				err = InvalidValueError("invalid volume value: expected float")
				break
			}
			err = p.SetVolume(f)

		case ActionSetMute:
			b, ok := r.Value.(bool)
			if !ok {
				err = InvalidValueError("invalid mute value: expected boolean")
				break
			}
			err = p.SetMute(b)

		case ActionSetTime:
			f, ok := r.Value.(float64)
			if !ok {
				err = InvalidValueError("invalid time value: expected float")
				break
			}
			err = p.SetTime(f)
		}

	default:
		err = InvalidActionError(a)
	}
	return
}

type rep struct {
	key string
	fn  RepFn
}

// NewRep creates a Player p which will call fn with a standardised representation for each
// call to methods of p (eee RepActions for the Action mapping).
// FIXME: this is possibly a unhelpful and overly generic mess.
func NewRep(key string, fn RepFn) Player {
	return &rep{
		key: key,
		fn:  fn,
	}
}

func (r rep) sendAction(action string) error {
	r.fn(RepAction{
		Action: action,
	})
	return nil
}

func (r rep) sendActionValue(action string, value interface{}) error {
	r.fn(RepAction{
		Action: action,
		Value:  value,
	})
	return nil
}

func (r rep) Key() string { return r.key }

var RepActions = map[Action]string{
	ActionPlay:            "PLAY",
	ActionPause:           "PAUSE",
	ActionNext:            "NEXT",
	ActionPrev:            "PREV",
	ActionTogglePlayPause: "TOGGLE_PLAY_PAUSE",
	ActionToggleMute:      "TOGGLE_MUTE",

	ActionSetVolume: "SET_VOLUME",
	ActionSetMute:   "SET_MUTE",
	ActionSetTime:   "SET_TIME",
}

func RepActionToAction(a string) (Action, bool) {
	for k, v := range RepActions {
		if v == a {
			return k, true
		}
	}
	return Action(""), false
}

type InvalidActionError string

func (i InvalidActionError) Error() string {
	return fmt.Sprintf("invalid player action: '%s'", string(i))
}

func (r rep) Do(a Action) error {
	s, ok := RepActions[a]
	if !ok {
		return InvalidActionError(a)
	}
	return r.sendAction(s)
}

func (r rep) SetMute(b bool) error      { return r.sendActionValue("mute", b) }
func (r rep) SetVolume(f float64) error { return r.sendActionValue("volume", f) }
func (r rep) SetTime(f float64) error   { return r.sendActionValue("time", f) }

func (r rep) MarshalJSON() ([]byte, error) {
	rep := struct {
		Key string `json:"key"`
	}{
		Key: r.key,
	}
	return json.Marshal(rep)
}
<|endoftext|>


<commit_before>package manta

import (
	"testing"

	"github.com/dotabuff/manta/dota"
	"github.com/golang/protobuf/proto"
	"github.com/stretchr/testify/assert"
)

const (
	HANDLE_NONE = uint32(2097151)
)

func TestReadProperties(t *testing.T) {
	assert := assert.New(t)

	scenarios := []struct {
		tableName   string                 // the name of the table, must have a sendtable fixture.
		run         bool                   // whether or not we run the test.
		debug       bool                   // whether or not we print debugging output.
		expectCount int                    // how many result entries we expect.
		expectKeys  map[string]interface{} // a map of entries we expect to be present.
	}{

		/*
			class id=571 name=CDOTATeam
			table id=586 name=CDOTATeam version=0
			 -> prop 0: type:uint8(727) name:m_iTeamNum(784) sendNode: (root)(734)
			 -> prop 1: type:CUtlVector< CHandle< CBasePlayer > >(1592) name:m_aPlayers(1593) sendNode: (root)(734)
			 -> prop 2: type:int32(706) name:m_iScore(1594) sendNode: (root)(734)
			 -> prop 3: type:int32(706) name:m_iRoundsWon(1595) sendNode: (root)(734)
			 -> prop 4: type:char[129](1596) name:m_szTeamname(1597) sendNode: (root)(734)
			 -> prop 5: type:int32(706) name:m_iHeroKills(1598) sendNode: (root)(734)
			 -> prop 6: type:int32(706) name:m_iTowerKills(1286) sendNode: (root)(734)
			 -> prop 7: type:int32(706) name:m_iBarracksKills(1599) sendNode: (root)(734)
			 -> prop 8: type:uint32(748) name:m_unTournamentTeamID(1600) sendNode: (root)(734)
			 -> prop 9: type:uint64(715) name:m_ulTeamLogo(1601) sendNode: (root)(734)
			 -> prop 10: type:uint64(715) name:m_ulTeamBaseLogo(1602) sendNode: (root)(734)
			 -> prop 11: type:uint64(715) name:m_ulTeamBannerLogo(1603) sendNode: (root)(734)
			 -> prop 12: type:bool(723) name:m_bTeamComplete(1604) sendNode: (root)(734)
			 -> prop 13: type:Color(774) name:m_CustomHealthbarColor(1605) sendNode: (root)(734)
			 -> prop 14: type:char[33](1606) name:m_szTag(1607) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTATeam",
			run:         true,
			debug:       false,
			expectCount: 15,
			expectKeys: map[string]interface{}{
				"m_iTeamNum":             uint64(0),
				"m_aPlayers":             uint32(0), // Most certainly wrong
				"m_iScore":               int32(0),
				"m_iRoundsWon":           int32(0),
				"m_szTeamname":           "Unassigned",
				"m_iHeroKills":           int32(0),
				"m_iTowerKills":          int32(0),
				"m_iBarracksKills":       int32(0),
				"m_unTournamentTeamID":   uint64(0),
				"m_ulTeamLogo":           uint64(0),
				"m_ulTeamBaseLogo":       uint64(0),
				"m_ulTeamBannerLogo":     uint64(0),
				"m_bTeamComplete":        false,
				"m_CustomHealthbarColor": int32(0),
				"m_szTag":                "",
			},
		},

		/*
			class id=559 name=CDOTAFogOfWarWasVisible
			table id=568 name=CDOTAFogOfWarWasVisible version=0
			 -> prop 0: type:uint64[1024](1355) name:m_bWasVisible(1356) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTAFogOfWarWasVisible",
			run:         true,
			debug:       false,
			expectCount: 1024,
			expectKeys: map[string]interface{}{
				"m_bWasVisible.0000": uint64(0),
				"m_bWasVisible.0001": uint64(0),
				"m_bWasVisible.0511": uint64(0),
				"m_bWasVisible.1023": uint64(0),
				"m_bWasVisible.1024": nil,
			},
		},

		/*
			class id=643 name=CRagdollManager
			table id=661 name=CRagdollManager version=0
			 -> prop 0: type:int8(718) name:m_iCurrentMaxRagdollCount(1982) sendNode: (root)(734)
		*/
		{
			tableName:   "CRagdollManager",
			run:         true,
			debug:       false,
			expectCount: 1,
			expectKeys: map[string]interface{}{
				"m_iCurrentMaxRagdollCount": int32(-1),
			},
		},

		/*
			class id=342 name=CDOTA_DataDire
			table id=351 name=CDOTA_DataDire version=0
			 -> prop 0: type:int32[10](1164) name:m_iReliableGold(1165) sendNode: (root)(734)
			 -> prop 1: type:int32[10](1164) name:m_iUnreliableGold(1166) sendNode: (root)(734)
			 -> prop 2: type:uint8(727) name:m_iTeamNum(784) sendNode: (root)(734)
			 -> prop 3: type:int32[30](1167) name:m_iStartingPositions(1168) sendNode: (root)(734)
			 -> prop 4: type:uint64[256](1169) name:m_bWorldTreeState(1170) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTA_DataDire",
			run:         true,
			debug:       false,
			expectCount: 428,
			expectKeys: map[string]interface{}{
				"m_vecDataTeam.0000.m_iUnreliableGold": int32(625),
				"m_iTeamNum":                           uint64(3),
				"m_bWorldTreeState.0000":               uint64(18446744073709551615),
				"m_bWorldTreeState.0110":               uint64(18446744073709551615),
				"m_bWorldTreeState.0128":               uint64(0),
				"m_bWorldTreeState.0129":               uint64(0),
				"m_bWorldTreeState.0255":               uint64(0),
			},
		},

		{
			tableName:   "CDOTA_DataRadiant",
			run:         true,
			debug:       false,
			expectCount: 428,
			expectKeys: map[string]interface{}{
				"m_vecDataTeam.0000.m_iUnreliableGold": int32(625),
				"m_iTeamNum":                           uint64(2),
				"m_bWorldTreeState.0000":               uint64(18446744073709551615),
				"m_bWorldTreeState.0110":               uint64(18446744073709551615),
				"m_bWorldTreeState.0128":               uint64(0),
				"m_bWorldTreeState.0129":               uint64(0),
				"m_bWorldTreeState.0255":               uint64(0),
			},
		},

		{
			tableName:   "CDOTA_DataSpectator",
			run:         true,
			debug:       false,
			expectCount: 284,
			expectKeys: map[string]interface{}{
				"m_hPrimaryRune":   HANDLE_NONE,
				"m_hSecondaryRune": HANDLE_NONE,
				"m_iNetWorth.0000": int32(625),
				"m_iNetWorth.0009": int32(625),
			},
		},

		{
			tableName:   "CDOTA_DataCustomTeam",
			run:         true,
			debug:       false,
			expectCount: 258,
			expectKeys: map[string]interface{}{
				"m_iTeamNum":             uint64(6),
				"m_bWorldTreeState.0000": uint64(18446744073709551615),
				"m_bWorldTreeState.0110": uint64(18446744073709551615),
				"m_bWorldTreeState.0128": uint64(0),
				"m_bWorldTreeState.0129": uint64(0),
				"m_bWorldTreeState.0255": uint64(0),
			},
		},

		{
			tableName:   "CFogController",
			run:         true,
			debug:       false,
			expectCount: 21,
			expectKeys: map[string]interface{}{
				"colorPrimary":         uint64(4294963108),
				"colorSecondary":       uint64(4294963108),
				"colorPrimaryLerpTo":   uint64(4294963108),
				"colorSecondaryLerpTo": uint64(4294963108),
				"enable":               true,
				"blend":                false,
				"m_bNoReflectionFog":   true,
			},
		},

		{
			tableName:   "CDOTASpectatorGraphManagerProxy",
			run:         true,
			debug:       false,
			expectCount: 412,
			expectKeys: map[string]interface{}{
				"CDOTASpectatorGraphManager.m_rgPlayerGraphData.0000": uint32(2097151),
				"CDOTASpectatorGraphManager.m_rgDireNetWorth.0063":    int32(0),
				"CDOTASpectatorGraphManager.m_nGoldGraphVersion":      int32(3),
			},
		},

		{
			tableName:   "CSpeechBubbleManager",
			run:         true,
			debug:       false,
			expectCount: 25, // WRONG, should carry the array index instead of overwriting. Needs some rework
			expectKeys: map[string]interface{}{
				"m_SpeechBubbles.0000.m_hNPC":       HANDLE_NONE,
				"m_SpeechBubbles.0000.m_flDuration": float32(0),
				"m_SpeechBubbles.0000.m_unOffsetX":  uint32(0),
				"m_SpeechBubbles.0000.m_unCount":    uint32(0),
			},
		},

		{
			tableName:   "CBaseEntity",
			run:         true,
			debug:       false,
			expectCount: 35,
			expectKeys: map[string]interface{}{
				"m_pEntity": true,
			},
		},

		{
			tableName:   "CBaseAnimating",
			run:         true,
			debug:       false,
			expectCount: 111,
			expectKeys: map[string]interface{}{
				"CRenderComponent":                      1,
				"CPhysicsComponent":                     1,
				"m_hEffectEntity":                       HANDLE_NONE,
				"CBodyComponentBaseAnimating.m_hParent": HANDLE_NONE,
				"m_hOwnerEntity":                        HANDLE_NONE,
			},
		},

		/*
			WIP:

			{
				tableName:   "CBaseAnimating",
				run:         false,
				debug:       false,
				expectCount: 0,
				expectKeys:  map[string]interface{}{},
			},
		*/
	}

	// Load our send tables
	m := &dota.CDemoSendTables{}
	if err := proto.Unmarshal(_read_fixture("send_tables/1731962898.pbmsg"), m); err != nil {
		panic(err)
	}

	// Retrieve the flattened field serializer
	fs := ParseSendTables(m, GetDefaultPropertySerializerTable())

	// Iterate through scenarios
	for _, s := range scenarios {
		// Load up a fixture
		buf := _read_fixture(_sprintf("instancebaseline/1731962898_%s.rawbuf", s.tableName))

		serializer := fs.Serializers[s.tableName][0]
		assert.NotNil(serializer)

		// Optionally skip
		if !s.run {
			continue
		}

		// Optionally disable debugging
		debugMode = s.debug

		// Read properties
		r := NewReader(buf)
		props := ReadProperties(r, serializer)
		assert.Equal(len(props), s.expectCount)

		for k, v := range s.expectKeys {
			assert.EqualValues(v, props[k])
		}

		// There shouldn't be more than 8 bits left in the buffer
		_debugf("Remaining bits %v", r.remBits())
		assert.True(r.remBits() < 8)
	}
}
<commit_msg>Working CWorld test<commit_after>package manta

import (
	"testing"

	"github.com/dotabuff/manta/dota"
	"github.com/golang/protobuf/proto"
	"github.com/stretchr/testify/assert"
)

const (
	HANDLE_NONE = uint32(2097151)
)

func TestReadProperties(t *testing.T) {
	assert := assert.New(t)

	scenarios := []struct {
		tableName   string                 // the name of the table, must have a sendtable fixture.
		run         bool                   // whether or not we run the test.
		debug       bool                   // whether or not we print debugging output.
		expectCount int                    // how many result entries we expect.
		expectKeys  map[string]interface{} // a map of entries we expect to be present.
	}{

		/*
			class id=571 name=CDOTATeam
			table id=586 name=CDOTATeam version=0
			 -> prop 0: type:uint8(727) name:m_iTeamNum(784) sendNode: (root)(734)
			 -> prop 1: type:CUtlVector< CHandle< CBasePlayer > >(1592) name:m_aPlayers(1593) sendNode: (root)(734)
			 -> prop 2: type:int32(706) name:m_iScore(1594) sendNode: (root)(734)
			 -> prop 3: type:int32(706) name:m_iRoundsWon(1595) sendNode: (root)(734)
			 -> prop 4: type:char[129](1596) name:m_szTeamname(1597) sendNode: (root)(734)
			 -> prop 5: type:int32(706) name:m_iHeroKills(1598) sendNode: (root)(734)
			 -> prop 6: type:int32(706) name:m_iTowerKills(1286) sendNode: (root)(734)
			 -> prop 7: type:int32(706) name:m_iBarracksKills(1599) sendNode: (root)(734)
			 -> prop 8: type:uint32(748) name:m_unTournamentTeamID(1600) sendNode: (root)(734)
			 -> prop 9: type:uint64(715) name:m_ulTeamLogo(1601) sendNode: (root)(734)
			 -> prop 10: type:uint64(715) name:m_ulTeamBaseLogo(1602) sendNode: (root)(734)
			 -> prop 11: type:uint64(715) name:m_ulTeamBannerLogo(1603) sendNode: (root)(734)
			 -> prop 12: type:bool(723) name:m_bTeamComplete(1604) sendNode: (root)(734)
			 -> prop 13: type:Color(774) name:m_CustomHealthbarColor(1605) sendNode: (root)(734)
			 -> prop 14: type:char[33](1606) name:m_szTag(1607) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTATeam",
			run:         true,
			debug:       false,
			expectCount: 15,
			expectKeys: map[string]interface{}{
				"m_iTeamNum":             uint64(0),
				"m_aPlayers":             uint32(0), // Most certainly wrong
				"m_iScore":               int32(0),
				"m_iRoundsWon":           int32(0),
				"m_szTeamname":           "Unassigned",
				"m_iHeroKills":           int32(0),
				"m_iTowerKills":          int32(0),
				"m_iBarracksKills":       int32(0),
				"m_unTournamentTeamID":   uint64(0),
				"m_ulTeamLogo":           uint64(0),
				"m_ulTeamBaseLogo":       uint64(0),
				"m_ulTeamBannerLogo":     uint64(0),
				"m_bTeamComplete":        false,
				"m_CustomHealthbarColor": int32(0),
				"m_szTag":                "",
			},
		},

		/*
			class id=559 name=CDOTAFogOfWarWasVisible
			table id=568 name=CDOTAFogOfWarWasVisible version=0
			 -> prop 0: type:uint64[1024](1355) name:m_bWasVisible(1356) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTAFogOfWarWasVisible",
			run:         true,
			debug:       false,
			expectCount: 1024,
			expectKeys: map[string]interface{}{
				"m_bWasVisible.0000": uint64(0),
				"m_bWasVisible.0001": uint64(0),
				"m_bWasVisible.0511": uint64(0),
				"m_bWasVisible.1023": uint64(0),
				"m_bWasVisible.1024": nil,
			},
		},

		/*
			class id=643 name=CRagdollManager
			table id=661 name=CRagdollManager version=0
			 -> prop 0: type:int8(718) name:m_iCurrentMaxRagdollCount(1982) sendNode: (root)(734)
		*/
		{
			tableName:   "CRagdollManager",
			run:         true,
			debug:       false,
			expectCount: 1,
			expectKeys: map[string]interface{}{
				"m_iCurrentMaxRagdollCount": int32(-1),
			},
		},

		/*
			class id=342 name=CDOTA_DataDire
			table id=351 name=CDOTA_DataDire version=0
			 -> prop 0: type:int32[10](1164) name:m_iReliableGold(1165) sendNode: (root)(734)
			 -> prop 1: type:int32[10](1164) name:m_iUnreliableGold(1166) sendNode: (root)(734)
			 -> prop 2: type:uint8(727) name:m_iTeamNum(784) sendNode: (root)(734)
			 -> prop 3: type:int32[30](1167) name:m_iStartingPositions(1168) sendNode: (root)(734)
			 -> prop 4: type:uint64[256](1169) name:m_bWorldTreeState(1170) sendNode: (root)(734)
		*/
		{
			tableName:   "CDOTA_DataDire",
			run:         true,
			debug:       false,
			expectCount: 428,
			expectKeys: map[string]interface{}{
				"m_vecDataTeam.0000.m_iUnreliableGold": int32(625),
				"m_iTeamNum":                           uint64(3),
				"m_bWorldTreeState.0000":               uint64(18446744073709551615),
				"m_bWorldTreeState.0110":               uint64(18446744073709551615),
				"m_bWorldTreeState.0128":               uint64(0),
				"m_bWorldTreeState.0129":               uint64(0),
				"m_bWorldTreeState.0255":               uint64(0),
			},
		},

		{
			tableName:   "CDOTA_DataRadiant",
			run:         true,
			debug:       false,
			expectCount: 428,
			expectKeys: map[string]interface{}{
				"m_vecDataTeam.0000.m_iUnreliableGold": int32(625),
				"m_iTeamNum":                           uint64(2),
				"m_bWorldTreeState.0000":               uint64(18446744073709551615),
				"m_bWorldTreeState.0110":               uint64(18446744073709551615),
				"m_bWorldTreeState.0128":               uint64(0),
				"m_bWorldTreeState.0129":               uint64(0),
				"m_bWorldTreeState.0255":               uint64(0),
			},
		},

		{
			tableName:   "CDOTA_DataSpectator",
			run:         true,
			debug:       false,
			expectCount: 284,
			expectKeys: map[string]interface{}{
				"m_hPrimaryRune":   HANDLE_NONE,
				"m_hSecondaryRune": HANDLE_NONE,
				"m_iNetWorth.0000": int32(625),
				"m_iNetWorth.0009": int32(625),
			},
		},

		{
			tableName:   "CDOTA_DataCustomTeam",
			run:         true,
			debug:       false,
			expectCount: 258,
			expectKeys: map[string]interface{}{
				"m_iTeamNum":             uint64(6),
				"m_bWorldTreeState.0000": uint64(18446744073709551615),
				"m_bWorldTreeState.0110": uint64(18446744073709551615),
				"m_bWorldTreeState.0128": uint64(0),
				"m_bWorldTreeState.0129": uint64(0),
				"m_bWorldTreeState.0255": uint64(0),
			},
		},

		{
			tableName:   "CFogController",
			run:         true,
			debug:       false,
			expectCount: 21,
			expectKeys: map[string]interface{}{
				"colorPrimary":         uint64(4294963108),
				"colorSecondary":       uint64(4294963108),
				"colorPrimaryLerpTo":   uint64(4294963108),
				"colorSecondaryLerpTo": uint64(4294963108),
				"enable":               true,
				"blend":                false,
				"m_bNoReflectionFog":   true,
			},
		},

		{
			tableName:   "CDOTASpectatorGraphManagerProxy",
			run:         true,
			debug:       false,
			expectCount: 412,
			expectKeys: map[string]interface{}{
				"CDOTASpectatorGraphManager.m_rgPlayerGraphData.0000": uint32(2097151),
				"CDOTASpectatorGraphManager.m_rgDireNetWorth.0063":    int32(0),
				"CDOTASpectatorGraphManager.m_nGoldGraphVersion":      int32(3),
			},
		},

		{
			tableName:   "CSpeechBubbleManager",
			run:         true,
			debug:       false,
			expectCount: 25, // WRONG, should carry the array index instead of overwriting. Needs some rework
			expectKeys: map[string]interface{}{
				"m_SpeechBubbles.0000.m_hNPC":       HANDLE_NONE,
				"m_SpeechBubbles.0000.m_flDuration": float32(0),
				"m_SpeechBubbles.0000.m_unOffsetX":  uint32(0),
				"m_SpeechBubbles.0000.m_unCount":    uint32(0),
			},
		},

		{
			tableName:   "CBaseEntity",
			run:         true,
			debug:       false,
			expectCount: 35,
			expectKeys: map[string]interface{}{
				"m_pEntity": true,
			},
		},

		{
			tableName:   "CBaseAnimating",
			run:         true,
			debug:       false,
			expectCount: 111,
			expectKeys: map[string]interface{}{
				"CRenderComponent":                      1,
				"CPhysicsComponent":                     1,
				"m_hEffectEntity":                       HANDLE_NONE,
				"CBodyComponentBaseAnimating.m_hParent": HANDLE_NONE,
				"m_hOwnerEntity":                        HANDLE_NONE,
			},
		},

		{
			tableName:   "CWorld",
			run:         true,
			debug:       false,
			expectCount: 139,
			expectKeys: map[string]interface{}{
				"m_iszOptimizedHeightFieldName": string(""),
				"m_hEffectEntity":               HANDLE_NONE,
				"m_pEntity":                     true,
			},
		},
	}

	// Load our send tables
	m := &dota.CDemoSendTables{}
	if err := proto.Unmarshal(_read_fixture("send_tables/1731962898.pbmsg"), m); err != nil {
		panic(err)
	}

	// Retrieve the flattened field serializer
	fs := ParseSendTables(m, GetDefaultPropertySerializerTable())

	// Iterate through scenarios
	for _, s := range scenarios {
		// Load up a fixture
		buf := _read_fixture(_sprintf("instancebaseline/1731962898_%s.rawbuf", s.tableName))

		serializer := fs.Serializers[s.tableName][0]
		assert.NotNil(serializer)

		// Optionally skip
		if !s.run {
			continue
		}

		// Optionally disable debugging
		debugMode = s.debug

		// Read properties
		r := NewReader(buf)
		props := ReadProperties(r, serializer)
		assert.Equal(len(props), s.expectCount)

		for k, v := range s.expectKeys {
			assert.EqualValues(v, props[k])
		}

		// There shouldn't be more than 8 bits left in the buffer
		_debugf("Remaining bits %v", r.remBits())
		assert.True(r.remBits() < 8)
	}
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"github.com/anacrolix/utp"
	"github.com/meshbird/meshbird/log"
	"github.com/meshbird/meshbird/network/protocol"
	"github.com/meshbird/meshbird/secure"
	"io"
	"net"
	"strconv"
	"time"
)

var (
	rnLoggerFormat = "remote %s"
)

type RemoteNode struct {
	Node
	conn          net.Conn
	sessionKey    []byte
	privateIP     net.IP
	publicAddress string
	logger        log.Logger
	lastHeartbeat time.Time
}

func NewRemoteNode(conn net.Conn, sessionKey []byte, privateIP net.IP) *RemoteNode {
	return &RemoteNode{
		conn:          conn,
		sessionKey:    sessionKey,
		privateIP:     privateIP,
		publicAddress: conn.RemoteAddr().String(),
		logger:        log.L(fmt.Sprintf(rnLoggerFormat, privateIP.String())),
		lastHeartbeat: time.Now(),
	}
}

func (rn *RemoteNode) SendToInterface(payload []byte) error {
	return protocol.WriteEncodeTransfer(rn.conn, payload)
}

func (rn *RemoteNode) SendPack(pack *protocol.Packet) (err error) {
	if err = protocol.EncodeAndWrite(rn.conn, pack); err != nil {
		err = fmt.Errorf("error on write transfer message, %v", err)
	}
	return
}

func (rn *RemoteNode) Close() {
	defer rn.conn.Close()
	rn.logger.Debug("closing...")
}

func (rn *RemoteNode) listen(ln *LocalNode) {
	defer rn.logger.Debug("listener stopped...")
	defer func() {
		ln.NetTable().RemoveRemoteNode(rn.privateIP)
	}()

	iface, ok := ln.Service("iface").(*InterfaceService)
	if !ok {
		rn.logger.Error("interface service not found")
		return
	}

	rn.logger.Debug("listening...")

	for {
		pack, err := protocol.Decode(rn.conn)
		if err != nil {
			rn.logger.Error("decode error, %v", err)
			if err == io.EOF {
				break
			}
			continue
		}
		rn.logger.Debug("received, %+v", pack)

		switch pack.Data.Type {
		case protocol.TypeTransfer:
			rn.logger.Debug("Writing to interface...")
			payloadEncrypted := pack.Data.Msg.(protocol.TransferMessage).Bytes()
			payload, errDec := secure.DecryptIV(payloadEncrypted, ln.State().Secret.Key, ln.State().Secret.Key)
			if errDec != nil {
				rn.logger.Error("error on decrypt, %v", err)
				break
			}
			srcAddr := payload[12:16].(net.IPAddr)
			dstAddr := payload[16:20].(net.IPAddr)
			rn.logger.Debug("received packet from %s to %s", srcAddr.String(), dstAddr.String())
			iface.WritePacket(payload)
		case protocol.TypeHeartbeat:
			rn.logger.Debug("heardbeat received, %v", pack.Data.Msg)
			rn.lastHeartbeat = time.Now()
		}
	}
}

func TryConnect(h string, networkSecret *secure.NetworkSecret, ln *LocalNode) (*RemoteNode, error) {
	host, portStr, errSplit := net.SplitHostPort(h)
	if errSplit != nil {
		return nil, errSplit
	}

	port, errConvert := strconv.Atoi(portStr)
	if errConvert != nil {
		return nil, errConvert
	}

	rn := new(RemoteNode)
	rn.lastHeartbeat = time.Now()
	rn.publicAddress = fmt.Sprintf("%s:%d", host, port + 1)

	rn.logger = log.L(fmt.Sprintf("public %s", rn.publicAddress))
	rn.logger.Debug("trying to connect...")

	s, errSocket := utp.NewSocket("udp4", ":0")
	if errSocket != nil {
		rn.logger.Error("unable to crete a socket, %v", errSocket)
		return nil, errSocket
	}

	conn, errDial := s.DialTimeout(rn.publicAddress, 10 * time.Second)
	if errDial != nil {
		rn.logger.Error("unable to dial, %v", errDial)
		return nil, errDial
	}

	rn.conn = conn
	rn.sessionKey = RandomBytes(16)

	if err := protocol.WriteEncodeHandshake(rn.conn, rn.sessionKey, networkSecret); err != nil {
		return nil, err
	}
	if _, okError := protocol.ReadDecodeOk(rn.conn); okError != nil {
		return nil, okError
	}

	peerInfo, errPeerInfo := protocol.ReadDecodePeerInfo(rn.conn)
	if errPeerInfo != nil {
		return nil, errPeerInfo
	}

	rn.privateIP = peerInfo.PrivateIP()

	// create new logger
	log.RemoveLogger(rn.logger.Name())
	rn.logger = log.L(fmt.Sprintf(rnLoggerFormat, rn.privateIP.String()))

	if err := protocol.WriteEncodePeerInfo(rn.conn, ln.State().PrivateIP); err != nil {
		return nil, err
	}

	rn.logger.Info("connected, with public address %q", rn.publicAddress)
	return rn, nil
}
<commit_msg>fix cast Add logs of source and destination when writing to interface<commit_after>package common

import (
	"fmt"
	"github.com/anacrolix/utp"
	"github.com/meshbird/meshbird/log"
	"github.com/meshbird/meshbird/network/protocol"
	"github.com/meshbird/meshbird/secure"
	"io"
	"net"
	"strconv"
	"time"
)

var (
	rnLoggerFormat = "remote %s"
)

type RemoteNode struct {
	Node
	conn          net.Conn
	sessionKey    []byte
	privateIP     net.IP
	publicAddress string
	logger        log.Logger
	lastHeartbeat time.Time
}

func NewRemoteNode(conn net.Conn, sessionKey []byte, privateIP net.IP) *RemoteNode {
	return &RemoteNode{
		conn:          conn,
		sessionKey:    sessionKey,
		privateIP:     privateIP,
		publicAddress: conn.RemoteAddr().String(),
		logger:        log.L(fmt.Sprintf(rnLoggerFormat, privateIP.String())),
		lastHeartbeat: time.Now(),
	}
}

func (rn *RemoteNode) SendToInterface(payload []byte) error {
	return protocol.WriteEncodeTransfer(rn.conn, payload)
}

func (rn *RemoteNode) SendPack(pack *protocol.Packet) (err error) {
	if err = protocol.EncodeAndWrite(rn.conn, pack); err != nil {
		err = fmt.Errorf("error on write transfer message, %v", err)
	}
	return
}

func (rn *RemoteNode) Close() {
	defer rn.conn.Close()
	rn.logger.Debug("closing...")
}

func (rn *RemoteNode) listen(ln *LocalNode) {
	defer rn.logger.Debug("listener stopped...")
	defer func() {
		ln.NetTable().RemoveRemoteNode(rn.privateIP)
	}()

	iface, ok := ln.Service("iface").(*InterfaceService)
	if !ok {
		rn.logger.Error("interface service not found")
		return
	}

	rn.logger.Debug("listening...")

	for {
		pack, err := protocol.Decode(rn.conn)
		if err != nil {
			rn.logger.Error("decode error, %v", err)
			if err == io.EOF {
				break
			}
			continue
		}
		rn.logger.Debug("received, %+v", pack)

		switch pack.Data.Type {
		case protocol.TypeTransfer:
			rn.logger.Debug("Writing to interface...")
			payloadEncrypted := pack.Data.Msg.(protocol.TransferMessage).Bytes()
			payload, errDec := secure.DecryptIV(payloadEncrypted, ln.State().Secret.Key, ln.State().Secret.Key)
			if errDec != nil {
				rn.logger.Error("error on decrypt, %v", err)
				break
			}
			srcAddr := net.IPAddr(payload[12:16])
			dstAddr := net.IPAddr(payload[16:20])
			rn.logger.Debug("received packet from %s to %s", srcAddr.String(), dstAddr.String())
			iface.WritePacket(payload)
		case protocol.TypeHeartbeat:
			rn.logger.Debug("heardbeat received, %v", pack.Data.Msg)
			rn.lastHeartbeat = time.Now()
		}
	}
}

func TryConnect(h string, networkSecret *secure.NetworkSecret, ln *LocalNode) (*RemoteNode, error) {
	host, portStr, errSplit := net.SplitHostPort(h)
	if errSplit != nil {
		return nil, errSplit
	}

	port, errConvert := strconv.Atoi(portStr)
	if errConvert != nil {
		return nil, errConvert
	}

	rn := new(RemoteNode)
	rn.lastHeartbeat = time.Now()
	rn.publicAddress = fmt.Sprintf("%s:%d", host, port + 1)

	rn.logger = log.L(fmt.Sprintf("public %s", rn.publicAddress))
	rn.logger.Debug("trying to connect...")

	s, errSocket := utp.NewSocket("udp4", ":0")
	if errSocket != nil {
		rn.logger.Error("unable to crete a socket, %v", errSocket)
		return nil, errSocket
	}

	conn, errDial := s.DialTimeout(rn.publicAddress, 10 * time.Second)
	if errDial != nil {
		rn.logger.Error("unable to dial, %v", errDial)
		return nil, errDial
	}

	rn.conn = conn
	rn.sessionKey = RandomBytes(16)

	if err := protocol.WriteEncodeHandshake(rn.conn, rn.sessionKey, networkSecret); err != nil {
		return nil, err
	}
	if _, okError := protocol.ReadDecodeOk(rn.conn); okError != nil {
		return nil, okError
	}

	peerInfo, errPeerInfo := protocol.ReadDecodePeerInfo(rn.conn)
	if errPeerInfo != nil {
		return nil, errPeerInfo
	}

	rn.privateIP = peerInfo.PrivateIP()

	// create new logger
	log.RemoveLogger(rn.logger.Name())
	rn.logger = log.L(fmt.Sprintf(rnLoggerFormat, rn.privateIP.String()))

	if err := protocol.WriteEncodePeerInfo(rn.conn, ln.State().PrivateIP); err != nil {
		return nil, err
	}

	rn.logger.Info("connected, with public address %q", rn.publicAddress)
	return rn, nil
}
<|endoftext|>


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

package pool

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/control-center/serviced/commons/queue"
)

// Item wrapper type for items stored in pool
type Item struct {
	Item         interface{}
	checkedOut   bool
	checkoutTime time.Time
	id           uint64
}

// ErrItemUnavailable Error when an item cannot be allocated fromt the pool
var ErrItemUnavailable = errors.New("Pool Item not avaialable")

// Pool interface for pooling arbitrary objects
type Pool interface {
	// Borrow item from pool, returns ErrItemUnavailable if none available
	Borrow() (*Item, error)

	// Borrow item from pool, wait timeout. If timeout < 0 wait indefinitely. Returns ErrItemUnavailable if object not available
	BorrowWait(timeout time.Duration) (*Item, error)

	// Return item back to pool
	Return(item *Item) error

	// Remove removes the object from the pool
	Remove(item *Item) error

	//Returns the current number of items borrowed
	Borrowed() int

	//Returns the current number of items in the pool (not borrowed)
	Idle() int
}

// ItemFactory function to create an item for the pool
type ItemFactory func() (interface{}, error)

// NewPool create a pool with a capacity and factory for creating items.
func NewPool(capacity int, itemFactory ItemFactory) (Pool, error) {

	q, err := queue.NewChannelQueue(capacity)
	if err != nil {
		return nil, err
	}

	itemMap := make(map[uint64]*Item)
	pool := itemPool{itemMap: itemMap, itemQ: q, capacity: capacity, itemFactory: itemFactory}
	return &pool, nil
}

type itemPool struct {
	itemMap map[uint64]*Item
	itemQ   queue.Queue

	idCounter   uint64
	capacity    int
	itemFactory ItemFactory
	poolLock    sync.RWMutex
}

func (p *itemPool) BorrowWait(timeout time.Duration) (*Item, error) {
	var item *Item
	var err error
	itemOrError := true

	// function for locking purposes
	func() {
		p.poolLock.Lock()
		defer p.poolLock.Unlock()
		qItem, found := p.itemQ.Poll()
		if found {
			item = p.checkout(qItem)
			return
		}
		if !found {
			newItem, err := p.newItem()
			if err != nil && err != ErrItemUnavailable {
				return
			} else if err == nil {
				item = p.checkout(newItem)
				return
			}
		}
		itemOrError = false
	}()

	if itemOrError {
		return item, err
	}
	var qItem interface{}
	itemChan, timeoutChan := p.itemQ.TakeChan(timeout)
	select {
	case qItem = <-itemChan:
		return p.checkout(qItem), nil
	case <-timeoutChan:
		return nil, ErrItemUnavailable
	}
}

func (p *itemPool) Borrow() (*Item, error) {
	return p.BorrowWait(0)
}

func (p *itemPool) Return(item *Item) error {
	err := func() error {
		p.poolLock.RLock()
		defer p.poolLock.RUnlock()
		if pooledItem, found := p.itemMap[item.id]; !found {
			return fmt.Errorf("Pool Return error, item not found")
		} else if pooledItem != item { //check same object (pointer compare)
			return fmt.Errorf("Pool Return error, item not in pool")
		} else if !item.checkedOut {
			return fmt.Errorf("Pool Return error, item not checked out")
		}
		item.checkedOut = false
		return nil
	}()
	if err != nil {
		return err
	}

	if !p.itemQ.Offer(item) {
		return p.Remove(item)
	}
	return nil
}

func (p *itemPool) Remove(item *Item) error {
	p.poolLock.Lock()
	defer p.poolLock.Unlock()

	if pooledItem, found := p.itemMap[item.id]; !found {
		return fmt.Errorf("Pool Remove error, item not found")
	} else if pooledItem != item { //check same object (pointer compare)
		return fmt.Errorf("Pool Remove error, item not in pool")
	} else if !item.checkedOut {
		return fmt.Errorf("Pool Remove error, item not checked out")
	}

	delete(p.itemMap, item.id)
	item, err := p.newItem()
	if err == nil {
		p.itemQ.Offer(item)
	} else {
		fmt.Printf("Remove error %v\n", err)
	}
	return nil
}

//Returns the current number of items borrowed
func (p *itemPool) Borrowed() int {
	p.poolLock.RLock()
	defer p.poolLock.RUnlock()
	count := 0
	for _, item := range p.itemMap {
		if item.checkedOut {
			count++
		}
	}
	return count
}

// Idle Returns the current number of items in the pool (not borrowed)
func (p *itemPool) Idle() int {
	p.poolLock.RLock()
	defer p.poolLock.RUnlock()
	count := 0
	for _, item := range p.itemMap {
		if !item.checkedOut {
			count++
		}
	}
	return count
}

func (p *itemPool) checkout(item interface{}) *Item {
	poolItem := item.(*Item)
	poolItem.checkedOut = true
	poolItem.checkoutTime = time.Now()
	return poolItem
}

// creates a new Item if it can
func (p *itemPool) newItem() (*Item, error) {
	//	p.poolLock.Lock()
	//	defer p.poolLock.Unlock()
	if len(p.itemMap) >= p.capacity {
		return nil, ErrItemUnavailable
	}
	i, err := p.itemFactory()
	if err != nil {
		return nil, err
	}

	pItem := &Item{id: p.nextID(), Item: i}
	p.itemMap[pItem.id] = pItem
	return pItem, nil
}

func (p *itemPool) nextID() uint64 {
	return atomic.AddUint64(&p.idCounter, 1)
}
<commit_msg>remove commented out code<commit_after>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.package rpcutils

package pool

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/control-center/serviced/commons/queue"
)

// Item wrapper type for items stored in pool
type Item struct {
	Item         interface{}
	checkedOut   bool
	checkoutTime time.Time
	id           uint64
}

// ErrItemUnavailable Error when an item cannot be allocated fromt the pool
var ErrItemUnavailable = errors.New("Pool Item not avaialable")

// Pool interface for pooling arbitrary objects
type Pool interface {
	// Borrow item from pool, returns ErrItemUnavailable if none available
	Borrow() (*Item, error)

	// Borrow item from pool, wait timeout. If timeout < 0 wait indefinitely. Returns ErrItemUnavailable if object not available
	BorrowWait(timeout time.Duration) (*Item, error)

	// Return item back to pool
	Return(item *Item) error

	// Remove removes the object from the pool
	Remove(item *Item) error

	//Returns the current number of items borrowed
	Borrowed() int

	//Returns the current number of items in the pool (not borrowed)
	Idle() int
}

// ItemFactory function to create an item for the pool
type ItemFactory func() (interface{}, error)

// NewPool create a pool with a capacity and factory for creating items.
func NewPool(capacity int, itemFactory ItemFactory) (Pool, error) {

	q, err := queue.NewChannelQueue(capacity)
	if err != nil {
		return nil, err
	}

	itemMap := make(map[uint64]*Item)
	pool := itemPool{itemMap: itemMap, itemQ: q, capacity: capacity, itemFactory: itemFactory}
	return &pool, nil
}

type itemPool struct {
	itemMap map[uint64]*Item
	itemQ   queue.Queue

	idCounter   uint64
	capacity    int
	itemFactory ItemFactory
	poolLock    sync.RWMutex
}

func (p *itemPool) BorrowWait(timeout time.Duration) (*Item, error) {
	var item *Item
	var err error
	itemOrError := true

	// function for locking purposes
	func() {
		p.poolLock.Lock()
		defer p.poolLock.Unlock()
		qItem, found := p.itemQ.Poll()
		if found {
			item = p.checkout(qItem)
			return
		}
		if !found {
			newItem, err := p.newItem()
			if err != nil && err != ErrItemUnavailable {
				return
			} else if err == nil {
				item = p.checkout(newItem)
				return
			}
		}
		itemOrError = false
	}()

	if itemOrError {
		return item, err
	}
	var qItem interface{}
	itemChan, timeoutChan := p.itemQ.TakeChan(timeout)
	select {
	case qItem = <-itemChan:
		return p.checkout(qItem), nil
	case <-timeoutChan:
		return nil, ErrItemUnavailable
	}
}

func (p *itemPool) Borrow() (*Item, error) {
	return p.BorrowWait(0)
}

func (p *itemPool) Return(item *Item) error {
	err := func() error {
		p.poolLock.RLock()
		defer p.poolLock.RUnlock()
		if pooledItem, found := p.itemMap[item.id]; !found {
			return fmt.Errorf("Pool Return error, item not found")
		} else if pooledItem != item { //check same object (pointer compare)
			return fmt.Errorf("Pool Return error, item not in pool")
		} else if !item.checkedOut {
			return fmt.Errorf("Pool Return error, item not checked out")
		}
		item.checkedOut = false
		return nil
	}()
	if err != nil {
		return err
	}

	if !p.itemQ.Offer(item) {
		return p.Remove(item)
	}
	return nil
}

func (p *itemPool) Remove(item *Item) error {
	p.poolLock.Lock()
	defer p.poolLock.Unlock()

	if pooledItem, found := p.itemMap[item.id]; !found {
		return fmt.Errorf("Pool Remove error, item not found")
	} else if pooledItem != item { //check same object (pointer compare)
		return fmt.Errorf("Pool Remove error, item not in pool")
	} else if !item.checkedOut {
		return fmt.Errorf("Pool Remove error, item not checked out")
	}

	delete(p.itemMap, item.id)
	item, err := p.newItem()
	if err == nil {
		p.itemQ.Offer(item)
	} else {
		fmt.Printf("Remove error %v\n", err)
	}
	return nil
}

//Returns the current number of items borrowed
func (p *itemPool) Borrowed() int {
	p.poolLock.RLock()
	defer p.poolLock.RUnlock()
	count := 0
	for _, item := range p.itemMap {
		if item.checkedOut {
			count++
		}
	}
	return count
}

// Idle Returns the current number of items in the pool (not borrowed)
func (p *itemPool) Idle() int {
	p.poolLock.RLock()
	defer p.poolLock.RUnlock()
	count := 0
	for _, item := range p.itemMap {
		if !item.checkedOut {
			count++
		}
	}
	return count
}

func (p *itemPool) checkout(item interface{}) *Item {
	poolItem := item.(*Item)
	poolItem.checkedOut = true
	poolItem.checkoutTime = time.Now()
	return poolItem
}

// creates a new Item if it can
func (p *itemPool) newItem() (*Item, error) {
	if len(p.itemMap) >= p.capacity {
		return nil, ErrItemUnavailable
	}
	i, err := p.itemFactory()
	if err != nil {
		return nil, err
	}

	pItem := &Item{id: p.nextID(), Item: i}
	p.itemMap[pItem.id] = pItem
	return pItem, nil
}

func (p *itemPool) nextID() uint64 {
	return atomic.AddUint64(&p.idCounter, 1)
}
<|endoftext|>