<commit_before>/*
Copyright 2018 Google LLC

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 deploy

import (
	"bytes"
	"context"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
	"gopkg.in/yaml.v2"
)

// Slightly modified from kubectl run --dry-run
var deploymentTemplate = template.Must(template.New("deployment").Parse(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    run: skaffold
  name: skaffold
spec:
  replicas: 1
  selector:
    matchLabels:
      run: skaffold
  strategy: {}
  template:
    metadata:
      labels:
        run: skaffold
    spec:
      containers:
      - image: {{ .Image }}
        name: app
{{if .Ports}}
        ports:
{{range .Ports}}
        - containerPort: {{.}}
{{end}}
{{end}}
`))

// KubectlDeployer deploys workflows using kubectl CLI.
type KubectlDeployer struct {
	*v1alpha2.DeployConfig
	kubeContext string
}

// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled
// with the needed configuration for `kubectl apply`
func NewKubectlDeployer(cfg *v1alpha2.DeployConfig, kubeContext string) *KubectlDeployer {
	return &KubectlDeployer{
		DeployConfig: cfg,
		kubeContext:  kubeContext,
	}
}

// Deploy templates the provided manifests with a simple `find and replace` and
// runs `kubectl apply` on those manifests
func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, b *build.BuildResult) (*Result, error) {
	manifests, err := k.readOrGenerateManifests(b)
	if err != nil {
		return nil, errors.Wrap(err, "reading manifests")
	}

	manifests, err = manifests.replaceImages(b.Builds)
	if err != nil {
		return nil, errors.Wrap(err, "replacing images in manifests")
	}

	err = k.kubectl(manifests.reader(), out, "apply", "-f", "-")
	if err != nil {
		return nil, errors.Wrap(err, "deploying manifests")
	}

	return &Result{}, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	if len(k.KubectlDeploy.Manifests) == 0 {
		return k.kubectl(nil, out, "delete", "deployment", "skaffold")
	}

	manifests, err := k.readManifests()
	if err != nil {
		return errors.Wrap(err, "reading manifests")
	}

	err = k.kubectl(manifests.reader(), out, "delete", "-f", "-")
	if err != nil {
		return errors.Wrap(err, "deleting manifests")
	}

	return nil
}

// Not implemented
func (k *KubectlDeployer) Dependencies() ([]string, error) {
	return manifestFiles(k.KubectlDeploy.Manifests)
}

// readOrGenerateManifests reads the manifests to deploy/delete. If no manifest exists, try to
// generate it with the information we have.
func (k *KubectlDeployer) readOrGenerateManifests(b *build.BuildResult) (manifestList, error) {
	if len(k.KubectlDeploy.Manifests) > 0 {
		return k.readManifests()
	}

	if len(b.Builds) != 1 {
		return nil, errors.New("must specify manifest if using more than one image")
	}

	yaml, err := generateManifest(b.Builds[0])
	if err != nil {
		return nil, errors.Wrap(err, "generating manifest")
	}

	return manifestList{yaml}, nil
}

func (k *KubectlDeployer) kubectl(in io.Reader, out io.Writer, arg ...string) error {
	args := append([]string{"--context", k.kubeContext}, arg...)

	cmd := exec.Command("kubectl", args...)
	cmd.Stdin = in
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func manifestFiles(manifests []string) ([]string, error) {
	list, err := util.ExpandPathsGlob(manifests)
	if err != nil {
		return nil, errors.Wrap(err, "expanding kubectl manifest paths")
	}

	var filteredManifests []string
	for _, f := range list {
		if !util.IsSupportedKubernetesFormat(f) {
			if !util.StrSliceContains(manifests, f) {
				logrus.Infof("refusing to deploy/delete non {json, yaml} file %s", f)
				logrus.Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.")
				continue
			}
		}
		filteredManifests = append(filteredManifests, f)
	}

	return filteredManifests, nil
}

// readManifests reads the manifests to deploy/delete.
func (k *KubectlDeployer) readManifests() (manifestList, error) {
	files, err := manifestFiles(k.KubectlDeploy.Manifests)
	if err != nil {
		return nil, errors.Wrap(err, "expanding user manifest list")
	}
	var manifests manifestList

	for _, manifest := range files {
		buf, err := afero.ReadFile(util.Fs, manifest)
		if err != nil {
			return nil, errors.Wrap(err, "reading manifest")
		}

		parts := bytes.Split(buf, []byte("\n---"))
		for _, part := range parts {
			manifests = append(manifests, part)
		}
	}

	for _, m := range k.KubectlDeploy.RemoteManifests {
		manifest, err := k.readRemoteManifest(m)
		if err != nil {
			return nil, errors.Wrap(err, "get remote manifests")
		}

		manifests = append(manifests, manifest)
	}

	logrus.Debugln("manifests", manifests.String())

	return manifests, nil
}

func (k *KubectlDeployer) readRemoteManifest(name string) ([]byte, error) {
	var args []string
	if parts := strings.Split(name, ":"); len(parts) > 1 {
		args = append(args, "--namespace", parts[0])
		name = parts[1]
	}
	args = append(args, "get", name, "-o", "yaml")

	var manifest bytes.Buffer
	err := k.kubectl(nil, &manifest, args...)
	if err != nil {
		return nil, errors.Wrap(err, "getting manifest")
	}

	return manifest.Bytes(), nil
}

func generateManifest(b build.Build) ([]byte, error) {
	logrus.Info("No manifests specified. Generating a deployment.")

	dockerfilePath := filepath.Join(b.Artifact.Workspace, b.Artifact.DockerArtifact.DockerfilePath)
	r, err := os.Open(dockerfilePath)
	if err != nil {
		return nil, errors.Wrap(err, "reading dockerfile")
	}

	ports, err := docker.PortsFromDockerfile(r)
	if err != nil {
		logrus.Warnf("Unable to determine port from Dockerfile: %s.", err)
	}

	var out bytes.Buffer
	if err := deploymentTemplate.Execute(&out, struct {
		Ports []string
		Image string
	}{
		Ports: ports,
		Image: b.ImageName,
	}); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

type replacement struct {
	tag   string
	found bool
}

type manifestList [][]byte

func (l *manifestList) String() string {
	var str string
	for i, manifest := range *l {
		if i != 0 {
			str += "\n---\n"
		}
		str += string(bytes.TrimSpace(manifest))
	}
	return str
}

func (l *manifestList) reader() io.Reader {
	return strings.NewReader(l.String())
}

func (l *manifestList) replaceImages(b []build.Build) (manifestList, error) {
	replacements := map[string]*replacement{}
	for _, build := range b {
		replacements[build.ImageName] = &replacement{
			tag: build.Tag,
		}
	}

	var updatedManifests manifestList

	for _, manifest := range *l {
		m := make(map[interface{}]interface{})
		if err := yaml.Unmarshal(manifest, &m); err != nil {
			return nil, errors.Wrap(err, "reading kubernetes YAML")
		}

		if len(m) == 0 {
			continue
		}

		recursiveReplaceImage(m, replacements)

		updatedManifest, err := yaml.Marshal(m)
		if err != nil {
			return nil, errors.Wrap(err, "marshalling yaml")
		}

		updatedManifests = append(updatedManifests, updatedManifest)
	}

	for name, replacement := range replacements {
		if !replacement.found {
			logrus.Warnf("image [%s] is not used by the deployment", name)
		}
	}

	logrus.Debugln("manifests with tagged images", updatedManifests.String())

	return updatedManifests, nil
}

func recursiveReplaceImage(i interface{}, replacements map[string]*replacement) {
	switch t := i.(type) {
	case []interface{}:
		for _, v := range t {
			recursiveReplaceImage(v, replacements)
		}
	case map[interface{}]interface{}:
		for k, v := range t {
			if k.(string) == "image" {
				name, tag := splitTag(v.(string))
				if tag == "" || tag == "latest" {
					if img, present := replacements[name]; present {
						t[k] = img.tag
						img.found = true
					}
				}
			} else {
				recursiveReplaceImage(v, replacements)
			}
		}
	}
}

func splitTag(image string) (string, string) {
	re := regexp.MustCompile(`(.*):([^/]+)$`)
	matches := re.FindStringSubmatch(image)

	if len(matches) == 3 {
		return matches[1], matches[2]
	}
	return image, ""
}
<commit_msg>Adding a log for images that are not replaced<commit_after>/*
Copyright 2018 Google LLC

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 deploy

import (
	"bytes"
	"context"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
	"gopkg.in/yaml.v2"
)

// Slightly modified from kubectl run --dry-run
var deploymentTemplate = template.Must(template.New("deployment").Parse(`apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    run: skaffold
  name: skaffold
spec:
  replicas: 1
  selector:
    matchLabels:
      run: skaffold
  strategy: {}
  template:
    metadata:
      labels:
        run: skaffold
    spec:
      containers:
      - image: {{ .Image }}
        name: app
{{if .Ports}}
        ports:
{{range .Ports}}
        - containerPort: {{.}}
{{end}}
{{end}}
`))

// KubectlDeployer deploys workflows using kubectl CLI.
type KubectlDeployer struct {
	*v1alpha2.DeployConfig
	kubeContext string
}

// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled
// with the needed configuration for `kubectl apply`
func NewKubectlDeployer(cfg *v1alpha2.DeployConfig, kubeContext string) *KubectlDeployer {
	return &KubectlDeployer{
		DeployConfig: cfg,
		kubeContext:  kubeContext,
	}
}

// Deploy templates the provided manifests with a simple `find and replace` and
// runs `kubectl apply` on those manifests
func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, b *build.BuildResult) (*Result, error) {
	manifests, err := k.readOrGenerateManifests(b)
	if err != nil {
		return nil, errors.Wrap(err, "reading manifests")
	}

	manifests, err = manifests.replaceImages(b.Builds)
	if err != nil {
		return nil, errors.Wrap(err, "replacing images in manifests")
	}

	err = k.kubectl(manifests.reader(), out, "apply", "-f", "-")
	if err != nil {
		return nil, errors.Wrap(err, "deploying manifests")
	}

	return &Result{}, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	if len(k.KubectlDeploy.Manifests) == 0 {
		return k.kubectl(nil, out, "delete", "deployment", "skaffold")
	}

	manifests, err := k.readManifests()
	if err != nil {
		return errors.Wrap(err, "reading manifests")
	}

	err = k.kubectl(manifests.reader(), out, "delete", "-f", "-")
	if err != nil {
		return errors.Wrap(err, "deleting manifests")
	}

	return nil
}

// Not implemented
func (k *KubectlDeployer) Dependencies() ([]string, error) {
	return manifestFiles(k.KubectlDeploy.Manifests)
}

// readOrGenerateManifests reads the manifests to deploy/delete. If no manifest exists, try to
// generate it with the information we have.
func (k *KubectlDeployer) readOrGenerateManifests(b *build.BuildResult) (manifestList, error) {
	if len(k.KubectlDeploy.Manifests) > 0 {
		return k.readManifests()
	}

	if len(b.Builds) != 1 {
		return nil, errors.New("must specify manifest if using more than one image")
	}

	yaml, err := generateManifest(b.Builds[0])
	if err != nil {
		return nil, errors.Wrap(err, "generating manifest")
	}

	return manifestList{yaml}, nil
}

func (k *KubectlDeployer) kubectl(in io.Reader, out io.Writer, arg ...string) error {
	args := append([]string{"--context", k.kubeContext}, arg...)

	cmd := exec.Command("kubectl", args...)
	cmd.Stdin = in
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func manifestFiles(manifests []string) ([]string, error) {
	list, err := util.ExpandPathsGlob(manifests)
	if err != nil {
		return nil, errors.Wrap(err, "expanding kubectl manifest paths")
	}

	var filteredManifests []string
	for _, f := range list {
		if !util.IsSupportedKubernetesFormat(f) {
			if !util.StrSliceContains(manifests, f) {
				logrus.Infof("refusing to deploy/delete non {json, yaml} file %s", f)
				logrus.Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.")
				continue
			}
		}
		filteredManifests = append(filteredManifests, f)
	}

	return filteredManifests, nil
}

// readManifests reads the manifests to deploy/delete.
func (k *KubectlDeployer) readManifests() (manifestList, error) {
	files, err := manifestFiles(k.KubectlDeploy.Manifests)
	if err != nil {
		return nil, errors.Wrap(err, "expanding user manifest list")
	}
	var manifests manifestList

	for _, manifest := range files {
		buf, err := afero.ReadFile(util.Fs, manifest)
		if err != nil {
			return nil, errors.Wrap(err, "reading manifest")
		}

		parts := bytes.Split(buf, []byte("\n---"))
		for _, part := range parts {
			manifests = append(manifests, part)
		}
	}

	for _, m := range k.KubectlDeploy.RemoteManifests {
		manifest, err := k.readRemoteManifest(m)
		if err != nil {
			return nil, errors.Wrap(err, "get remote manifests")
		}

		manifests = append(manifests, manifest)
	}

	logrus.Debugln("manifests", manifests.String())

	return manifests, nil
}

func (k *KubectlDeployer) readRemoteManifest(name string) ([]byte, error) {
	var args []string
	if parts := strings.Split(name, ":"); len(parts) > 1 {
		args = append(args, "--namespace", parts[0])
		name = parts[1]
	}
	args = append(args, "get", name, "-o", "yaml")

	var manifest bytes.Buffer
	err := k.kubectl(nil, &manifest, args...)
	if err != nil {
		return nil, errors.Wrap(err, "getting manifest")
	}

	return manifest.Bytes(), nil
}

func generateManifest(b build.Build) ([]byte, error) {
	logrus.Info("No manifests specified. Generating a deployment.")

	dockerfilePath := filepath.Join(b.Artifact.Workspace, b.Artifact.DockerArtifact.DockerfilePath)
	r, err := os.Open(dockerfilePath)
	if err != nil {
		return nil, errors.Wrap(err, "reading dockerfile")
	}

	ports, err := docker.PortsFromDockerfile(r)
	if err != nil {
		logrus.Warnf("Unable to determine port from Dockerfile: %s.", err)
	}

	var out bytes.Buffer
	if err := deploymentTemplate.Execute(&out, struct {
		Ports []string
		Image string
	}{
		Ports: ports,
		Image: b.ImageName,
	}); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

type replacement struct {
	tag   string
	found bool
}

type manifestList [][]byte

func (l *manifestList) String() string {
	var str string
	for i, manifest := range *l {
		if i != 0 {
			str += "\n---\n"
		}
		str += string(bytes.TrimSpace(manifest))
	}
	return str
}

func (l *manifestList) reader() io.Reader {
	return strings.NewReader(l.String())
}

func (l *manifestList) replaceImages(b []build.Build) (manifestList, error) {
	replacements := map[string]*replacement{}
	for _, build := range b {
		replacements[build.ImageName] = &replacement{
			tag: build.Tag,
		}
	}

	var updatedManifests manifestList

	for _, manifest := range *l {
		m := make(map[interface{}]interface{})
		if err := yaml.Unmarshal(manifest, &m); err != nil {
			return nil, errors.Wrap(err, "reading kubernetes YAML")
		}

		if len(m) == 0 {
			continue
		}

		recursiveReplaceImage(m, replacements)

		updatedManifest, err := yaml.Marshal(m)
		if err != nil {
			return nil, errors.Wrap(err, "marshalling yaml")
		}

		updatedManifests = append(updatedManifests, updatedManifest)
	}

	for name, replacement := range replacements {
		if !replacement.found {
			logrus.Warnf("image [%s] is not used by the deployment", name)
		}
	}

	logrus.Debugln("manifests with tagged images", updatedManifests.String())

	return updatedManifests, nil
}

func recursiveReplaceImage(i interface{}, replacements map[string]*replacement) {
	switch t := i.(type) {
	case []interface{}:
		for _, v := range t {
			recursiveReplaceImage(v, replacements)
		}
	case map[interface{}]interface{}:
		for k, v := range t {
			if k.(string) == "image" {
				name, tag := splitTag(v.(string))
				if img, present := replacements[name]; present {
					if tag == "" || tag == "latest" {
						t[k] = img.tag
						img.found = true
					} else {
						// TODO(1.0.0): Remove this warning.
						logrus.Infof("Not replacing fully qualified image: %s (see #565)", v)
					}
				}
			} else {
				recursiveReplaceImage(v, replacements)
			}
		}
	}
}

func splitTag(image string) (string, string) {
	re := regexp.MustCompile(`(.*):([^/]+)$`)
	matches := re.FindStringSubmatch(image)

	if len(matches) == 3 {
		return matches[1], matches[2]
	}
	return image, ""
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 deploy

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"strings"

	"github.com/segmentio/textio"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

// KubectlDeployer deploys workflows using kubectl CLI.
type KubectlDeployer struct {
	*latest.KubectlDeploy

	originalImages     []build.Artifact
	workingDir         string
	kubectl            deploy.CLI
	insecureRegistries map[string]bool
	//todo:option-1
	deployOnly bool
}

// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled
// with the needed configuration for `kubectl apply`
func NewKubectlDeployer(runCtx *runcontext.RunContext) *KubectlDeployer {
	return &KubectlDeployer{
		KubectlDeploy: runCtx.Cfg.Deploy.KubectlDeploy,
		workingDir:    runCtx.WorkingDir,
		kubectl: deploy.CLI{
			CLI:         kubectl.NewFromRunContext(runCtx),
			Flags:       runCtx.Cfg.Deploy.KubectlDeploy.Flags,
			ForceDeploy: runCtx.Opts.Force,
		},
		insecureRegistries: runCtx.InsecureRegistries,
		//todo:option-1
		deployOnly: runCtx.Opts.DeployOnly,
	}
}

func (k *KubectlDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "kubectl",
	}
}

// Deploy templates the provided manifests with a simple `find and replace` and
// runs `kubectl apply` on those manifests
func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller) *Result {
	event.DeployInProgress()
	//todo:option-1
	////
	var manifests deploy.ManifestList
	var err error
	if k.deployOnly {
		manifests, err = k.readManifests(ctx)
	} else {
		manifests, err = k.renderManifests(ctx, out, builds, labellers)
	}
	/////
	if err != nil {
		event.DeployFailed(err)
		return NewDeployErrorResult(err)
	}

	if len(manifests) == 0 {
		event.DeployComplete()
		return NewDeploySuccessResult(nil)
	}

	namespaces, err := manifests.CollectNamespaces()
	if err != nil {
		event.DeployInfoEvent(fmt.Errorf("could not fetch deployed resource namespace. "+
			"This might cause port-forward and deploy health-check to fail: %w", err))
	}

	if err := k.kubectl.Apply(ctx, textio.NewPrefixWriter(out, " - "), manifests); err != nil {
		event.DeployFailed(err)
		return NewDeployErrorResult(fmt.Errorf("kubectl error: %w", err))
	}

	event.DeployComplete()
	return NewDeploySuccessResult(namespaces)
}

func (k *KubectlDeployer) manifestFiles(manifests []string) ([]string, error) {
	var nonURLManifests []string
	for _, manifest := range manifests {
		if !util.IsURL(manifest) {
			nonURLManifests = append(nonURLManifests, manifest)
		}
	}

	list, err := util.ExpandPathsGlob(k.workingDir, nonURLManifests)
	if err != nil {
		return nil, fmt.Errorf("expanding kubectl manifest paths: %w", err)
	}

	var filteredManifests []string
	for _, f := range list {
		if !kubernetes.HasKubernetesFileExtension(f) {
			if !util.StrSliceContains(manifests, f) {
				logrus.Infof("refusing to deploy/delete non {json, yaml} file %s", f)
				logrus.Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.")
				continue
			}
		}
		filteredManifests = append(filteredManifests, f)
	}

	return filteredManifests, nil
}

// readManifests reads the manifests to deploy/delete.
func (k *KubectlDeployer) readManifests(ctx context.Context) (deploy.ManifestList, error) {
	// Get file manifests
	manifests, err := k.Dependencies()
	if err != nil {
		return nil, fmt.Errorf("listing manifests: %w", err)
	}

	// Append URL manifests
	for _, manifest := range k.KubectlDeploy.Manifests {
		if util.IsURL(manifest) {
			manifests = append(manifests, manifest)
		}
	}

	if len(manifests) == 0 {
		return deploy.ManifestList{}, nil
	}

	return k.kubectl.ReadManifests(ctx, manifests)
}

// readRemoteManifests will try to read manifests from the given kubernetes
// context in the specified namespace and for the specified type
func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) {
	var args []string
	ns := ""
	if parts := strings.Split(name, ":"); len(parts) > 1 {
		ns = parts[0]
		name = parts[1]
	}
	args = append(args, name, "-o", "yaml")

	var manifest bytes.Buffer
	err := k.kubectl.RunInNamespace(ctx, nil, &manifest, "get", ns, args...)
	if err != nil {
		return nil, fmt.Errorf("getting manifest: %w", err)
	}

	return manifest.Bytes(), nil
}

func (k *KubectlDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller, filepath string) error {
	manifests, err := k.renderManifests(ctx, out, builds, labellers)
	if err != nil {
		return err
	}

	return outputRenderedManifests(manifests.String(), filepath, out)
}

func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller) (deploy.ManifestList, error) {
	if err := k.kubectl.CheckVersion(ctx); err != nil {
		color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx))
		color.Default.Fprintln(out, err)
	}

	manifests, err := k.readManifests(ctx)
	if err != nil {
		return nil, fmt.Errorf("reading manifests: %w", err)
	}

	for _, m := range k.RemoteManifests {
		manifest, err := k.readRemoteManifest(ctx, m)
		if err != nil {
			return nil, fmt.Errorf("get remote manifests: %w", err)
		}

		manifests = append(manifests, manifest)
	}

	if len(k.originalImages) == 0 {
		k.originalImages, err = manifests.GetImages()
		if err != nil {
			return nil, fmt.Errorf("get images from manifests: %w", err)
		}
	}

	if len(manifests) == 0 {
		return nil, nil
	}

	manifests, err = manifests.ReplaceImages(builds)
	if err != nil {
		return nil, fmt.Errorf("replacing images in manifests: %w", err)
	}

	for _, transform := range manifestTransforms {
		manifests, err = transform(manifests, builds, k.insecureRegistries)
		if err != nil {
			return nil, fmt.Errorf("unable to transform manifests: %w", err)
		}
	}

	manifests, err = manifests.SetLabels(merge(k, labellers...))
	if err != nil {
		return nil, fmt.Errorf("setting labels in manifests: %w", err)
	}

	return manifests, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	manifests, err := k.readManifests(ctx)
	if err != nil {
		return fmt.Errorf("reading manifests: %w", err)
	}

	// revert remote manifests
	// TODO(dgageot): That seems super dangerous and I don't understand
	// why we need to update resources just before we delete them.
	if len(k.RemoteManifests) > 0 {
		var rm deploy.ManifestList
		for _, m := range k.RemoteManifests {
			manifest, err := k.readRemoteManifest(ctx, m)
			if err != nil {
				return fmt.Errorf("get remote manifests: %w", err)
			}
			rm = append(rm, manifest)
		}

		upd, err := rm.ReplaceImages(k.originalImages)
		if err != nil {
			return fmt.Errorf("replacing with originals: %w", err)
		}

		if err := k.kubectl.Apply(ctx, out, upd); err != nil {
			return fmt.Errorf("apply original: %w", err)
		}
	}

	if err := k.kubectl.Delete(ctx, textio.NewPrefixWriter(out, " - "), manifests); err != nil {
		return fmt.Errorf("delete: %w", err)
	}

	return nil
}

// Dependencies lists all the files that describe what needs to be deployed.
func (k *KubectlDeployer) Dependencies() ([]string, error) {
	return k.manifestFiles(k.KubectlDeploy.Manifests)
}
<commit_msg>Modify the deployer to support the new flag and add the ability to read manifests in GCS<commit_after>/*
Copyright 2019 The Skaffold 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 deploy

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/segmentio/textio"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	deploy "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

// KubectlDeployer deploys workflows using kubectl CLI.
type KubectlDeployer struct {
	*latest.KubectlDeploy

	originalImages     []build.Artifact
	workingDir         string
	kubectl            deploy.CLI
	insecureRegistries map[string]bool
	skipRender         bool
}

// NewKubectlDeployer returns a new KubectlDeployer for a DeployConfig filled
// with the needed configuration for `kubectl apply`
func NewKubectlDeployer(runCtx *runcontext.RunContext) *KubectlDeployer {
	return &KubectlDeployer{
		KubectlDeploy: runCtx.Cfg.Deploy.KubectlDeploy,
		workingDir:    runCtx.WorkingDir,
		kubectl: deploy.CLI{
			CLI:         kubectl.NewFromRunContext(runCtx),
			Flags:       runCtx.Cfg.Deploy.KubectlDeploy.Flags,
			ForceDeploy: runCtx.Opts.Force,
		},
		insecureRegistries: runCtx.InsecureRegistries,
		skipRender:         runCtx.Opts.SkipRender,
	}
}

func (k *KubectlDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "kubectl",
	}
}

// Deploy templates the provided manifests with a simple `find and replace` and
// runs `kubectl apply` on those manifests
func (k *KubectlDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller) *Result {
	event.DeployInProgress()
	var manifests deploy.ManifestList
	var err error
	if k.skipRender {
		manifests, err = k.readManifests(ctx)
	} else {
		manifests, err = k.renderManifests(ctx, out, builds, labellers)
	}
	if err != nil {
		event.DeployFailed(err)
		return NewDeployErrorResult(err)
	}

	if len(manifests) == 0 {
		event.DeployComplete()
		return NewDeploySuccessResult(nil)
	}

	namespaces, err := manifests.CollectNamespaces()
	if err != nil {
		event.DeployInfoEvent(fmt.Errorf("could not fetch deployed resource namespace. "+
			"This might cause port-forward and deploy health-check to fail: %w", err))
	}

	if err := k.kubectl.Apply(ctx, textio.NewPrefixWriter(out, " - "), manifests); err != nil {
		event.DeployFailed(err)
		return NewDeployErrorResult(fmt.Errorf("kubectl error: %w", err))
	}

	event.DeployComplete()
	return NewDeploySuccessResult(namespaces)
}

func (k *KubectlDeployer) manifestFiles(manifests []string) ([]string, error) {
	var nonURLManifests, gcsManifests []string
	for _, manifest := range manifests {
		if !util.IsURL(manifest) {
			if strings.HasPrefix(manifest, "gs://") {
				gcsManifests = append(gcsManifests, manifest)
			} else {
				nonURLManifests = append(nonURLManifests, manifest)
			}
		}
	}

	list, err := util.ExpandPathsGlob(k.workingDir, nonURLManifests)
	if err != nil {
		return nil, fmt.Errorf("expanding kubectl manifest paths: %w", err)
	}

	if len(gcsManifests) != 0 {
		// return tmp dir of the downloaded manifests
		tmpDir, err := downloadManifestsFromGCS(gcsManifests)
		if err != nil {
			return nil, fmt.Errorf("downloading from GCS: %w", err)
		}
		l, err := util.ExpandPathsGlob(tmpDir, []string{"*"})
		if err != nil {
			return nil, fmt.Errorf("expanding kubectl manifest paths: %w", err)
		}
		list = append(list, l...)
	}

	var filteredManifests []string
	for _, f := range list {
		if !kubernetes.HasKubernetesFileExtension(f) {
			if !util.StrSliceContains(manifests, f) {
				logrus.Infof("refusing to deploy/delete non {json, yaml} file %s", f)
				logrus.Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.")
				continue
			}
		}
		filteredManifests = append(filteredManifests, f)
	}

	return filteredManifests, nil
}

// readManifests reads the manifests to deploy/delete.
func (k *KubectlDeployer) readManifests(ctx context.Context) (deploy.ManifestList, error) {
	// Get file manifests
	manifests, err := k.Dependencies()
	if err != nil {
		return nil, fmt.Errorf("listing manifests: %w", err)
	}
	// Clean the temporary directory that holds the manifests downloaded from GCS
	defer os.RemoveAll(manifestTmpDir)

	// Append URL manifests
	for _, manifest := range k.KubectlDeploy.Manifests {

		if util.IsURL(manifest) {
			manifests = append(manifests, manifest)
		}
	}
	if len(manifests) == 0 {
		return deploy.ManifestList{}, nil
	}
	return k.kubectl.ReadManifests(ctx, manifests)
}

// readRemoteManifests will try to read manifests from the given kubernetes
// context in the specified namespace and for the specified type
func (k *KubectlDeployer) readRemoteManifest(ctx context.Context, name string) ([]byte, error) {
	var args []string
	ns := ""
	if parts := strings.Split(name, ":"); len(parts) > 1 {
		ns = parts[0]
		name = parts[1]
	}
	args = append(args, name, "-o", "yaml")

	var manifest bytes.Buffer
	err := k.kubectl.RunInNamespace(ctx, nil, &manifest, "get", ns, args...)
	if err != nil {
		return nil, fmt.Errorf("getting manifest: %w", err)
	}

	return manifest.Bytes(), nil
}

func (k *KubectlDeployer) Render(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller, filepath string) error {
	manifests, err := k.renderManifests(ctx, out, builds, labellers)
	if err != nil {
		return err
	}

	return outputRenderedManifests(manifests.String(), filepath, out)
}

func (k *KubectlDeployer) renderManifests(ctx context.Context, out io.Writer, builds []build.Artifact, labellers []Labeller) (deploy.ManifestList, error) {
	if err := k.kubectl.CheckVersion(ctx); err != nil {
		color.Default.Fprintln(out, "kubectl client version:", k.kubectl.Version(ctx))
		color.Default.Fprintln(out, err)
	}

	manifests, err := k.readManifests(ctx)
	if err != nil {
		return nil, fmt.Errorf("reading manifests: %w", err)
	}

	for _, m := range k.RemoteManifests {
		manifest, err := k.readRemoteManifest(ctx, m)
		if err != nil {
			return nil, fmt.Errorf("get remote manifests: %w", err)
		}

		manifests = append(manifests, manifest)
	}

	if len(k.originalImages) == 0 {
		k.originalImages, err = manifests.GetImages()
		if err != nil {
			return nil, fmt.Errorf("get images from manifests: %w", err)
		}
	}

	if len(manifests) == 0 {
		return nil, nil
	}

	manifests, err = manifests.ReplaceImages(builds)
	if err != nil {
		return nil, fmt.Errorf("replacing images in manifests: %w", err)
	}

	for _, transform := range manifestTransforms {
		manifests, err = transform(manifests, builds, k.insecureRegistries)
		if err != nil {
			return nil, fmt.Errorf("unable to transform manifests: %w", err)
		}
	}

	manifests, err = manifests.SetLabels(merge(k, labellers...))
	if err != nil {
		return nil, fmt.Errorf("setting labels in manifests: %w", err)
	}

	return manifests, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (k *KubectlDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	manifests, err := k.readManifests(ctx)
	if err != nil {
		return fmt.Errorf("reading manifests: %w", err)
	}

	// revert remote manifests
	// TODO(dgageot): That seems super dangerous and I don't understand
	// why we need to update resources just before we delete them.
	if len(k.RemoteManifests) > 0 {
		var rm deploy.ManifestList
		for _, m := range k.RemoteManifests {
			manifest, err := k.readRemoteManifest(ctx, m)
			if err != nil {
				return fmt.Errorf("get remote manifests: %w", err)
			}
			rm = append(rm, manifest)
		}

		upd, err := rm.ReplaceImages(k.originalImages)
		if err != nil {
			return fmt.Errorf("replacing with originals: %w", err)
		}

		if err := k.kubectl.Apply(ctx, out, upd); err != nil {
			return fmt.Errorf("apply original: %w", err)
		}
	}

	if err := k.kubectl.Delete(ctx, textio.NewPrefixWriter(out, " - "), manifests); err != nil {
		return fmt.Errorf("delete: %w", err)
	}

	return nil
}

// Dependencies lists all the files that describe what needs to be deployed.
func (k *KubectlDeployer) Dependencies() ([]string, error) {
	return k.manifestFiles(k.KubectlDeploy.Manifests)
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
 * All rights reserved
 *
 * Use of this source code is governed by a 2-clause BSD license
 * that can be found in the LICENSE file.
 */

package db

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/boltdb/bolt"
)

func (d *DB) SaveJob(jid, jtype string) error {
	if err := d.Open(); err != nil {
		return err
	}
	defer d.Close()
	now := time.Now().UTC().Format(rfc3339Milli)

	return d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		id, _ := b.NextSequence()
		return b.Put(
			uitob(id),
			[]byte(fmt.Sprintf("%s|%s|%s", jid, now, jtype)),
		)
	})
}

func (d *DB) FinishJob(id uint64, job *proto.Job) error {
	if err := d.Open(); err != nil {
		return err
	}
	defer d.Close()

	jid := job.Id
	data, err := json.Marshal(job)
	if err != nil {
		return err
	}

	return d.db.Update(func(tx *bolt.Tx) error {
		// delete from active list
		bActive := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		if err := bActive.Delete(uitob(id)); err != nil {
			return err
		}

		// insert into finished list
		bFinished := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`finished`))
		if err := bFinished.Put(uitob(id), []byte(jid)); err != nil {
			return err
		}

		// save job data
		bData := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`data`))
		return bData.Put([]byte(jid), data)
	})
}

// Slice of slice of strings, with the inner slice contents as follows:
// 0: storage key
// 1: JobID
// 2: Timestamp
// 3: JobType
func (d *DB) GetActiveJobs() ([][]string, error) {
	if err := d.Open(); err != nil {
		return nil, err
	}
	defer d.Close()

	count := 0
	jobs := [][]string{}
	if err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			count++
			r := []string{bots(k)}
			r = append(r, strings.Split(string(v), `|`)...)
			jobs = append(jobs, r)
		}
		return nil
	}); err != nil {
		return nil, err
	}
	if count > 0 {
		return jobs, nil
	}
	return nil, bolt.ErrBucketNotFound
}

func uitob(v uint64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, v)
	return b
}

func botui(b []byte) uint64 {
	return binary.BigEndian.Uint64(b)
}

func bots(b []byte) string {
	return strconv.FormatUint(binary.BigEndian.Uint64(b), 10)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Rename GetActiveJobs() -> ActiveJobs()<commit_after>/*-
 * Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
 * All rights reserved
 *
 * Use of this source code is governed by a 2-clause BSD license
 * that can be found in the LICENSE file.
 */

package db

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/boltdb/bolt"
)

func (d *DB) SaveJob(jid, jtype string) error {
	if err := d.Open(); err != nil {
		return err
	}
	defer d.Close()
	now := time.Now().UTC().Format(rfc3339Milli)

	return d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		id, _ := b.NextSequence()
		return b.Put(
			uitob(id),
			[]byte(fmt.Sprintf("%s|%s|%s", jid, now, jtype)),
		)
	})
}

func (d *DB) FinishJob(id uint64, job *proto.Job) error {
	if err := d.Open(); err != nil {
		return err
	}
	defer d.Close()

	jid := job.Id
	data, err := json.Marshal(job)
	if err != nil {
		return err
	}

	return d.db.Update(func(tx *bolt.Tx) error {
		// delete from active list
		bActive := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		if err := bActive.Delete(uitob(id)); err != nil {
			return err
		}

		// insert into finished list
		bFinished := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`finished`))
		if err := bFinished.Put(uitob(id), []byte(jid)); err != nil {
			return err
		}

		// save job data
		bData := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`data`))
		return bData.Put([]byte(jid), data)
	})
}

// Slice of slice of strings, with the inner slice contents as follows:
// 0: storage key
// 1: JobID
// 2: Timestamp
// 3: JobType
func (d *DB) ActiveJobs() ([][]string, error) {
	if err := d.Open(); err != nil {
		return nil, err
	}
	defer d.Close()

	count := 0
	jobs := [][]string{}
	if err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(`jobs`)).Bucket([]byte(`active`))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			count++
			r := []string{bots(k)}
			r = append(r, strings.Split(string(v), `|`)...)
			jobs = append(jobs, r)
		}
		return nil
	}); err != nil {
		return nil, err
	}
	if count > 0 {
		return jobs, nil
	}
	return nil, bolt.ErrBucketNotFound
}

func uitob(v uint64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, v)
	return b
}

func botui(b []byte) uint64 {
	return binary.BigEndian.Uint64(b)
}

func bots(b []byte) string {
	return strconv.FormatUint(binary.BigEndian.Uint64(b), 10)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"

	"github.com/codegangsta/cli"
	"github.com/inconshreveable/log15"

	"polydawn.net/repeatr/api/def"
	"polydawn.net/repeatr/core/actors"
	"polydawn.net/repeatr/core/executor/dispatch"
)

func TwerkCommandPattern(stdin io.Reader, stdout, stderr io.Writer) cli.Command {
	return cli.Command{
		Name:  "twerk",
		Usage: "Run one-time-use interactive (thus nonrepeatable!) command.  All the defaults are filled in for you.  Great for experimentation.",
		Action: func(ctx *cli.Context) {
			execr := executordispatch.Get("runc")
			formula := def.Formula{
				Inputs: def.InputGroup{"main": {
					Type:      "tar",
					MountPath: "/",
					Hash:      "aLMH4qK1EdlPDavdhErOs0BPxqO0i6lUaeRE4DuUmnNMxhHtF56gkoeSulvwWNqT",
					Warehouses: def.WarehouseCoords{
						"http+ca://repeatr.s3.amazonaws.com/assets/",
					},
				}},
				Action: def.Action{
					Entrypoint: []string{"bash"},
					Escapes: def.Escapes{
						Mounts: []def.Mount{{
							SourcePath: ".",
							TargetPath: "/whee",
							Writable:   true,
						}},
					},
					Cwd: "/whee",
				},
			}

			// Set up a logger.
			log := log15.New()
			log.SetHandler(log15.StreamHandler(stderr, log15.TerminalFormat()))

			// Create a local formula runner, and fire.
			runner := actor.NewFormulaRunner(execr, log)
			runner.InjectStdin(stdin)
			runID := runner.StartRun(&formula)

			// Stream job output to terminal in real time
			runner.FollowStreams(runID, stdout, stderr)

			// Wait for results.
			result := runner.FollowResults(runID)

			if result.Failure != nil {
				panic(Exit.NewWith(
					fmt.Sprintf("job execution errored: %s", result.Failure),
					SetExitCode(EXIT_USER), // TODO review exit code
				))
			}
			exitCode := result.Results["$exitcode"].Hash
			if exitCode != "0" {
				panic(Exit.NewWith(
					fmt.Sprintf("done; action finished with exit status %s", exitCode),
					SetExitCode(EXIT_JOB),
				))
			}
			panic(Exit.NewWith("done; action reported successful exit status", SetExitCode(EXIT_SUCCESS)))
		},
	}
}
<commit_msg>Allow policy changes in introspective mode.<commit_after>package cli

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/inconshreveable/log15"

	"polydawn.net/repeatr/api/def"
	"polydawn.net/repeatr/core/actors"
	"polydawn.net/repeatr/core/executor/dispatch"
)

func TwerkCommandPattern(stdin io.Reader, stdout, stderr io.Writer) cli.Command {
	return cli.Command{
		Name:  "twerk",
		Usage: "Run one-time-use interactive (thus nonrepeatable!) command.  All the defaults are filled in for you.  Great for experimentation.",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "executor",
				Value: "runc",
				Usage: "Which executor to use. Using anything other than runc at this time is not particularly useful or functional.",
			},
			cli.StringSliceFlag{
				Name:  "patch, p",
				Usage: "files with additional pieces of formula to apply before launch",
			},
			cli.StringSliceFlag{
				Name:  "env, e",
				Usage: "apply additional environment vars to formula before launch (overrides 'patch').  Format like '-e KEY=val'",
			},
			cli.StringFlag{
				Name:  "policy, P",
				Value: string(def.PolicyRoutine),
				Usage: "Which capabilities policy to use",
			},
		},
		Action: func(ctx *cli.Context) {
			executor := executordispatch.Get(ctx.String("executor"))
			patchPaths := ctx.StringSlice("patch")
			envArgs := ctx.StringSlice("env")
			policy := ctx.String("policy")

			formula := def.Formula{
				Inputs: def.InputGroup{"main": {
					Type:      "tar",
					MountPath: "/",
					Hash:      "aLMH4qK1EdlPDavdhErOs0BPxqO0i6lUaeRE4DuUmnNMxhHtF56gkoeSulvwWNqT",
					Warehouses: def.WarehouseCoords{
						"http+ca://repeatr.s3.amazonaws.com/assets/",
					},
				}},
				Action: def.Action{
					Entrypoint: []string{"bash"},
					Escapes: def.Escapes{
						Mounts: []def.Mount{{
							SourcePath: ".",
							TargetPath: "/whee",
							Writable:   true,
						}},
					},
					Cwd: "/whee",
				},
			}

			// Parse patches into formulas as well.
			//  Apply each one as it's loaded.
			for _, patchPath := range patchPaths {
				formula.ApplyPatch(LoadFormulaFromFile(patchPath))
			}

			if !func(policy string) bool {
				for _, policyType := range def.PolicyValues {
					if policy == string(policyType) {
						return true
					}
				}
				return false
			}(policy) {
				var buffer bytes.Buffer
				buffer.WriteString("Must select a valid policy:")
				for _, value := range def.PolicyValues {
					buffer.WriteString(" ")
					buffer.WriteString(string(value))
				}
				panic(Error.NewWith(buffer.String(), SetExitCode(EXIT_BADARGS)))
			}
			// Any policy overrides apply after patches
			formula.Action.Policy = def.Policy(policy)

			// Any env var overrides stomp even on top of patches.
			for _, envArg := range envArgs {
				parts := strings.SplitN(envArg, "=", 2)
				if len(parts) < 2 {
					panic(Error.NewWith(
						"env arguments must have an equal sign (like this: '-e KEY=val').",
						SetExitCode(EXIT_BADARGS),
					))
				}
				formula.ApplyPatch(def.Formula{Action: def.Action{
					Env: map[string]string{parts[0]: parts[1]},
				}})
			}

			// Set up a logger.
			log := log15.New()
			log.SetHandler(log15.StreamHandler(stderr, log15.TerminalFormat()))

			// Create a local formula runner, and fire.
			runner := actor.NewFormulaRunner(executor, log)
			runner.InjectStdin(stdin)
			runID := runner.StartRun(&formula)

			// Stream job output to terminal in real time
			runner.FollowStreams(runID, stdout, stderr)

			// Wait for results.
			result := runner.FollowResults(runID)

			if result.Failure != nil {
				panic(Exit.NewWith(
					fmt.Sprintf("job execution errored: %s", result.Failure),
					SetExitCode(EXIT_USER), // TODO review exit code
				))
			}
			exitCode := result.Results["$exitcode"].Hash
			if exitCode != "0" {
				panic(Exit.NewWith(
					fmt.Sprintf("done; action finished with exit status %s", exitCode),
					SetExitCode(EXIT_JOB),
				))
			}
			panic(Exit.NewWith("done; action reported successful exit status", SetExitCode(EXIT_SUCCESS)))
		},
	}
}
<|endoftext|>


<commit_before>package commands

import (
	cmds "github.com/jbenet/go-ipfs/commands"
	config "github.com/jbenet/go-ipfs/config"
)

type VersionOutput struct {
	Version string
}

var VersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Outputs the current version of IPFS",
		ShortDescription: "Returns the version number of IPFS and exits.",
	},

	Options: []cmds.Option{
		cmds.BoolOption("number", "n", "Only output the version number"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		return &VersionOutput{
			Version: config.CurrentVersionNumber,
		}, nil
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) ([]byte, error) {
			v := res.Output().(*VersionOutput)
			s := ""

			number, found, err := res.Request().Option("number").Bool()
			if err != nil {
				return nil, err
			}
			if !found {
				number = false
			}

			if !number {
				s += "ipfs version "
			}
			s += v.Version
			return []byte(s), nil
		},
	},
	Type: &VersionOutput{},
}
<commit_msg>fix(2/version)<commit_after>package commands

import (
	"fmt"

	cmds "github.com/jbenet/go-ipfs/commands"
	config "github.com/jbenet/go-ipfs/config"
)

type VersionOutput struct {
	Version string
}

var VersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Shows ipfs version information",
		ShortDescription: "Shows ipfs version information",
		LongDescription: `ipfs version - Show ipfs version information.

		Returns the current version of ipfs and exits.
		`,
	},

	Options: []cmds.Option{
		cmds.BoolOption("number", "n", "Only show the version number"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		return &VersionOutput{
			Version: config.CurrentVersionNumber,
		}, nil
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) ([]byte, error) {
			v := res.Output().(*VersionOutput)

			number, found, err := res.Request().Option("number").Bool()
			if err != nil {
				return nil, err
			}
			if found && number {
				return []byte(fmt.Sprintln(v.Version)), nil
			}
			return []byte(fmt.Sprintf("ipfs version %s\n", v.Version)), nil
		},
	},
	Type: &VersionOutput{},
}
<|endoftext|>


<commit_before>package corehttp

import (
	"net/http"
	"time"

	manners "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
	core "github.com/ipfs/go-ipfs/core"
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
)

var log = eventlog.Logger("core/server")

// ServeOption registers any HTTP handlers it provides on the given mux.
// It returns the mux to expose to future options, which may be a new mux if it
// is interested in mediating requests to future options, or the same mux
// initially passed in if not.
type ServeOption func(*core.IpfsNode, *http.ServeMux) (*http.ServeMux, error)

// makeHandler turns a list of ServeOptions into a http.Handler that implements
// all of the given options, in order.
func makeHandler(n *core.IpfsNode, options ...ServeOption) (http.Handler, error) {
	topMux := http.NewServeMux()
	mux := topMux
	for _, option := range options {
		var err error
		mux, err = option(n, mux)
		if err != nil {
			return nil, err
		}
	}
	return topMux, nil
}

// ListenAndServe runs an HTTP server listening at |listeningMultiAddr| with
// the given serve options. The address must be provided in multiaddr format.
//
// TODO intelligently parse address strings in other formats so long as they
// unambiguously map to a valid multiaddr. e.g. for convenience, ":8080" should
// map to "/ip4/0.0.0.0/tcp/8080".
func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...ServeOption) error {
	addr, err := ma.NewMultiaddr(listeningMultiAddr)
	if err != nil {
		return err
	}
	handler, err := makeHandler(n, options...)
	if err != nil {
		return err
	}
	return listenAndServe(n, addr, handler)
}

func listenAndServe(node *core.IpfsNode, addr ma.Multiaddr, handler http.Handler) error {
	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	server := manners.NewServer()

	// if the server exits beforehand
	var serverError error
	serverExited := make(chan struct{})

	go func() {
		serverError = server.ListenAndServe(host, handler)
		close(serverExited)
	}()

	// wait for server to exit.
	select {
	case <-serverExited:

	// if node being closed before server exits, close server
	case <-node.Closing():
		log.Infof("server at %s terminating...", addr)
		server.Shutdown <- true

	outer:
		for {
			// wait until server exits
			select {
			case <-serverExited:
				break outer
			case <-time.After(5 * time.Second):
				log.Infof("waiting for server at %s to terminate...", addr)
			}
		}
	}

	log.Infof("server at %s terminated", addr)
	return serverError
}
<commit_msg>corehttp: ensure node closing/teardown waits for server termination<commit_after>package corehttp

import (
	"net/http"
	"time"

	manners "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
	core "github.com/ipfs/go-ipfs/core"
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
)

var log = eventlog.Logger("core/server")

// ServeOption registers any HTTP handlers it provides on the given mux.
// It returns the mux to expose to future options, which may be a new mux if it
// is interested in mediating requests to future options, or the same mux
// initially passed in if not.
type ServeOption func(*core.IpfsNode, *http.ServeMux) (*http.ServeMux, error)

// makeHandler turns a list of ServeOptions into a http.Handler that implements
// all of the given options, in order.
func makeHandler(n *core.IpfsNode, options ...ServeOption) (http.Handler, error) {
	topMux := http.NewServeMux()
	mux := topMux
	for _, option := range options {
		var err error
		mux, err = option(n, mux)
		if err != nil {
			return nil, err
		}
	}
	return topMux, nil
}

// ListenAndServe runs an HTTP server listening at |listeningMultiAddr| with
// the given serve options. The address must be provided in multiaddr format.
//
// TODO intelligently parse address strings in other formats so long as they
// unambiguously map to a valid multiaddr. e.g. for convenience, ":8080" should
// map to "/ip4/0.0.0.0/tcp/8080".
func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...ServeOption) error {
	addr, err := ma.NewMultiaddr(listeningMultiAddr)
	if err != nil {
		return err
	}
	handler, err := makeHandler(n, options...)
	if err != nil {
		return err
	}
	return listenAndServe(n, addr, handler)
}

func listenAndServe(node *core.IpfsNode, addr ma.Multiaddr, handler http.Handler) error {
	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	server := manners.NewServer()

	// if the server exits beforehand
	var serverError error
	serverExited := make(chan struct{})

	node.Children().Add(1)
	defer node.Children().Done()

	go func() {
		serverError = server.ListenAndServe(host, handler)
		close(serverExited)
	}()

	// wait for server to exit.
	select {
	case <-serverExited:

	// if node being closed before server exits, close server
	case <-node.Closing():
		log.Infof("server at %s terminating...", addr)
		server.Shutdown <- true

	outer:
		for {
			// wait until server exits
			select {
			case <-serverExited:
				break outer
			case <-time.After(5 * time.Second):
				log.Infof("waiting for server at %s to terminate...", addr)
			}
		}
	}

	log.Infof("server at %s terminated", addr)
	return serverError
}
<|endoftext|>


<commit_before>package dnsserver

import (
	"fmt"
	"net"
	"strings"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/parse"
	"github.com/coredns/coredns/plugin/pkg/transport"

	"github.com/miekg/dns"
)

type zoneAddr struct {
	Zone      string
	Port      string
	Transport string     // dns, tls or grpc
	IPNet     *net.IPNet // if reverse zone this hold the IPNet
	Address   string     // used for bound zoneAddr - validation of overlapping
}

// String return the string representation of z.
func (z zoneAddr) String() string {
	s := z.Transport + "://" + z.Zone + ":" + z.Port
	if z.Address != "" {
		s += " on " + z.Address
	}
	return s
}

// normalizeZone parses an zone string into a structured format with separate
// host, and port portions, as well as the original input string.
func normalizeZone(str string) (zoneAddr, error) {
	var err error

	var trans string
	trans, str = parse.Transport(str)

	host, port, ipnet, err := plugin.SplitHostPort(str)
	if err != nil {
		return zoneAddr{}, err
	}

	if port == "" {
		switch trans {
		case transport.DNS:
			port = Port
		case transport.TLS:
			port = transport.TLSPort
		case transport.GRPC:
			port = transport.GRPCPort
		case transport.HTTPS:
			port = transport.HTTPSPort
		}
	}

	return zoneAddr{Zone: dns.Fqdn(host), Port: port, Transport: trans, IPNet: ipnet}, nil
}

// SplitProtocolHostPort splits a full formed address like "dns://[::1]:53" into parts.
func SplitProtocolHostPort(address string) (protocol string, ip string, port string, err error) {
	parts := strings.Split(address, "://")
	switch len(parts) {
	case 1:
		ip, port, err := net.SplitHostPort(parts[0])
		return "", ip, port, err
	case 2:
		ip, port, err := net.SplitHostPort(parts[1])
		return parts[0], ip, port, err
	default:
		return "", "", "", fmt.Errorf("provided value is not in an address format : %s", address)
	}
}

type zoneOverlap struct {
	registeredAddr map[zoneAddr]zoneAddr // each zoneAddr is registered once by its key
	unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAdddr is registered by its original key
}

func newOverlapZone() *zoneOverlap {
	return &zoneOverlap{registeredAddr: make(map[zoneAddr]zoneAddr), unboundOverlap: make(map[zoneAddr]zoneAddr)}
}

// registerAndCheck adds a new zoneAddr for validation, it returns information about existing or overlapping with already registered
// we consider that an unbound address is overlapping all bound addresses for same zone, same port
func (zo *zoneOverlap) registerAndCheck(z zoneAddr) (existingZone *zoneAddr, overlappingZone *zoneAddr) {

	if exist, ok := zo.registeredAddr[z]; ok {
		// exact same zone already registered
		return &exist, nil
	}
	uz := zoneAddr{Zone: z.Zone, Address: "", Port: z.Port, Transport: z.Transport}
	if already, ok := zo.unboundOverlap[uz]; ok {
		if z.Address == "" {
			// current is not bound to an address, but there is already another zone with a bind address registered
			return nil, &already
		}
		if _, ok := zo.registeredAddr[uz]; ok {
			// current zone is bound to an address, but there is already an overlapping zone+port with no bind address
			return nil, &uz
		}
	}
	// there is no overlap, keep the current zoneAddr for future checks
	zo.registeredAddr[z] = z
	zo.unboundOverlap[uz] = z
	return nil, nil
}
<commit_msg>Fix nits in address.go. (#2404)<commit_after>package dnsserver

import (
	"fmt"
	"net"
	"strings"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/parse"
	"github.com/coredns/coredns/plugin/pkg/transport"

	"github.com/miekg/dns"
)

type zoneAddr struct {
	Zone      string
	Port      string
	Transport string     // dns, tls or grpc
	IPNet     *net.IPNet // if reverse zone this hold the IPNet
	Address   string     // used for bound zoneAddr - validation of overlapping
}

// String returns the string representation of z.
func (z zoneAddr) String() string {
	s := z.Transport + "://" + z.Zone + ":" + z.Port
	if z.Address != "" {
		s += " on " + z.Address
	}
	return s
}

// normalizeZone parses a zone string into a structured format with separate
// host, and port portions, as well as the original input string.
func normalizeZone(str string) (zoneAddr, error) {
	trans, str := parse.Transport(str)

	host, port, ipnet, err := plugin.SplitHostPort(str)
	if err != nil {
		return zoneAddr{}, err
	}

	if port == "" {
		switch trans {
		case transport.DNS:
			port = Port
		case transport.TLS:
			port = transport.TLSPort
		case transport.GRPC:
			port = transport.GRPCPort
		case transport.HTTPS:
			port = transport.HTTPSPort
		}
	}

	return zoneAddr{Zone: dns.Fqdn(host), Port: port, Transport: trans, IPNet: ipnet}, nil
}

// SplitProtocolHostPort splits a full formed address like "dns://[::1]:53" into parts.
func SplitProtocolHostPort(address string) (protocol string, ip string, port string, err error) {
	parts := strings.Split(address, "://")
	switch len(parts) {
	case 1:
		ip, port, err := net.SplitHostPort(parts[0])
		return "", ip, port, err
	case 2:
		ip, port, err := net.SplitHostPort(parts[1])
		return parts[0], ip, port, err
	default:
		return "", "", "", fmt.Errorf("provided value is not in an address format : %s", address)
	}
}

type zoneOverlap struct {
	registeredAddr map[zoneAddr]zoneAddr // each zoneAddr is registered once by its key
	unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAdddr is registered by its original key
}

func newOverlapZone() *zoneOverlap {
	return &zoneOverlap{registeredAddr: make(map[zoneAddr]zoneAddr), unboundOverlap: make(map[zoneAddr]zoneAddr)}
}

// registerAndCheck adds a new zoneAddr for validation, it returns information about existing or overlapping with already registered
// we consider that an unbound address is overlapping all bound addresses for same zone, same port
func (zo *zoneOverlap) registerAndCheck(z zoneAddr) (existingZone *zoneAddr, overlappingZone *zoneAddr) {

	if exist, ok := zo.registeredAddr[z]; ok {
		// exact same zone already registered
		return &exist, nil
	}
	uz := zoneAddr{Zone: z.Zone, Address: "", Port: z.Port, Transport: z.Transport}
	if already, ok := zo.unboundOverlap[uz]; ok {
		if z.Address == "" {
			// current is not bound to an address, but there is already another zone with a bind address registered
			return nil, &already
		}
		if _, ok := zo.registeredAddr[uz]; ok {
			// current zone is bound to an address, but there is already an overlapping zone+port with no bind address
			return nil, &uz
		}
	}
	// there is no overlap, keep the current zoneAddr for future checks
	zo.registeredAddr[z] = z
	zo.unboundOverlap[uz] = z
	return nil, nil
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"fmt"
	"net"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"runtime"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/BurntSushi/toml"
)

type Cluster struct {
	Name    string `json:"name"`
	GitHost string `json:"git_host"`
	URL     string `json:"url"`
	Key     string `json:"key"`
	TLSPin  string `json:"tls_pin"`
}

type Config struct {
	Default  string     `toml:"default"`
	Clusters []*Cluster `toml:"cluster"`
}

func HomeDir() string {
	key := "HOME"
	if runtime.GOOS == "windows" {
		key = "USERPROFILE"
	}
	home := os.Getenv(key)
	if home == "" {
		user, _ := user.Current()
		home = user.HomeDir
	}
	return home
}

func Dir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "flynn")
	}
	return filepath.Join(HomeDir(), ".flynn")
}

func DefaultPath() string {
	if p := os.Getenv("FLYNNRC"); p != "" {
		return p
	}
	if runtime.GOOS == "windows" {
		return filepath.Join(Dir(), "flynnrc")
	}
	return filepath.Join(HomeDir(), ".flynnrc")
}

func ReadFile(path string) (*Config, error) {
	c := &Config{}
	_, err := toml.DecodeFile(path, c)
	if err != nil {
		return c, err
	}
	return c, nil
}

func (c *Config) Marshal() []byte {
	var buf bytes.Buffer
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
		panic(err)
	}
	return buf.Bytes()
}

func (c *Config) Add(s *Cluster, force bool) error {
	if s.GitHost == "" {
		u, err := url.Parse(s.URL)
		if err != nil {
			return err
		}
		if host, _, err := net.SplitHostPort(u.Host); err == nil {
			s.GitHost = host
		} else {
			s.GitHost = u.Host
		}
	}

	for i, existing := range c.Clusters {
		msg := ""

		switch {
		case existing.Name == s.Name:
			msg = fmt.Sprintf("Cluster %q already exists in ~/.flynnrc", s.Name)
		case existing.URL == s.URL:
			msg = fmt.Sprintf("A cluster with the URL %q already exists in ~/.flynnrc", s.URL)
		case existing.GitHost == s.GitHost:
			msg = fmt.Sprintf("A cluster with the git host %q already exists in ~/.flynnrc", s.GitHost)
		}

		// The new cluster config match with existing one
		if msg != "" {
			if !force {
				return fmt.Errorf(msg)
			}

			// Remove existing match
			c.Clusters = append(c.Clusters[:i], c.Clusters[i+1:]...)
		}
	}

	// Did not match
	c.Clusters = append(c.Clusters, s)

	return nil
}

func (c *Config) Remove(name string) bool {
	for i, s := range c.Clusters {
		if s.Name != name {
			continue
		}
		c.Clusters = append(c.Clusters[:i], c.Clusters[i+1:]...)
		return true
	}
	return false
}

func (c *Config) SetDefault(name string) bool {
	for _, s := range c.Clusters {
		if s.Name != name {
			continue
		}
		c.Default = name
		return true
	}
	return false
}

func (c *Config) SaveTo(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	if len(c.Clusters) != 0 {
		if err := toml.NewEncoder(f).Encode(c); err != nil {
			return err
		}
		f.Write([]byte("\n"))
	}
	return nil
}
<commit_msg>cli: Use go-homedir instead of os/user<commit_after>package config

import (
	"bytes"
	"fmt"
	"net"
	"net/url"
	"os"
	"path/filepath"
	"runtime"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/BurntSushi/toml"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
)

type Cluster struct {
	Name    string `json:"name"`
	GitHost string `json:"git_host"`
	URL     string `json:"url"`
	Key     string `json:"key"`
	TLSPin  string `json:"tls_pin"`
}

type Config struct {
	Default  string     `toml:"default"`
	Clusters []*Cluster `toml:"cluster"`
}

func HomeDir() string {
	dir, err := homedir.Dir()
	if err != nil {
		panic(err)
	}
	return dir
}

func Dir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "flynn")
	}
	return filepath.Join(HomeDir(), ".flynn")
}

func DefaultPath() string {
	if p := os.Getenv("FLYNNRC"); p != "" {
		return p
	}
	if runtime.GOOS == "windows" {
		return filepath.Join(Dir(), "flynnrc")
	}
	return filepath.Join(HomeDir(), ".flynnrc")
}

func ReadFile(path string) (*Config, error) {
	c := &Config{}
	_, err := toml.DecodeFile(path, c)
	if err != nil {
		return c, err
	}
	return c, nil
}

func (c *Config) Marshal() []byte {
	var buf bytes.Buffer
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
		panic(err)
	}
	return buf.Bytes()
}

func (c *Config) Add(s *Cluster, force bool) error {
	if s.GitHost == "" {
		u, err := url.Parse(s.URL)
		if err != nil {
			return err
		}
		if host, _, err := net.SplitHostPort(u.Host); err == nil {
			s.GitHost = host
		} else {
			s.GitHost = u.Host
		}
	}

	for i, existing := range c.Clusters {
		msg := ""

		switch {
		case existing.Name == s.Name:
			msg = fmt.Sprintf("Cluster %q already exists in ~/.flynnrc", s.Name)
		case existing.URL == s.URL:
			msg = fmt.Sprintf("A cluster with the URL %q already exists in ~/.flynnrc", s.URL)
		case existing.GitHost == s.GitHost:
			msg = fmt.Sprintf("A cluster with the git host %q already exists in ~/.flynnrc", s.GitHost)
		}

		// The new cluster config match with existing one
		if msg != "" {
			if !force {
				return fmt.Errorf(msg)
			}

			// Remove existing match
			c.Clusters = append(c.Clusters[:i], c.Clusters[i+1:]...)
		}
	}

	// Did not match
	c.Clusters = append(c.Clusters, s)

	return nil
}

func (c *Config) Remove(name string) bool {
	for i, s := range c.Clusters {
		if s.Name != name {
			continue
		}
		c.Clusters = append(c.Clusters[:i], c.Clusters[i+1:]...)
		return true
	}
	return false
}

func (c *Config) SetDefault(name string) bool {
	for _, s := range c.Clusters {
		if s.Name != name {
			continue
		}
		c.Default = name
		return true
	}
	return false
}

func (c *Config) SaveTo(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()

	if len(c.Clusters) != 0 {
		if err := toml.NewEncoder(f).Encode(c); err != nil {
			return err
		}
		f.Write([]byte("\n"))
	}
	return nil
}
<|endoftext|>


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

package document

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/go-kit/kit/log"

	"github.com/diegobernardes/flare"
	infraHTTP "github.com/diegobernardes/flare/infra/http"
)

// Service implements the HTTP handler to manage documents.
type Service struct {
	logger                 log.Logger
	documentRepository     flare.DocumentRepositorier
	resourceRepository     flare.ResourceRepositorier
	subscriptionRepository flare.SubscriptionRepositorier
	subscriptionTrigger    flare.SubscriptionTrigger
	getDocumentId          func(*http.Request) string
	getDocumentURI         func(id string) string
	writeResponse          func(http.ResponseWriter, interface{}, int, http.Header)
}

// HandleShow receive the request to show a given document.
func (s *Service) HandleShow(w http.ResponseWriter, r *http.Request) {
	d, err := s.documentRepository.FindOne(r.Context(), s.getDocumentId(r))
	if err != nil {
		status := http.StatusInternalServerError
		if errRepo, ok := err.(flare.ResourceRepositoryError); ok && errRepo.NotFound() {
			status = http.StatusNotFound
		}

		s.writeError(w, err, "error during document search", status)
		return
	}

	s.writeResponse(w, transformDocument(d), http.StatusOK, nil)
}

// HandleUpdate process the request to update a document.
func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request) {
	document, ok := s.parseHandleUpdateDocument(w, r)
	if !ok {
		return
	}

	referenceDocument, err := s.documentRepository.FindOne(r.Context(), document.Id)
	if err != nil {
		if _, ok := err.(flare.DocumentRepositoryError); !ok {
			s.writeError(w, err, "error during document search", http.StatusInternalServerError)
			return
		}
	}

	hasSubscr, err := s.subscriptionRepository.HasSubscription(r.Context(), document.Resource.Id)
	if err != nil {
		s.writeError(
			w,
			err,
			"error during check if the document resource has subscriptions",
			http.StatusInternalServerError,
		)
		return
	}
	if !hasSubscr {
		s.writeResponse(w, &response{Document: transformDocument(document)}, http.StatusOK, nil)
		return
	}

	status := http.StatusOK
	if referenceDocument == nil {
		status = http.StatusCreated
	}

	if ok := s.updateAndTriggerDocumentChange(w, r, document, referenceDocument, status); !ok {
		return
	}

	header := make(http.Header)
	header.Set("Location", s.getDocumentURI(document.Id))
	s.writeResponse(w, &response{Document: transformDocument(document)}, status, header)
}

func (s *Service) parseHandleUpdateDocument(
	w http.ResponseWriter, r *http.Request,
) (*flare.Document, bool) {
	d := json.NewDecoder(r.Body)
	content := make(map[string]interface{})
	if err := d.Decode(&content); err != nil {
		s.writeError(w, err, "invalid body content", http.StatusBadRequest)
		return nil, false
	}

	documentId := s.getDocumentId(r)
	resource, err := s.resourceRepository.FindByURI(r.Context(), documentId)
	if err != nil {
		s.writeError(w, err, "error during resource search", http.StatusInternalServerError)
		return nil, false
	}

	document := &flare.Document{
		Id:               documentId,
		ChangeFieldValue: content[resource.Change.Field],
		Resource:         *resource,
	}
	if err = document.Valid(); err != nil {
		s.writeError(w, err, "document is not valid", http.StatusBadRequest)
		return nil, false
	}

	return document, true
}

func (s *Service) updateAndTriggerDocumentChange(
	w http.ResponseWriter, r *http.Request, document, referenceDocument *flare.Document, status int,
) bool {
	var (
		newer bool
		err   error
	)

	if referenceDocument != nil {
		newer, err = document.Newer(referenceDocument)
		if err != nil {
			s.writeError(
				w,
				err,
				"error during comparing the document with the latest one on datastorage",
				http.StatusBadRequest,
			)
			return false
		}
	}

	if newer || status == http.StatusCreated {
		if err = s.documentRepository.Update(r.Context(), document); err != nil {
			s.writeError(w, err, "error during document persistence", http.StatusInternalServerError)
			return false
		}
	}

	switch status {
	case http.StatusOK, http.StatusCreated:
		err = s.subscriptionTrigger.Update(r.Context(), document)
	}
	if err != nil {
		s.writeError(w, err, "error during document change trigger", http.StatusInternalServerError)
		return false
	}
	return true
}

// HandleDelete receive the request to delete a document.
func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request) {
	document, err := s.documentRepository.FindOne(r.Context(), s.getDocumentId(r))
	if err != nil {
		status := http.StatusInternalServerError
		if errRepo, ok := err.(flare.DocumentRepositoryError); ok && errRepo.NotFound() {
			status = http.StatusNotFound
		}

		s.writeError(w, err, "error during the check if the document exists", status)
		return
	}

	if err = s.documentRepository.Delete(r.Context(), document.Id); err != nil {
		s.writeError(w, err, "error during delete", http.StatusInternalServerError)
		return
	}

	if err = s.subscriptionTrigger.Delete(r.Context(), document); err != nil {
		s.writeError(w, err, "error during document change trigger", http.StatusInternalServerError)
		return
	}

	s.writeResponse(w, nil, http.StatusNoContent, nil)
}

// NewService initialize the service to handle HTTP requests.
func NewService(options ...func(*Service)) (*Service, error) {
	s := &Service{}

	for _, option := range options {
		option(s)
	}

	if s.logger == nil {
		return nil, errors.New("logger not found")
	}

	if s.subscriptionTrigger == nil {
		return nil, errors.New("subscriptionTrigger not found")
	}

	if s.documentRepository == nil {
		return nil, errors.New("documentRepository not found")
	}

	if s.resourceRepository == nil {
		return nil, errors.New("resourceRepository not found")
	}

	if s.subscriptionRepository == nil {
		return nil, errors.New("subscriptionRepository not found")
	}

	if s.getDocumentId == nil {
		return nil, errors.New("getDocumentId not found")
	}

	if s.getDocumentURI == nil {
		return nil, errors.New("getDocumentURI not found")
	}

	s.writeResponse = infraHTTP.WriteResponse(s.logger)
	return s, nil
}

// ServiceDocumentRepository set the repository to access the documents.
func ServiceDocumentRepository(repo flare.DocumentRepositorier) func(*Service) {
	return func(s *Service) { s.documentRepository = repo }
}

// ServiceResourceRepository set the repository to access the resources.
func ServiceResourceRepository(repo flare.ResourceRepositorier) func(*Service) {
	return func(s *Service) { s.resourceRepository = repo }
}

// ServiceSubscriptionRepository set the repository to access the subscriptions.
func ServiceSubscriptionRepository(repo flare.SubscriptionRepositorier) func(*Service) {
	return func(s *Service) { s.subscriptionRepository = repo }
}

// ServiceLogger set the logger.
func ServiceLogger(logger log.Logger) func(*Service) {
	return func(s *Service) { s.logger = logger }
}

// ServiceGetDocumentId set the function to get the document id..
func ServiceGetDocumentId(fn func(*http.Request) string) func(*Service) {
	return func(s *Service) { s.getDocumentId = fn }
}

// ServiceGetDocumentURI set the function to generate the URI of a given Document.
func ServiceGetDocumentURI(fn func(string) string) func(*Service) {
	return func(s *Service) { s.getDocumentURI = fn }
}

// ServiceSubscriptionTrigger set the subscription trigger processor.
func ServiceSubscriptionTrigger(trigger flare.SubscriptionTrigger) func(*Service) {
	return func(s *Service) { s.subscriptionTrigger = trigger }
}
<commit_msg>document: protect the update endpoint from accepting query strings<commit_after>// Copyright 2017 Diego Bernardes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package document

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/go-kit/kit/log"

	"github.com/diegobernardes/flare"
	infraHTTP "github.com/diegobernardes/flare/infra/http"
)

// Service implements the HTTP handler to manage documents.
type Service struct {
	logger                 log.Logger
	documentRepository     flare.DocumentRepositorier
	resourceRepository     flare.ResourceRepositorier
	subscriptionRepository flare.SubscriptionRepositorier
	subscriptionTrigger    flare.SubscriptionTrigger
	getDocumentId          func(*http.Request) string
	getDocumentURI         func(id string) string
	writeResponse          func(http.ResponseWriter, interface{}, int, http.Header)
}

// HandleShow receive the request to show a given document.
func (s *Service) HandleShow(w http.ResponseWriter, r *http.Request) {
	d, err := s.documentRepository.FindOne(r.Context(), s.getDocumentId(r))
	if err != nil {
		status := http.StatusInternalServerError
		if errRepo, ok := err.(flare.ResourceRepositoryError); ok && errRepo.NotFound() {
			status = http.StatusNotFound
		}

		s.writeError(w, err, "error during document search", status)
		return
	}

	s.writeResponse(w, transformDocument(d), http.StatusOK, nil)
}

// HandleUpdate process the request to update a document.
func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request) {
	if r.URL.RawQuery != "" {
		s.writeError(
			w,
			fmt.Errorf("query string not allowed '%s'", r.URL.RawQuery),
			"error during document search",
			http.StatusBadRequest,
		)
		return
	}

	document, ok := s.parseHandleUpdateDocument(w, r)
	if !ok {
		return
	}

	referenceDocument, err := s.documentRepository.FindOne(r.Context(), document.Id)
	if err != nil {
		if _, ok := err.(flare.DocumentRepositoryError); !ok {
			s.writeError(w, err, "error during document search", http.StatusInternalServerError)
			return
		}
	}

	hasSubscr, err := s.subscriptionRepository.HasSubscription(r.Context(), document.Resource.Id)
	if err != nil {
		s.writeError(
			w,
			err,
			"error during check if the document resource has subscriptions",
			http.StatusInternalServerError,
		)
		return
	}
	if !hasSubscr {
		s.writeResponse(w, &response{Document: transformDocument(document)}, http.StatusOK, nil)
		return
	}

	status := http.StatusOK
	if referenceDocument == nil {
		status = http.StatusCreated
	}

	if ok := s.updateAndTriggerDocumentChange(w, r, document, referenceDocument, status); !ok {
		return
	}

	header := make(http.Header)
	header.Set("Location", s.getDocumentURI(document.Id))
	s.writeResponse(w, &response{Document: transformDocument(document)}, status, header)
}

func (s *Service) parseHandleUpdateDocument(
	w http.ResponseWriter, r *http.Request,
) (*flare.Document, bool) {
	d := json.NewDecoder(r.Body)
	content := make(map[string]interface{})
	if err := d.Decode(&content); err != nil {
		s.writeError(w, err, "invalid body content", http.StatusBadRequest)
		return nil, false
	}

	documentId := s.getDocumentId(r)
	resource, err := s.resourceRepository.FindByURI(r.Context(), documentId)
	if err != nil {
		s.writeError(w, err, "error during resource search", http.StatusInternalServerError)
		return nil, false
	}

	document := &flare.Document{
		Id:               documentId,
		ChangeFieldValue: content[resource.Change.Field],
		Resource:         *resource,
	}
	if err = document.Valid(); err != nil {
		s.writeError(w, err, "document is not valid", http.StatusBadRequest)
		return nil, false
	}

	return document, true
}

func (s *Service) updateAndTriggerDocumentChange(
	w http.ResponseWriter, r *http.Request, document, referenceDocument *flare.Document, status int,
) bool {
	var (
		newer bool
		err   error
	)

	if referenceDocument != nil {
		newer, err = document.Newer(referenceDocument)
		if err != nil {
			s.writeError(
				w,
				err,
				"error during comparing the document with the latest one on datastorage",
				http.StatusBadRequest,
			)
			return false
		}
	}

	if newer || status == http.StatusCreated {
		if err = s.documentRepository.Update(r.Context(), document); err != nil {
			s.writeError(w, err, "error during document persistence", http.StatusInternalServerError)
			return false
		}
	}

	switch status {
	case http.StatusOK, http.StatusCreated:
		err = s.subscriptionTrigger.Update(r.Context(), document)
	}
	if err != nil {
		s.writeError(w, err, "error during document change trigger", http.StatusInternalServerError)
		return false
	}
	return true
}

// HandleDelete receive the request to delete a document.
func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request) {
	document, err := s.documentRepository.FindOne(r.Context(), s.getDocumentId(r))
	if err != nil {
		status := http.StatusInternalServerError
		if errRepo, ok := err.(flare.DocumentRepositoryError); ok && errRepo.NotFound() {
			status = http.StatusNotFound
		}

		s.writeError(w, err, "error during the check if the document exists", status)
		return
	}

	if err = s.documentRepository.Delete(r.Context(), document.Id); err != nil {
		s.writeError(w, err, "error during delete", http.StatusInternalServerError)
		return
	}

	if err = s.subscriptionTrigger.Delete(r.Context(), document); err != nil {
		s.writeError(w, err, "error during document change trigger", http.StatusInternalServerError)
		return
	}

	s.writeResponse(w, nil, http.StatusNoContent, nil)
}

// NewService initialize the service to handle HTTP requests.
func NewService(options ...func(*Service)) (*Service, error) {
	s := &Service{}

	for _, option := range options {
		option(s)
	}

	if s.logger == nil {
		return nil, errors.New("logger not found")
	}

	if s.subscriptionTrigger == nil {
		return nil, errors.New("subscriptionTrigger not found")
	}

	if s.documentRepository == nil {
		return nil, errors.New("documentRepository not found")
	}

	if s.resourceRepository == nil {
		return nil, errors.New("resourceRepository not found")
	}

	if s.subscriptionRepository == nil {
		return nil, errors.New("subscriptionRepository not found")
	}

	if s.getDocumentId == nil {
		return nil, errors.New("getDocumentId not found")
	}

	if s.getDocumentURI == nil {
		return nil, errors.New("getDocumentURI not found")
	}

	s.writeResponse = infraHTTP.WriteResponse(s.logger)
	return s, nil
}

// ServiceDocumentRepository set the repository to access the documents.
func ServiceDocumentRepository(repo flare.DocumentRepositorier) func(*Service) {
	return func(s *Service) { s.documentRepository = repo }
}

// ServiceResourceRepository set the repository to access the resources.
func ServiceResourceRepository(repo flare.ResourceRepositorier) func(*Service) {
	return func(s *Service) { s.resourceRepository = repo }
}

// ServiceSubscriptionRepository set the repository to access the subscriptions.
func ServiceSubscriptionRepository(repo flare.SubscriptionRepositorier) func(*Service) {
	return func(s *Service) { s.subscriptionRepository = repo }
}

// ServiceLogger set the logger.
func ServiceLogger(logger log.Logger) func(*Service) {
	return func(s *Service) { s.logger = logger }
}

// ServiceGetDocumentId set the function to get the document id..
func ServiceGetDocumentId(fn func(*http.Request) string) func(*Service) {
	return func(s *Service) { s.getDocumentId = fn }
}

// ServiceGetDocumentURI set the function to generate the URI of a given Document.
func ServiceGetDocumentURI(fn func(string) string) func(*Service) {
	return func(s *Service) { s.getDocumentURI = fn }
}

// ServiceSubscriptionTrigger set the subscription trigger processor.
func ServiceSubscriptionTrigger(trigger flare.SubscriptionTrigger) func(*Service) {
	return func(s *Service) { s.subscriptionTrigger = trigger }
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/strings/via/pkg"
	"bytes"
	"flag"
	"fmt"
	"github.com/str1ngs/util"
	"github.com/str1ngs/util/console/command"
	"github.com/str1ngs/util/json"
	"log"
	"os"
	"os/exec"
	"path"
)

var (
	root     = flag.String("r", "/", "root directory")
	verbose  = flag.Bool("v", false, "verbose output")
	finstall = flag.Bool("i", false, "install package after build")
	fdebug   = flag.Bool("d", false, "debug output")
)

func main() {
	flag.Parse()
	via.Verbose(*verbose)
	via.Root(*root)
	util.Verbose = *verbose
	via.Debug(*fdebug)
	command.Add("build", build, "build plan")
	command.Add("clean", clean, "clean build dir")
	command.Add("create", create, "create plan from URL")
	command.Add("edit", edit, "calls EDITOR to edit plan")
	command.Add("list", list, "lists files")
	command.Add("install", install, "install package")
	command.Add("lint", lint, "lint plans")
	command.Add("repo", repo, "update repo")
	command.Add("owns", owns, "finds which package owns a file")
	command.Add("sync", sync, "fetch remote repo data")
	command.Add("search", search, "search for plans (currently lists all use grep)")
	command.Add("pack", pack, "package plan")
	command.Add("remove", remove, "remove package")
	command.Add("show", xshow, "prints plan to stdout")
	command.Add("config", config, "prints config to stdout")
	command.Add("elf", elf, "prints elf information to stdout")
	err := command.Run()
	if err != nil {
		log.Fatal(err)
	}
	return
}

func edit() error {
	var (
		editor = os.Getenv("EDITOR")
		arg0   = command.Args()[0]
		p      = path.Join(via.GetConfig().Plans, "config.json")
		err    error
	)
	if arg0 != "config" {
		p, err = via.FindPlanPath(arg0)
		if err != nil {
			return err
		}
	}
	cmd := exec.Command(editor, p)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func create() error {
	for _, arg := range command.Args() {
		err := via.Create(arg)
		if err != nil {
			return err
		}
	}
	return nil
}

func build() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		err = via.BuildSteps(plan)
		if err != nil {
			return err
		}
		if *finstall {
			err := via.Install(plan.Name)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func pack() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		err = via.Package("", plan)
		if err != nil {
			return err
		}
	}
	return nil
}

func list() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		for _, f := range plan.Files {
			fmt.Println(f)
		}
	}
	return nil
}

func install() error {
	return command.ArgsDo(via.Install)
}

func remove() error {
	return command.ArgsDo(via.Remove)
}

func lint() error {
	return via.Lint()
}

func xshow() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			log.Fatal(err)
		}
		buf := new(bytes.Buffer)
		less := exec.Command("less")
		less.Stdin = buf
		less.Stdout = os.Stdout
		less.Stderr = os.Stderr
		err = json.WritePretty(&plan, buf)
		if err != nil {
			fmt.Println(err)
		}
		less.Run()
	}
	return nil
}

func config() error {
	err := json.WritePretty(via.GetConfig(), os.Stdout)
	if err != nil {
		return err
	}
	return nil
}

func clean() error {
	return command.ArgsDo(via.Clean)
}

func elf() error {
	for _, arg := range command.Args() {
		err := via.Readelf(arg)
		if err != nil {
			return err
		}
	}
	return nil
}

func sync() error {
	return via.PlanSync()
}

func owns() error {
	rfiles, err := via.ReadRepoFiles()
	if err != nil {
		return err
	}
	for _, arg := range command.Args() {
		owner := rfiles.Owns(arg)
		if owner == "" {
			fmt.Println(arg+":", "owner not found.")
			continue
		}
		fmt.Println(owner)
	}
	return nil
}

func repo() error {
	return via.RepoCreate()
}

func search() error {
	plans, err := via.NewPlanSlice()
	if err != nil {
		return err
	}
	plans.SortSize().Print()
	return nil
}
<commit_msg>rename PlanSlice<commit_after>package main

import (
	"bitbucket.org/strings/via/pkg"
	"bytes"
	"flag"
	"fmt"
	"github.com/str1ngs/util"
	"github.com/str1ngs/util/console/command"
	"github.com/str1ngs/util/json"
	"log"
	"os"
	"os/exec"
	"path"
)

var (
	root     = flag.String("r", "/", "root directory")
	verbose  = flag.Bool("v", false, "verbose output")
	finstall = flag.Bool("i", false, "install package after build")
	fdebug   = flag.Bool("d", false, "debug output")
)

func main() {
	flag.Parse()
	via.Verbose(*verbose)
	via.Root(*root)
	util.Verbose = *verbose
	via.Debug(*fdebug)
	command.Add("build", build, "build plan")
	command.Add("clean", clean, "clean build dir")
	command.Add("create", create, "create plan from URL")
	command.Add("edit", edit, "calls EDITOR to edit plan")
	command.Add("list", list, "lists files")
	command.Add("install", install, "install package")
	command.Add("lint", lint, "lint plans")
	command.Add("repo", repo, "update repo")
	command.Add("owns", owns, "finds which package owns a file")
	command.Add("sync", sync, "fetch remote repo data")
	command.Add("search", search, "search for plans (currently lists all use grep)")
	command.Add("pack", pack, "package plan")
	command.Add("remove", remove, "remove package")
	command.Add("show", xshow, "prints plan to stdout")
	command.Add("config", config, "prints config to stdout")
	command.Add("elf", elf, "prints elf information to stdout")
	err := command.Run()
	if err != nil {
		log.Fatal(err)
	}
	return
}

func edit() error {
	var (
		editor = os.Getenv("EDITOR")
		arg0   = command.Args()[0]
		p      = path.Join(via.GetConfig().Plans, "config.json")
		err    error
	)
	if arg0 != "config" {
		p, err = via.FindPlanPath(arg0)
		if err != nil {
			return err
		}
	}
	cmd := exec.Command(editor, p)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func create() error {
	for _, arg := range command.Args() {
		err := via.Create(arg)
		if err != nil {
			return err
		}
	}
	return nil
}

func build() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		err = via.BuildSteps(plan)
		if err != nil {
			return err
		}
		if *finstall {
			err := via.Install(plan.Name)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func pack() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		err = via.Package("", plan)
		if err != nil {
			return err
		}
	}
	return nil
}

func list() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			return err
		}
		for _, f := range plan.Files {
			fmt.Println(f)
		}
	}
	return nil
}

func install() error {
	return command.ArgsDo(via.Install)
}

func remove() error {
	return command.ArgsDo(via.Remove)
}

func lint() error {
	return via.Lint()
}

func xshow() error {
	for _, arg := range command.Args() {
		plan, err := via.FindPlan(arg)
		if err != nil {
			log.Fatal(err)
		}
		buf := new(bytes.Buffer)
		less := exec.Command("less")
		less.Stdin = buf
		less.Stdout = os.Stdout
		less.Stderr = os.Stderr
		err = json.WritePretty(&plan, buf)
		if err != nil {
			fmt.Println(err)
		}
		less.Run()
	}
	return nil
}

func config() error {
	err := json.WritePretty(via.GetConfig(), os.Stdout)
	if err != nil {
		return err
	}
	return nil
}

func clean() error {
	return command.ArgsDo(via.Clean)
}

func elf() error {
	for _, arg := range command.Args() {
		err := via.Readelf(arg)
		if err != nil {
			return err
		}
	}
	return nil
}

func sync() error {
	return via.PlanSync()
}

func owns() error {
	rfiles, err := via.ReadRepoFiles()
	if err != nil {
		return err
	}
	for _, arg := range command.Args() {
		owner := rfiles.Owns(arg)
		if owner == "" {
			fmt.Println(arg+":", "owner not found.")
			continue
		}
		fmt.Println(owner)
	}
	return nil
}

func repo() error {
	return via.RepoCreate()
}

func search() error {
	plans, err := via.GetPlans()
	if err != nil {
		return err
	}
	plans.SortSize().Print()
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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

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

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

package job_test

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/venicegeo/pzsvc-sdk-go/job"
)

func ExampleFoo() {
	var msg job.InputMsg
	fmt.Println(msg)
}

func TestInputMsg(t *testing.T) {
	in := `
    {
      "source": {
        "bucket": "Foo",
        "key": "Bar"
      },
      "function": "Baz",
			"destination": {
				"bucket": "Out",
				"key": "File"
			}
    }`

	b := []byte(in)

	var msg job.InputMsg
	if err := json.Unmarshal(b, &msg); err != nil {
		t.Error("Error parsing InputMsg")
	}
	if msg.Source.Bucket != "Foo" {
		t.Error(msg.Source.Bucket, "!= `Foo`")
	}
	if msg.Source.Key != "Bar" {
		t.Error(msg.Source.Key, "!= `Bar`")
	}
	if *msg.Function != "Baz" {
		t.Error(msg.Function, "!= `Baz`")
	}
	if msg.Destination.Bucket != "Out" {
		t.Error(msg.Destination.Bucket, "!= `Out`")
	}
	if msg.Destination.Key != "File" {
		t.Error(msg.Destination.Key, "!= `File`")
	}
}

func TestOutputMsg(t *testing.T) {
	out := `
    {
      "input": {
        "source": {
          "bucket": "Foo",
          "key": "Bar"
        },
        "function": "Baz"
      },
      "started_at": "2015-12-11T01:31:26.784569058Z",
      "finished_at": "2015-12-11T01:31:26.784569058Z",
      "status": "submitted",
      "response": {"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}
    }`

	b := []byte(out)

	var msg job.OutputMsg
	if err := json.Unmarshal(b, &msg); err != nil {
		t.Error("Error parsing OutputMsg")
	}
	if msg.Input.Source.Bucket != "Foo" {
		t.Error(msg.Input.Source.Bucket, "!= `Foo`")
	}
	if msg.Input.Source.Key != "Bar" {
		t.Error(msg.Input.Source.Key, "!= `Bar`")
	}
	if *msg.Input.Function != "Baz" {
		t.Error(msg.Input.Function, "!= `Baz`")
	}
	// if msg.StartedAt != "2015-12-11T01:31:26.784569058Z" {
	// 	t.Error(msg.StartedAt, "!= `2015-12-11T01:31:26.784569058Z`")
	// }
	// if msg.FinishedAt != "2015-12-11T01:31:26.784569058Z" {
	// 	t.Error(msg.FinishedAt, "!= `2015-12-11T01:31:26.784569058Z`")
	// }
	// if msg.Status != "submitted" {
	// 	t.Error(msg.Status, "!= `submitted`")
	// }
	// if msg.Response != `{"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}` {
	// 	t.Error(msg.Response, `!= {"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}`)
	// }
}

func TestStatusTypes(t *testing.T) {
	if job.Submitted.String() != "submitted" {
		t.Error(job.Submitted.String(), "!= `submitted`")
	}
	if job.Running.String() != "running" {
		t.Error(job.Running.String(), "!= `running`")
	}
	if job.Success.String() != "success" {
		t.Error(job.Success.String(), "!= `success`")
	}
	if job.Cancelled.String() != "cancelled" {
		t.Error(job.Cancelled.String(), "!= `cancelled`")
	}
	if job.Error.String() != "error" {
		t.Error(job.Error.String(), "!= `error`")
	}
	if job.Fail.String() != "fail" {
		t.Error(job.Fail.String(), "!= `fail`")
	}
}
<commit_msg>Examples may have to name an existing function, with which the example is associated<commit_after>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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

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

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

package job_test

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/venicegeo/pzsvc-sdk-go/job"
)

func ExampleUpdate() {
	var msg job.InputMsg
	fmt.Println(msg)
}

func TestInputMsg(t *testing.T) {
	in := `
    {
      "source": {
        "bucket": "Foo",
        "key": "Bar"
      },
      "function": "Baz",
			"destination": {
				"bucket": "Out",
				"key": "File"
			}
    }`

	b := []byte(in)

	var msg job.InputMsg
	if err := json.Unmarshal(b, &msg); err != nil {
		t.Error("Error parsing InputMsg")
	}
	if msg.Source.Bucket != "Foo" {
		t.Error(msg.Source.Bucket, "!= `Foo`")
	}
	if msg.Source.Key != "Bar" {
		t.Error(msg.Source.Key, "!= `Bar`")
	}
	if *msg.Function != "Baz" {
		t.Error(msg.Function, "!= `Baz`")
	}
	if msg.Destination.Bucket != "Out" {
		t.Error(msg.Destination.Bucket, "!= `Out`")
	}
	if msg.Destination.Key != "File" {
		t.Error(msg.Destination.Key, "!= `File`")
	}
}

func TestOutputMsg(t *testing.T) {
	out := `
    {
      "input": {
        "source": {
          "bucket": "Foo",
          "key": "Bar"
        },
        "function": "Baz"
      },
      "started_at": "2015-12-11T01:31:26.784569058Z",
      "finished_at": "2015-12-11T01:31:26.784569058Z",
      "status": "submitted",
      "response": {"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}
    }`

	b := []byte(out)

	var msg job.OutputMsg
	if err := json.Unmarshal(b, &msg); err != nil {
		t.Error("Error parsing OutputMsg")
	}
	if msg.Input.Source.Bucket != "Foo" {
		t.Error(msg.Input.Source.Bucket, "!= `Foo`")
	}
	if msg.Input.Source.Key != "Bar" {
		t.Error(msg.Input.Source.Key, "!= `Bar`")
	}
	if *msg.Input.Function != "Baz" {
		t.Error(msg.Input.Function, "!= `Baz`")
	}
	// if msg.StartedAt != "2015-12-11T01:31:26.784569058Z" {
	// 	t.Error(msg.StartedAt, "!= `2015-12-11T01:31:26.784569058Z`")
	// }
	// if msg.FinishedAt != "2015-12-11T01:31:26.784569058Z" {
	// 	t.Error(msg.FinishedAt, "!= `2015-12-11T01:31:26.784569058Z`")
	// }
	// if msg.Status != "submitted" {
	// 	t.Error(msg.Status, "!= `submitted`")
	// }
	// if msg.Response != `{"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}` {
	// 	t.Error(msg.Response, `!= {"filename":"download_file.laz","pdal_version":"1.1.0 (git-version: 0c36aa)"}`)
	// }
}

func TestStatusTypes(t *testing.T) {
	if job.Submitted.String() != "submitted" {
		t.Error(job.Submitted.String(), "!= `submitted`")
	}
	if job.Running.String() != "running" {
		t.Error(job.Running.String(), "!= `running`")
	}
	if job.Success.String() != "success" {
		t.Error(job.Success.String(), "!= `success`")
	}
	if job.Cancelled.String() != "cancelled" {
		t.Error(job.Cancelled.String(), "!= `cancelled`")
	}
	if job.Error.String() != "error" {
		t.Error(job.Error.String(), "!= `error`")
	}
	if job.Fail.String() != "fail" {
		t.Error(job.Fail.String(), "!= `fail`")
	}
}
<|endoftext|>