<commit_before>package shared_test

import (
	"runtime"
	"time"

	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/commandfakes"
	. "code.cloudfoundry.org/cli/command/v3/shared"
	"code.cloudfoundry.org/cli/util/ui"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("New Clients", func() {
	var (
		binaryName string
		fakeConfig *commandfakes.FakeConfig
		testUI     *ui.UI
	)

	BeforeEach(func() {
		binaryName = "faceman"
		fakeConfig = new(commandfakes.FakeConfig)
		fakeConfig.BinaryNameReturns(binaryName)

		testUI = ui.NewTestUI(NewBuffer(), NewBuffer(), NewBuffer())
	})

	Context("when the api endpoint is not set", func() {
		It("returns the NoAPISetError", func() {
			_, err := NewClients(fakeConfig, testUI)
			Expect(err).To(MatchError(command.NoAPISetError{
				BinaryName: binaryName,
			}))
		})
	})

	Context("when the api does not exist", func() {
		BeforeEach(func() {
			fakeConfig.TargetReturns("http://4012493825site.com")
		})

		It("returns the ClientTargetError", func() {
			_, err := NewClients(fakeConfig, testUI)
			Expect(err.Error()).To(MatchRegexp("Note that this command requires CF API version 3.0.0+."))
		})
	})

	Context("when the DialTimeout is set", func() {
		BeforeEach(func() {
			if runtime.GOOS == "windows" {
				Skip("due to timing issues on windows")
			}
			fakeConfig.TargetReturns("https://potato.bananapants11122.co.uk")
			fakeConfig.DialTimeoutReturns(time.Nanosecond)
		})

		It("passes the value to the target", func() {
			_, err := NewClients(fakeConfig, testUI)
			if e, ok := err.(ClientTargetError); ok {
				Expect(e.Message).To(Equal("Get https://potato.bananapants11122.co.uk: dial tcp: i/o timeout"))
			} else {
				Fail("Expected err to be type ClientTargetError")
			}
		})
	})
})
<commit_msg>fix flakyness<commit_after>package shared_test

import (
	"runtime"
	"time"

	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/commandfakes"
	. "code.cloudfoundry.org/cli/command/v3/shared"
	"code.cloudfoundry.org/cli/util/ui"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("New Clients", func() {
	var (
		binaryName string
		fakeConfig *commandfakes.FakeConfig
		testUI     *ui.UI
	)

	BeforeEach(func() {
		binaryName = "faceman"
		fakeConfig = new(commandfakes.FakeConfig)
		fakeConfig.BinaryNameReturns(binaryName)

		testUI = ui.NewTestUI(NewBuffer(), NewBuffer(), NewBuffer())
	})

	Context("when the api endpoint is not set", func() {
		It("returns the NoAPISetError", func() {
			_, err := NewClients(fakeConfig, testUI)
			Expect(err).To(MatchError(command.NoAPISetError{
				BinaryName: binaryName,
			}))
		})
	})

	Context("when the api does not exist", func() {
		BeforeEach(func() {
			fakeConfig.TargetReturns("http://4012493825site.com")
		})

		It("returns the ClientTargetError", func() {
			_, err := NewClients(fakeConfig, testUI)
			Expect(err.Error()).To(MatchRegexp("Note that this command requires CF API version 3.0.0+."))
		})
	})

	Context("when the DialTimeout is set", func() {
		BeforeEach(func() {
			if runtime.GOOS == "windows" {
				Skip("due to timing issues on windows")
			}
			fakeConfig.TargetReturns("https://potato.bananapants11122.co.uk")
			fakeConfig.DialTimeoutReturns(time.Nanosecond)
		})

		It("passes the value to the target", func() {
			_, err := NewClients(fakeConfig, testUI)
			if e, ok := err.(ClientTargetError); ok {
				Expect(e.Message).To(MatchRegexp("https://potato.bananapants11122.co.uk: dial tcp.*i/o timeout"))
			} else {
				Fail("Expected err to be type ClientTargetError")
			}
		})
	})
})
<|endoftext|>


<commit_before>package graphdriver // import "github.com/docker/docker/daemon/graphdriver"

import "sync"

type minfo struct {
	check bool
	count int
}

// RefCounter is a generic counter for use by graphdriver Get/Put calls
type RefCounter struct {
	counts  map[string]*minfo
	mu      sync.Mutex
	checker Checker
}

// NewRefCounter returns a new RefCounter
func NewRefCounter(c Checker) *RefCounter {
	return &RefCounter{
		checker: c,
		counts:  make(map[string]*minfo),
	}
}

// Increment increases the ref count for the given id and returns the current count
func (c *RefCounter) Increment(path string) int {
	return c.incdec(path, func(minfo *minfo) {
		minfo.count++
	})
}

// Decrement decreases the ref count for the given id and returns the current count
func (c *RefCounter) Decrement(path string) int {
	return c.incdec(path, func(minfo *minfo) {
		minfo.count--
	})
}

func (c *RefCounter) incdec(path string, infoOp func(minfo *minfo)) int {
	c.mu.Lock()
	m := c.counts[path]
	if m == nil {
		m = &minfo{}
		c.counts[path] = m
	}
	// if we are checking this path for the first time check to make sure
	// if it was already mounted on the system and make sure we have a correct ref
	// count if it is mounted as it is in use.
	if !m.check {
		m.check = true
		if c.checker.IsMounted(path) {
			m.count++
		}
	}
	infoOp(m)
	count := m.count
	c.mu.Unlock()
	return count
}
<commit_msg>graphdriver: Fix RefCounter memory leak<commit_after>package graphdriver // import "github.com/docker/docker/daemon/graphdriver"

import "sync"

type minfo struct {
	check bool
	count int
}

// RefCounter is a generic counter for use by graphdriver Get/Put calls
type RefCounter struct {
	counts  map[string]*minfo
	mu      sync.Mutex
	checker Checker
}

// NewRefCounter returns a new RefCounter
func NewRefCounter(c Checker) *RefCounter {
	return &RefCounter{
		checker: c,
		counts:  make(map[string]*minfo),
	}
}

// Increment increases the ref count for the given id and returns the current count
func (c *RefCounter) Increment(path string) int {
	return c.incdec(path, func(minfo *minfo) {
		minfo.count++
	})
}

// Decrement decreases the ref count for the given id and returns the current count
func (c *RefCounter) Decrement(path string) int {
	return c.incdec(path, func(minfo *minfo) {
		minfo.count--
	})
}

func (c *RefCounter) incdec(path string, infoOp func(minfo *minfo)) int {
	c.mu.Lock()
	m := c.counts[path]
	if m == nil {
		m = &minfo{}
		c.counts[path] = m
	}
	// if we are checking this path for the first time check to make sure
	// if it was already mounted on the system and make sure we have a correct ref
	// count if it is mounted as it is in use.
	if !m.check {
		m.check = true
		if c.checker.IsMounted(path) {
			m.count++
		}
	}
	infoOp(m)
	count := m.count
	if count <= 0 {
		delete(c.counts, path)
	}
	c.mu.Unlock()
	return count
}
<|endoftext|>


<commit_before>package git

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/stretchr/testify/assert"
)

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestNotAGitFolder(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.Error(Pipe{}.Run(ctx))
}

func TestSingleCommit(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit1")
	gitTag(t, "v0.0.1")
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.1", ctx.Git.CurrentTag)
}

func TestNewRepository(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.Error(Pipe{}.Run(ctx))
}

func TestInvalidTagFormat(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit2")
	gitTag(t, "sadasd")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	assert.EqualError(Pipe{}.Run(ctx), "sadasd is not in a valid version format")
	assert.Equal("sadasd", ctx.Git.CurrentTag)
}

func TestDirty(t *testing.T) {
	var assert = assert.New(t)
	folder, back := createAndChdir(t)
	defer back()
	gitInit(t)
	dummy, err := os.Create(filepath.Join(folder, "dummy"))
	assert.NoError(err)
	gitAdd(t)
	gitCommit(t, "commit2")
	gitTag(t, "v0.0.1")
	assert.NoError(ioutil.WriteFile(dummy.Name(), []byte("lorem ipsum"), 0644))
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	err = Pipe{}.Run(ctx)
	assert.Error(err)
	assert.Contains(err.Error(), "git is currently in a dirty state:")
}

func TestTagIsNotLastCommit(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit3")
	gitTag(t, "v0.0.1")
	gitCommit(t, "commit4")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	err := Pipe{}.Run(ctx)
	assert.Error(err)
	assert.Contains(err.Error(), "git tag v0.0.1 was not made against commit")
}

func TestNoValidate(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitAdd(t)
	gitCommit(t, "commit5")
	gitTag(t, "v0.0.1")
	gitCommit(t, "commit6")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: false,
	}
	assert.NoError(Pipe{}.Run(ctx))
}

func TestChangelog(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "first")
	gitTag(t, "v0.0.1")
	gitCommit(t, "added feature 1")
	gitCommit(t, "fixed bug 2")
	gitTag(t, "v0.0.2")
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.2", ctx.Git.CurrentTag)
	assert.Contains(ctx.ReleaseNotes, "## Changelog")
	assert.Contains(ctx.ReleaseNotes, "added feature 1")
	assert.Contains(ctx.ReleaseNotes, "fixed bug 2")
}

func TestCustomReleaseNotes(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "first")
	gitTag(t, "v0.0.1")
	var ctx = &context.Context{
		Config:       config.Project{},
		ReleaseNotes: "custom",
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.1", ctx.Git.CurrentTag)
	assert.Equal(ctx.ReleaseNotes, "custom")
}

//
// helper functions
//

func createAndChdir(t *testing.T) (current string, back func()) {
	var assert = assert.New(t)
	folder, err := ioutil.TempDir("", "goreleasertest")
	assert.NoError(err)
	previous, err := os.Getwd()
	assert.NoError(err)
	assert.NoError(os.Chdir(folder))
	return folder, func() {
		assert.NoError(os.Chdir(previous))
	}
}

func gitInit(t *testing.T) {
	var assert = assert.New(t)
	out, err := git("init")
	assert.NoError(err)
	assert.Contains(out, "Initialized empty Git repository")
}

func gitCommit(t *testing.T, msg string) {
	var assert = assert.New(t)
	out, err := fakeGit("commit", "--allow-empty", "-m", msg)
	assert.NoError(err)
	assert.Contains(out, "master", msg)
}

func gitTag(t *testing.T, tag string) {
	var assert = assert.New(t)
	out, err := fakeGit("tag", tag)
	assert.NoError(err)
	assert.Empty(out)
}

func gitAdd(t *testing.T) {
	var assert = assert.New(t)
	out, err := git("add", "-A")
	assert.NoError(err)
	assert.Empty(out)
}

func fakeGit(args ...string) (string, error) {
	var allArgs = []string{
		"-c",
		"user.name='GoReleaser'",
		"-c",
		"user.email='test@goreleaser.github.com'",
	}
	allArgs = append(allArgs, args...)
	return git(allArgs...)
}
<commit_msg>added more tests to the git pipe<commit_after>package git

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/stretchr/testify/assert"
)

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestNotAGitFolder(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.Error(Pipe{}.Run(ctx))
}

func TestSingleCommit(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit1")
	gitTag(t, "v0.0.1")
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.1", ctx.Git.CurrentTag)
}

func TestNewRepository(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.Error(Pipe{}.Run(ctx))
}

func TestInvalidTagFormat(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit2")
	gitTag(t, "sadasd")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	assert.EqualError(Pipe{}.Run(ctx), "sadasd is not in a valid version format")
	assert.Equal("sadasd", ctx.Git.CurrentTag)
}

func TestDirty(t *testing.T) {
	var assert = assert.New(t)
	folder, back := createAndChdir(t)
	defer back()
	gitInit(t)
	dummy, err := os.Create(filepath.Join(folder, "dummy"))
	assert.NoError(err)
	gitAdd(t)
	gitCommit(t, "commit2")
	gitTag(t, "v0.0.1")
	assert.NoError(ioutil.WriteFile(dummy.Name(), []byte("lorem ipsum"), 0644))
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	err = Pipe{}.Run(ctx)
	assert.Error(err)
	assert.Contains(err.Error(), "git is currently in a dirty state:")
}

func TestTagIsNotLastCommit(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit3")
	gitTag(t, "v0.0.1")
	gitCommit(t, "commit4")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	err := Pipe{}.Run(ctx)
	assert.Error(err)
	assert.Contains(err.Error(), "git tag v0.0.1 was not made against commit")
}

func TestValidState(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "commit3")
	gitTag(t, "v0.0.1")
	gitCommit(t, "commit4")
	gitTag(t, "v0.0.2")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: true,
	}
	assert.NoError(Pipe{}.Run(ctx))
}

func TestNoValidate(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitAdd(t)
	gitCommit(t, "commit5")
	gitTag(t, "v0.0.1")
	gitCommit(t, "commit6")
	var ctx = &context.Context{
		Config:   config.Project{},
		Validate: false,
	}
	assert.NoError(Pipe{}.Run(ctx))
}

func TestChangelog(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "first")
	gitTag(t, "v0.0.1")
	gitCommit(t, "added feature 1")
	gitCommit(t, "fixed bug 2")
	gitTag(t, "v0.0.2")
	var ctx = &context.Context{
		Config: config.Project{},
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.2", ctx.Git.CurrentTag)
	assert.Contains(ctx.ReleaseNotes, "## Changelog")
	assert.Contains(ctx.ReleaseNotes, "added feature 1")
	assert.Contains(ctx.ReleaseNotes, "fixed bug 2")
}

func TestCustomReleaseNotes(t *testing.T) {
	var assert = assert.New(t)
	_, back := createAndChdir(t)
	defer back()
	gitInit(t)
	gitCommit(t, "first")
	gitTag(t, "v0.0.1")
	var ctx = &context.Context{
		Config:       config.Project{},
		ReleaseNotes: "custom",
	}
	assert.NoError(Pipe{}.Run(ctx))
	assert.Equal("v0.0.1", ctx.Git.CurrentTag)
	assert.Equal(ctx.ReleaseNotes, "custom")
}

//
// helper functions
//

func createAndChdir(t *testing.T) (current string, back func()) {
	var assert = assert.New(t)
	folder, err := ioutil.TempDir("", "goreleasertest")
	assert.NoError(err)
	previous, err := os.Getwd()
	assert.NoError(err)
	assert.NoError(os.Chdir(folder))
	return folder, func() {
		assert.NoError(os.Chdir(previous))
	}
}

func gitInit(t *testing.T) {
	var assert = assert.New(t)
	out, err := git("init")
	assert.NoError(err)
	assert.Contains(out, "Initialized empty Git repository")
}

func gitCommit(t *testing.T, msg string) {
	var assert = assert.New(t)
	out, err := fakeGit("commit", "--allow-empty", "-m", msg)
	assert.NoError(err)
	assert.Contains(out, "master", msg)
}

func gitTag(t *testing.T, tag string) {
	var assert = assert.New(t)
	out, err := fakeGit("tag", tag)
	assert.NoError(err)
	assert.Empty(out)
}

func gitAdd(t *testing.T) {
	var assert = assert.New(t)
	out, err := git("add", "-A")
	assert.NoError(err)
	assert.Empty(out)
}

func fakeGit(args ...string) (string, error) {
	var allArgs = []string{
		"-c",
		"user.name='GoReleaser'",
		"-c",
		"user.email='test@goreleaser.github.com'",
	}
	allArgs = append(allArgs, args...)
	return git(allArgs...)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Heptio 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 backup

import (
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"

	v1 "github.com/heptio/velero/pkg/apis/velero/v1"
	"github.com/heptio/velero/pkg/kuberesource"
	"github.com/heptio/velero/pkg/util/collections"
)

// podAction implements ItemAction.
type podAction struct {
	log logrus.FieldLogger
}

// NewPodAction creates a new ItemAction for pods.
func NewPodAction(logger logrus.FieldLogger) ItemAction {
	return &podAction{log: logger}
}

// AppliesTo returns a ResourceSelector that applies only to pods.
func (a *podAction) AppliesTo() (ResourceSelector, error) {
	return ResourceSelector{
		IncludedResources: []string{"pods"},
	}, nil
}

// Execute scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a
// ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by
// the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up too.
func (a *podAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []ResourceIdentifier, error) {
	a.log.Info("Executing podAction")
	defer a.log.Info("Done executing podAction")

	pod := item.UnstructuredContent()
	if !collections.Exists(pod, "spec.volumes") {
		a.log.Info("pod has no volumes")
		return item, nil, nil
	}

	metadata, err := meta.Accessor(item)
	if err != nil {
		return nil, nil, errors.Wrap(err, "unable to access pod metadata")
	}

	volumes, err := collections.GetSlice(pod, "spec.volumes")
	if err != nil {
		return nil, nil, errors.WithMessage(err, "error getting spec.volumes")
	}

	var errs []error
	var additionalItems []ResourceIdentifier

	for i := range volumes {
		volume, ok := volumes[i].(map[string]interface{})
		if !ok {
			errs = append(errs, errors.Errorf("unexpected type %T", volumes[i]))
			continue
		}
		if !collections.Exists(volume, "persistentVolumeClaim.claimName") {
			continue
		}

		claimName, err := collections.GetString(volume, "persistentVolumeClaim.claimName")
		if err != nil {
			errs = append(errs, err)
			continue
		}

		a.log.Infof("Adding pvc %s to additionalItems", claimName)

		additionalItems = append(additionalItems, ResourceIdentifier{
			GroupResource: kuberesource.PersistentVolumeClaims,
			Namespace:     metadata.GetNamespace(),
			Name:          claimName,
		})
	}

	return item, additionalItems, nil
}
<commit_msg>pkg/backup: remove usage of pkg/util/collections<commit_after>/*
Copyright 2017 Heptio 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 backup

import (
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	corev1api "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"

	v1 "github.com/heptio/velero/pkg/apis/velero/v1"
	"github.com/heptio/velero/pkg/kuberesource"
)

// podAction implements ItemAction.
type podAction struct {
	log logrus.FieldLogger
}

// NewPodAction creates a new ItemAction for pods.
func NewPodAction(logger logrus.FieldLogger) ItemAction {
	return &podAction{log: logger}
}

// AppliesTo returns a ResourceSelector that applies only to pods.
func (a *podAction) AppliesTo() (ResourceSelector, error) {
	return ResourceSelector{
		IncludedResources: []string{"pods"},
	}, nil
}

// Execute scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a
// ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by
// the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up too.
func (a *podAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []ResourceIdentifier, error) {
	a.log.Info("Executing podAction")
	defer a.log.Info("Done executing podAction")

	pod := new(corev1api.Pod)
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil {
		return nil, nil, errors.WithStack(err)
	}

	if len(pod.Spec.Volumes) == 0 {
		a.log.Info("pod has no volumes")
		return item, nil, nil
	}

	var additionalItems []ResourceIdentifier
	for _, volume := range pod.Spec.Volumes {
		if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" {
			a.log.Infof("Adding pvc %s to additionalItems", volume.PersistentVolumeClaim.ClaimName)

			additionalItems = append(additionalItems, ResourceIdentifier{
				GroupResource: kuberesource.PersistentVolumeClaims,
				Namespace:     pod.Namespace,
				Name:          volume.PersistentVolumeClaim.ClaimName,
			})
		}
	}

	return item, additionalItems, nil
}
<|endoftext|>


<commit_before>package builder

import (
	"github.com/fsouza/go-dockerclient"
	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/source-to-image/pkg/sti"
)

// STIBuilder performs an STI build given the build object
type STIBuilder struct {
	dockerClient DockerClient
	dockerSocket string
	authPresent  bool
	auth         docker.AuthConfiguration
	build        *api.Build
}

// NewSTIBuilder creates a new STIBuilder instance
func NewSTIBuilder(client DockerClient, dockerSocket string, authCfg docker.AuthConfiguration, authPresent bool, build *api.Build) *STIBuilder {
	return &STIBuilder{
		dockerClient: client,
		dockerSocket: dockerSocket,
		authPresent:  authPresent,
		auth:         authCfg,
		build:        build,
	}
}

// Build executes the STI build
func (s *STIBuilder) Build() error {
	request := &sti.STIRequest{
		BaseImage:    s.build.Parameters.Strategy.STIStrategy.Image,
		DockerSocket: s.dockerSocket,
		Source:       s.build.Parameters.Source.Git.URI,
		Tag:          imageTag(s.build),
		ScriptsUrl:   s.build.Parameters.Strategy.STIStrategy.Scripts,
		Environment:  getBuildEnvVars(s.build),
		Clean:        s.build.Parameters.Strategy.STIStrategy.Clean,
	}
	if s.build.Parameters.Revision != nil && s.build.Parameters.Revision.Git != nil &&
		s.build.Parameters.Revision.Git.Commit != "" {
		request.Ref = s.build.Parameters.Revision.Git.Commit
	} else if s.build.Parameters.Source.Git.Ref != "" {
		request.Ref = s.build.Parameters.Source.Git.Ref
	}
	builder, err := sti.NewBuilder(request)
	if err != nil {
		return err
	}
	if _, err = builder.Build(); err != nil {
		return err
	}
	if s.build.Parameters.Output.Registry != "" || s.authPresent {
		return pushImage(s.dockerClient, imageTag(s.build), s.auth)
	}
	return nil
}
<commit_msg>Updated STI builder according to latest STI version<commit_after>package builder

import (
	"github.com/fsouza/go-dockerclient"
	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/source-to-image/pkg/sti"
)

// STIBuilder performs an STI build given the build object
type STIBuilder struct {
	dockerClient DockerClient
	dockerSocket string
	authPresent  bool
	auth         docker.AuthConfiguration
	build        *api.Build
}

// NewSTIBuilder creates a new STIBuilder instance
func NewSTIBuilder(client DockerClient, dockerSocket string, authCfg docker.AuthConfiguration, authPresent bool, build *api.Build) *STIBuilder {
	return &STIBuilder{
		dockerClient: client,
		dockerSocket: dockerSocket,
		authPresent:  authPresent,
		auth:         authCfg,
		build:        build,
	}
}

// Build executes the STI build
func (s *STIBuilder) Build() error {
	request := &sti.Request{
		BaseImage:    s.build.Parameters.Strategy.STIStrategy.Image,
		DockerSocket: s.dockerSocket,
		Source:       s.build.Parameters.Source.Git.URI,
		Tag:          imageTag(s.build),
		ScriptsURL:   s.build.Parameters.Strategy.STIStrategy.Scripts,
		Environment:  getBuildEnvVars(s.build),
		Clean:        s.build.Parameters.Strategy.STIStrategy.Clean,
	}
	if s.build.Parameters.Revision != nil && s.build.Parameters.Revision.Git != nil &&
		s.build.Parameters.Revision.Git.Commit != "" {
		request.Ref = s.build.Parameters.Revision.Git.Commit
	} else if s.build.Parameters.Source.Git.Ref != "" {
		request.Ref = s.build.Parameters.Source.Git.Ref
	}
	builder, err := sti.NewBuilder(request)
	if err != nil {
		return err
	}
	if _, err = builder.Build(); err != nil {
		return err
	}
	if s.build.Parameters.Output.Registry != "" || s.authPresent {
		return pushImage(s.dockerClient, imageTag(s.build), s.auth)
	}
	return nil
}
<|endoftext|>


<commit_before>package isolation

import (
	"io/ioutil"
	"os/exec"
	"path"
	"strconv"
	"strings"
)

// CPUSet describes a cgroup cpuset with core ids and numa (memory) nodes.
type CPUSet struct {
	name string
	cpus Set
	mems Set
}

// NewCPUSet creates an instance of input data.
func NewCPUSet(name string, cpus Set, mems Set) Isolation {
	return &CPUSet{
		name: name,
		cpus: cpus,
		mems: mems,
	}
}

// Prefix returns the command prefix to run with this isolation mechanism.
func (cpuSet *CPUSet) Prefix() string {
	return "cgexec -g cpuset:" + cpuSet.name
}

// Clean removes specified cgroup.
func (cpuSet *CPUSet) Clean() error {
	cmd := exec.Command("cgdelete", "-g", "cpuset:"+cpuSet.name)
	err := cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

// Create specified cgroup.
func (cpuSet *CPUSet) Create() error {
	// 1.a Create cpuset cgroup.
	cmd := exec.Command("cgcreate", "-g", "cpuset:"+cpuSet.name)
	err := cmd.Run()
	if err != nil {
		return err
	}

	// 1.b Set cpu nodes for cgroup cpus. This is a temporary change.
	// After we discover platform, we change accordingly.
	cpus := []string{}
	for cpu := range cpuSet.cpus {
		cpus = append(cpus, strconv.Itoa(cpu))
	}

	cmd = exec.Command("cgset", "-r", "cpuset.cpus="+strings.Join(cpus, ","), cpuSet.name)
	err = cmd.Run()
	if err != nil {
		return err
	}

	mems := []string{}
	for mem := range cpuSet.mems {
		mems = append(mems, strconv.Itoa(mem))
	}

	cmd = exec.Command("cgset", "-r", "cpuset.mems="+strings.Join(mems, ","), cpuSet.name)

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

	return nil
}

// Isolate creates specified cgroup.
func (cpuSet *CPUSet) Isolate(PID int) error {
	// Set PID to cgroups
	strPID := strconv.Itoa(PID)
	d := []byte(strPID)
	err := ioutil.WriteFile(path.Join("/sys/fs/cgroup/cpuset", cpuSet.name, "/tasks"), d, 0644)

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Terminate sleep process used in test.go for immediate clean up<commit_after>package isolation

import (
	"io/ioutil"
	"os/exec"
	"path"
	"strconv"
	"strings"
)

// CPUSet describes a cgroup cpuset with core ids and numa (memory) nodes.
type CPUSet struct {
	name string
	cpus Set
	mems Set
}

// NewCPUSet creates an instance of input data.
func NewCPUSet(name string, cpus Set, mems Set) Isolation {
	return &CPUSet{
		name: name,
		cpus: cpus,
		mems: mems,
	}
}

// Prefix returns the command prefix to run with this isolation mechanism.
func (cpuSet *CPUSet) Prefix() string {
	return "cgexec -g cpuset:" + cpuSet.name
}

// Clean removes specified cgroup.
func (cpuSet *CPUSet) Clean() error {
	cmd := exec.Command("cgdelete", "-g", "cpuset:"+cpuSet.name)
	err := cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

// Create specified cgroup.
func (cpuSet *CPUSet) Create() error {
	// 1.a Create cpuset cgroup.
	cmd := exec.Command("cgcreate", "-g", "cpuset:"+cpuSet.name)
	err := cmd.Run()
	if err != nil {
		return err
	}

	// 1.b Set cpu nodes for cgroup cpus. This is a temporary change.
	// After we discover platform, we change accordingly.
	cpus := []string{}
	for cpu := range cpuSet.cpus {
		cpus = append(cpus, strconv.Itoa(cpu))
	}

	cmd = exec.Command("cgset", "-r", "cpuset.cpus="+strings.Join(cpus, ","), cpuSet.name)
	err = cmd.Run()
	if err != nil {
		return err
	}

	mems := []string{}
	for mem := range cpuSet.mems {
		mems = append(mems, strconv.Itoa(mem))
	}

	cmd = exec.Command("cgset", "-r", "cpuset.mems="+strings.Join(mems, ","), cpuSet.name)

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

	return nil
}

// Isolate creates specified cgroup.
func (cpuSet *CPUSet) Isolate(PID int) error {
	// Set PID to cgroups
	strPID := strconv.Itoa(PID)
	d := []byte(strPID)
	err := ioutil.WriteFile(path.Join("/sys/fs/cgroup/cpuset", cpuSet.name, "/tasks"), d, 0644)

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package workers

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	htmlTemplate "html/template"
	textTemplate "text/template"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/gomail"
)

func init() {
	jobs.AddWorker("sendmail", &jobs.WorkerConfig{
		Concurrency:  4,
		MaxExecCount: 3,
		Timeout:      10 * time.Second,
		WorkerFunc:   SendMail,
	})
}

// MailAddress contains the name and mail of a mail recipient.
type MailAddress struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

// MailOptions should be used as the options of a mail with manually defined
// content: body and body content-type. It is used as the input of the
// "sendmail" worker.
type MailOptions struct {
	Mode           string                `json:"mode"`
	From           *MailAddress          `json:"from"`
	To             []*MailAddress        `json:"to"`
	Subject        string                `json:"subject"`
	Dialer         *gomail.DialerOptions `json:"dialer,omitempty"`
	Date           *time.Time            `json:"date"`
	Parts          []*MailPart           `json:"parts"`
	TemplateValues interface{}           `json:"template_values"`
}

// MailPart represent a part of the content of the mail. It has a type
// specifying the content type of the part, and a body.
type MailPart struct {
	Type string `json:"body_type"`
	Body string `json:"body"`
}

// SendMail is the sendmail worker function.
func SendMail(ctx context.Context, m *jobs.Message) error {
	opts := &MailOptions{}
	err := m.Unmarshal(&opts)
	if err != nil {
		return err
	}
	domain := ctx.Value(jobs.ContextDomainKey).(string)
	switch opts.Mode {
	case "noreply":
		toAddr, err := addressFromDomain(domain)
		if err != nil {
			return err
		}
		opts.To = []*MailAddress{toAddr}
		opts.From = &MailAddress{Email: "noreply@" + domain}
	case "from":
		fromAddr, err := addressFromDomain(domain)
		if err != nil {
			return err
		}
		opts.From = fromAddr
	default:
		return fmt.Errorf("Mail sent with unknown mode %s", opts.Mode)
	}
	return sendMail(ctx, opts)
}

func addressFromDomain(domain string) (*MailAddress, error) {
	in, err := instance.Get(domain)
	if err != nil {
		return nil, err
	}
	doc := &couchdb.JSONDoc{}
	err = couchdb.GetDoc(in, consts.Settings, consts.InstanceSettingsID, doc)
	if err != nil {
		return nil, err
	}
	email, ok := doc.M["email"].(string)
	if !ok {
		return nil, fmt.Errorf("Domain %s has no email in its settings", domain)
	}
	publicName, _ := doc.M["public_name"].(string)
	return &MailAddress{
		Name:  publicName,
		Email: email,
	}, nil
}

func doSendMail(ctx context.Context, opts *MailOptions) error {
	if opts.Subject == "" {
		return errors.New("Missing mail subject")
	}
	if len(opts.To) == 0 {
		return errors.New("Missing mail recipient")
	}
	if opts.From == nil {
		return errors.New("Missing mail sender")
	}
	mail := gomail.NewMessage()
	dialerOptions := opts.Dialer
	if dialerOptions == nil {
		dialerOptions = config.GetConfig().Mail
	}
	var date time.Time
	if opts.Date == nil {
		date = time.Now()
	} else {
		date = *opts.Date
	}
	toAddresses := make([]string, len(opts.To))
	for i, to := range opts.To {
		toAddresses[i] = mail.FormatAddress(to.Email, to.Name)
	}
	mail.SetHeaders(map[string][]string{
		"From":    {mail.FormatAddress(opts.From.Email, opts.From.Name)},
		"To":      toAddresses,
		"Subject": {opts.Subject},
	})
	mail.SetDateHeader("Date", date)
	for _, part := range opts.Parts {
		if err := addPart(mail, part, opts.TemplateValues); err != nil {
			return err
		}
	}
	dialer := gomail.NewDialer(dialerOptions)
	if deadline, ok := ctx.Deadline(); ok {
		dialer.SetDeadline(deadline)
	}
	return dialer.DialAndSend(mail)
}

func addPart(mail *gomail.Message, part *MailPart, templateValues interface{}) error {
	contentType := part.Type
	var body string
	if contentType != "text/plain" && contentType != "text/html" {
		return fmt.Errorf("Unknown body content-type %s", contentType)
	}
	if templateValues != nil {
		b := new(bytes.Buffer)
		switch contentType {
		case "text/html":
			t, err := htmlTemplate.New("mail").Parse(part.Body)
			if err != nil {
				return err
			}
			if err = t.Execute(b, templateValues); err != nil {
				return err
			}
		case "text/plain":
			t, err := textTemplate.New("mail").Parse(part.Body)
			if err != nil {
				return err
			}
			if err = t.Execute(b, templateValues); err != nil {
				return err
			}
		}
		body = b.String()
	} else {
		body = part.Body
	}
	mail.AddAlternative(contentType, body)
	return nil
}

// var for testability
var sendMail = doSendMail
<commit_msg>Rename body_type to type<commit_after>package workers

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	htmlTemplate "html/template"
	textTemplate "text/template"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/gomail"
)

func init() {
	jobs.AddWorker("sendmail", &jobs.WorkerConfig{
		Concurrency:  4,
		MaxExecCount: 3,
		Timeout:      10 * time.Second,
		WorkerFunc:   SendMail,
	})
}

// MailAddress contains the name and mail of a mail recipient.
type MailAddress struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

// MailOptions should be used as the options of a mail with manually defined
// content: body and body content-type. It is used as the input of the
// "sendmail" worker.
type MailOptions struct {
	Mode           string                `json:"mode"`
	From           *MailAddress          `json:"from"`
	To             []*MailAddress        `json:"to"`
	Subject        string                `json:"subject"`
	Dialer         *gomail.DialerOptions `json:"dialer,omitempty"`
	Date           *time.Time            `json:"date"`
	Parts          []*MailPart           `json:"parts"`
	TemplateValues interface{}           `json:"template_values"`
}

// MailPart represent a part of the content of the mail. It has a type
// specifying the content type of the part, and a body.
type MailPart struct {
	Type string `json:"type"`
	Body string `json:"body"`
}

// SendMail is the sendmail worker function.
func SendMail(ctx context.Context, m *jobs.Message) error {
	opts := &MailOptions{}
	err := m.Unmarshal(&opts)
	if err != nil {
		return err
	}
	domain := ctx.Value(jobs.ContextDomainKey).(string)
	switch opts.Mode {
	case "noreply":
		toAddr, err := addressFromDomain(domain)
		if err != nil {
			return err
		}
		opts.To = []*MailAddress{toAddr}
		opts.From = &MailAddress{Email: "noreply@" + domain}
	case "from":
		fromAddr, err := addressFromDomain(domain)
		if err != nil {
			return err
		}
		opts.From = fromAddr
	default:
		return fmt.Errorf("Mail sent with unknown mode %s", opts.Mode)
	}
	return sendMail(ctx, opts)
}

func addressFromDomain(domain string) (*MailAddress, error) {
	in, err := instance.Get(domain)
	if err != nil {
		return nil, err
	}
	doc := &couchdb.JSONDoc{}
	err = couchdb.GetDoc(in, consts.Settings, consts.InstanceSettingsID, doc)
	if err != nil {
		return nil, err
	}
	email, ok := doc.M["email"].(string)
	if !ok {
		return nil, fmt.Errorf("Domain %s has no email in its settings", domain)
	}
	publicName, _ := doc.M["public_name"].(string)
	return &MailAddress{
		Name:  publicName,
		Email: email,
	}, nil
}

func doSendMail(ctx context.Context, opts *MailOptions) error {
	if opts.Subject == "" {
		return errors.New("Missing mail subject")
	}
	if len(opts.To) == 0 {
		return errors.New("Missing mail recipient")
	}
	if opts.From == nil {
		return errors.New("Missing mail sender")
	}
	mail := gomail.NewMessage()
	dialerOptions := opts.Dialer
	if dialerOptions == nil {
		dialerOptions = config.GetConfig().Mail
	}
	var date time.Time
	if opts.Date == nil {
		date = time.Now()
	} else {
		date = *opts.Date
	}
	toAddresses := make([]string, len(opts.To))
	for i, to := range opts.To {
		toAddresses[i] = mail.FormatAddress(to.Email, to.Name)
	}
	mail.SetHeaders(map[string][]string{
		"From":    {mail.FormatAddress(opts.From.Email, opts.From.Name)},
		"To":      toAddresses,
		"Subject": {opts.Subject},
	})
	mail.SetDateHeader("Date", date)
	for _, part := range opts.Parts {
		if err := addPart(mail, part, opts.TemplateValues); err != nil {
			return err
		}
	}
	dialer := gomail.NewDialer(dialerOptions)
	if deadline, ok := ctx.Deadline(); ok {
		dialer.SetDeadline(deadline)
	}
	return dialer.DialAndSend(mail)
}

func addPart(mail *gomail.Message, part *MailPart, templateValues interface{}) error {
	contentType := part.Type
	var body string
	if contentType != "text/plain" && contentType != "text/html" {
		return fmt.Errorf("Unknown body content-type %s", contentType)
	}
	if templateValues != nil {
		b := new(bytes.Buffer)
		switch contentType {
		case "text/html":
			t, err := htmlTemplate.New("mail").Parse(part.Body)
			if err != nil {
				return err
			}
			if err = t.Execute(b, templateValues); err != nil {
				return err
			}
		case "text/plain":
			t, err := textTemplate.New("mail").Parse(part.Body)
			if err != nil {
				return err
			}
			if err = t.Execute(b, templateValues); err != nil {
				return err
			}
		}
		body = b.String()
	} else {
		body = part.Body
	}
	mail.AddAlternative(contentType, body)
	return nil
}

// var for testability
var sendMail = doSendMail
<|endoftext|>


<commit_before>// Copyright 2018 Authors of Cilium
//
// 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 k8s

import (
	"net"

	"github.com/cilium/cilium/pkg/loadbalancer"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/versioned"

	"k8s.io/api/core/v1"
	"k8s.io/api/extensions/v1beta1"
)

// CacheAction is the type of action that was performed on the cache
type CacheAction int

const (
	// UpdateService reflects that the service was updated or added
	UpdateService CacheAction = iota

	// DeleteService reflects that the service was deleted
	DeleteService

	// UpdateIngress reflects that the ingress was updated or added
	UpdateIngress

	// DeleteIngress reflects that the ingress was deleted
	DeleteIngress
)

// String returns the cache action as a string
func (c CacheAction) String() string {
	switch c {
	case UpdateService:
		return "service-updated"
	case DeleteService:
		return "service-deleted"
	case UpdateIngress:
		return "ingress-updated"
	case DeleteIngress:
		return "ingress-deleted"
	default:
		return "unknown"
	}
}

// ServiceEvent is emitted via the Events channel of ServiceCache and describes
// the change that occurred in the cache
type ServiceEvent struct {
	// Action is the action that was performed in the cache
	Action CacheAction

	// ID is the identified of the service
	ID ServiceID

	// Service is the service structure
	Service *Service

	// K8sService is the corresponding Kubernetes service resource that
	// triggered the action or nil if the action was not triggered by a
	// Kubernetes service update
	K8sService *v1.Service

	// Endpoints is the endpoints structured correlated with the service
	Endpoints *Endpoints

	// K8sEndpoints is the corresponding Kubernetes endpoints resource that
	// triggered the action or nil if the action was not triggered by a
	// Kubernetes endpoints update
	K8sEndpoints *v1.Endpoints
}

// ServiceCache is a list of services and ingresses correlated with the
// matching endpoints. The Events member will receive events as services and
// ingresses
type ServiceCache struct {
	mutex     lock.RWMutex
	services  map[ServiceID]*Service
	endpoints map[ServiceID]*Endpoints
	ingresses map[ServiceID]*Service

	Events chan ServiceEvent
}

// NewServiceCache returns a new ServiceCache
func NewServiceCache() ServiceCache {
	return ServiceCache{
		services:  map[ServiceID]*Service{},
		endpoints: map[ServiceID]*Endpoints{},
		ingresses: map[ServiceID]*Service{},
		Events:    make(chan ServiceEvent, 128),
	}
}

// UpdateService parses a Kubernetes service and adds or updates it in the
// ServiceCache. Returns the ServiceID unless the Kubernetes service could not
// be parsed and a bool to indicate whether the service was changed in the
// cache or not.
func (s *ServiceCache) UpdateService(k8sSvc *v1.Service) ServiceID {
	svcID, newService := ParseService(k8sSvc)
	if newService == nil {
		return svcID
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldService, ok := s.services[svcID]; ok {
		if oldService.DeepEquals(newService) {
			return svcID
		}
	}

	s.services[svcID] = newService

	// Check if the corresponding Endpoints resource is already available
	endpoints, ok := s.endpoints[svcID]
	if ok {
		s.Events <- ServiceEvent{
			Action:     UpdateService,
			ID:         svcID,
			Service:    newService,
			K8sService: k8sSvc,
			Endpoints:  endpoints,
		}
	}

	return svcID
}

// DeleteService parses a Kubernetes service and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteService(k8sSvc *v1.Service) {
	svcID := ParseServiceID(k8sSvc)

	s.mutex.Lock()
	oldService, serviceOK := s.services[svcID]
	endpoints, endpointsOK := s.endpoints[svcID]
	delete(s.services, svcID)
	s.mutex.Unlock()

	if serviceOK && endpointsOK {
		s.Events <- ServiceEvent{
			Action:     DeleteService,
			ID:         svcID,
			Service:    oldService,
			K8sService: k8sSvc,
			Endpoints:  endpoints,
		}
	}
}

// ListMissingServices returns a map of all services listed in requiredServices
// that are not found in the cache.
func (s *ServiceCache) ListMissingServices(requiredServices versioned.Map) versioned.Map {
	missing := versioned.NewMap()

	s.mutex.RLock()
	for uuid, svcObj := range requiredServices {
		neededSvc := svcObj.Data.(*v1.Service)
		id := ParseServiceID(neededSvc)

		existingService, ok := s.services[id]
		if !ok {
			missing.Add(uuid, svcObj)
			continue
		}

		_, newService := ParseService(neededSvc)
		if !existingService.DeepEquals(newService) {
			missing.Add(uuid, svcObj)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UpdateEndpoints parses a Kubernetes endpoints and adds or updates it in the
// ServiceCache. Returns the ServiceID unless the Kubernetes endpoints could not
// be parsed and a bool to indicate whether the endpoints was changed in the
// cache or not.
func (s *ServiceCache) UpdateEndpoints(k8sEndpoints *v1.Endpoints) (ServiceID, *Endpoints) {
	svcID, newEndpoints := ParseEndpoints(k8sEndpoints)

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldEndpoints, ok := s.endpoints[svcID]; ok {
		if oldEndpoints.DeepEquals(newEndpoints) {
			return svcID, newEndpoints
		}
	}

	s.endpoints[svcID] = newEndpoints

	// Check if the corresponding Endpoints resource is already available
	service, ok := s.services[svcID]
	if ok {
		s.Events <- ServiceEvent{
			Action:       UpdateService,
			ID:           svcID,
			Service:      service,
			Endpoints:    newEndpoints,
			K8sEndpoints: k8sEndpoints,
		}
	}

	return svcID, newEndpoints
}

// DeleteEndpoints parses a Kubernetes endpoints and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteEndpoints(k8sEndpoints *v1.Endpoints) ServiceID {
	svcID := ParseEndpointsID(k8sEndpoints)

	s.mutex.Lock()
	service, serviceOK := s.services[svcID]
	endpoints, endpointsOK := s.endpoints[svcID]
	delete(s.endpoints, svcID)
	s.mutex.Unlock()

	if serviceOK && endpointsOK {
		s.Events <- ServiceEvent{
			Action:    DeleteService,
			ID:        svcID,
			Service:   service,
			Endpoints: endpoints,
		}
	}

	return svcID
}

// ListMissingEndpoints returns the list of required endpoints that is not in
// the cache
func (s *ServiceCache) ListMissingEndpoints(requiredEndpoints versioned.Map) versioned.Map {
	type parsedEndpoints struct {
		id        ServiceID
		endpoints *Endpoints
		uuid      versioned.UUID
		object    versioned.Object
	}

	// parse endpoints first to avoid holding the loadBalancer mutex
	parsed := make([]parsedEndpoints, 0, len(requiredEndpoints))
	for uuid, endpointsObj := range requiredEndpoints {
		id, endpoints := ParseEndpoints(endpointsObj.Data.(*v1.Endpoints))
		parsed = append(parsed, parsedEndpoints{
			id:        id,
			endpoints: endpoints,
			uuid:      uuid,
			object:    endpointsObj,
		})
	}

	missing := versioned.NewMap()

	s.mutex.RLock()
	for _, p := range parsed {
		existingEndpoint, ok := s.endpoints[p.id]
		if !ok {
			missing.Add(p.uuid, p.object)
			continue
		}
		if !p.endpoints.DeepEquals(existingEndpoint) {
			missing.Add(p.uuid, p.object)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UpdateIngress parses a Kubernetes ingress and adds or updates it in the
// ServiceCache.
func (s *ServiceCache) UpdateIngress(ingress *v1beta1.Ingress, host net.IP) (ServiceID, error) {
	svcID, newService, err := ParseIngress(ingress, host)
	if err != nil {
		return svcID, err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldService, ok := s.ingresses[svcID]; ok {
		if oldService.DeepEquals(newService) {
			return svcID, nil
		}
	}

	s.ingresses[svcID] = newService

	s.Events <- ServiceEvent{
		Action:  UpdateIngress,
		ID:      svcID,
		Service: newService,
	}

	return svcID, nil
}

// DeleteIngress parses a Kubernetes ingress and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteIngress(ingress *v1beta1.Ingress) {
	svcID := ParseIngressID(ingress)

	s.mutex.Lock()
	oldService, ok := s.ingresses[svcID]
	endpoints, _ := s.endpoints[svcID]
	delete(s.ingresses, svcID)
	s.mutex.Unlock()

	if ok {
		s.Events <- ServiceEvent{
			Action:    DeleteIngress,
			ID:        svcID,
			Service:   oldService,
			Endpoints: endpoints,
		}
	}
}

// ListMissingIngresses returns a map of all ingress listed in required that
// are not found in the cache.
func (s *ServiceCache) ListMissingIngresses(required versioned.Map, host net.IP) versioned.Map {
	missing := versioned.NewMap()

	s.mutex.RLock()
	for uuid, svcObj := range required {
		neededSvc := svcObj.Data.(*v1beta1.Ingress)
		id := ParseIngressID(neededSvc)

		existingService, ok := s.ingresses[id]
		if !ok {
			missing.Add(uuid, svcObj)
			continue
		}

		_, newService, err := ParseIngress(neededSvc, host)
		if err != nil || !existingService.DeepEquals(newService) {
			missing.Add(uuid, svcObj)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UniqueServiceFrontends returns all services known to the service cache as a map, indexed by
// the string representation of a loadbalancer.L3n4Addr
func (s *ServiceCache) UniqueServiceFrontends() map[string]struct{} {
	uniqueFrontends := make(map[string]struct{})

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	for _, svc := range s.services {
		for _, p := range svc.Ports {
			address := loadbalancer.L3n4Addr{
				IP:     svc.FrontendIP,
				L4Addr: *p.L4Addr,
			}

			uniqueFrontends[address.StringWithProtocol()] = struct{}{}
		}
	}

	return uniqueFrontends
}
<commit_msg>k8s: Remove K8sService and K8sEndpoints field<commit_after>// Copyright 2018 Authors of Cilium
//
// 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 k8s

import (
	"net"

	"github.com/cilium/cilium/pkg/loadbalancer"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/versioned"

	"k8s.io/api/core/v1"
	"k8s.io/api/extensions/v1beta1"
)

// CacheAction is the type of action that was performed on the cache
type CacheAction int

const (
	// UpdateService reflects that the service was updated or added
	UpdateService CacheAction = iota

	// DeleteService reflects that the service was deleted
	DeleteService

	// UpdateIngress reflects that the ingress was updated or added
	UpdateIngress

	// DeleteIngress reflects that the ingress was deleted
	DeleteIngress
)

// String returns the cache action as a string
func (c CacheAction) String() string {
	switch c {
	case UpdateService:
		return "service-updated"
	case DeleteService:
		return "service-deleted"
	case UpdateIngress:
		return "ingress-updated"
	case DeleteIngress:
		return "ingress-deleted"
	default:
		return "unknown"
	}
}

// ServiceEvent is emitted via the Events channel of ServiceCache and describes
// the change that occurred in the cache
type ServiceEvent struct {
	// Action is the action that was performed in the cache
	Action CacheAction

	// ID is the identified of the service
	ID ServiceID

	// Service is the service structure
	Service *Service

	// Endpoints is the endpoints structured correlated with the service
	Endpoints *Endpoints
}

// ServiceCache is a list of services and ingresses correlated with the
// matching endpoints. The Events member will receive events as services and
// ingresses
type ServiceCache struct {
	mutex     lock.RWMutex
	services  map[ServiceID]*Service
	endpoints map[ServiceID]*Endpoints
	ingresses map[ServiceID]*Service

	Events chan ServiceEvent
}

// NewServiceCache returns a new ServiceCache
func NewServiceCache() ServiceCache {
	return ServiceCache{
		services:  map[ServiceID]*Service{},
		endpoints: map[ServiceID]*Endpoints{},
		ingresses: map[ServiceID]*Service{},
		Events:    make(chan ServiceEvent, 128),
	}
}

// UpdateService parses a Kubernetes service and adds or updates it in the
// ServiceCache. Returns the ServiceID unless the Kubernetes service could not
// be parsed and a bool to indicate whether the service was changed in the
// cache or not.
func (s *ServiceCache) UpdateService(k8sSvc *v1.Service) ServiceID {
	svcID, newService := ParseService(k8sSvc)
	if newService == nil {
		return svcID
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldService, ok := s.services[svcID]; ok {
		if oldService.DeepEquals(newService) {
			return svcID
		}
	}

	s.services[svcID] = newService

	// Check if the corresponding Endpoints resource is already available
	endpoints, ok := s.endpoints[svcID]
	if ok {
		s.Events <- ServiceEvent{
			Action:    UpdateService,
			ID:        svcID,
			Service:   newService,
			Endpoints: endpoints,
		}
	}

	return svcID
}

// DeleteService parses a Kubernetes service and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteService(k8sSvc *v1.Service) {
	svcID := ParseServiceID(k8sSvc)

	s.mutex.Lock()
	oldService, serviceOK := s.services[svcID]
	endpoints, endpointsOK := s.endpoints[svcID]
	delete(s.services, svcID)
	s.mutex.Unlock()

	if serviceOK && endpointsOK {
		s.Events <- ServiceEvent{
			Action:    DeleteService,
			ID:        svcID,
			Service:   oldService,
			Endpoints: endpoints,
		}
	}
}

// ListMissingServices returns a map of all services listed in requiredServices
// that are not found in the cache.
func (s *ServiceCache) ListMissingServices(requiredServices versioned.Map) versioned.Map {
	missing := versioned.NewMap()

	s.mutex.RLock()
	for uuid, svcObj := range requiredServices {
		neededSvc := svcObj.Data.(*v1.Service)
		id := ParseServiceID(neededSvc)

		existingService, ok := s.services[id]
		if !ok {
			missing.Add(uuid, svcObj)
			continue
		}

		_, newService := ParseService(neededSvc)
		if !existingService.DeepEquals(newService) {
			missing.Add(uuid, svcObj)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UpdateEndpoints parses a Kubernetes endpoints and adds or updates it in the
// ServiceCache. Returns the ServiceID unless the Kubernetes endpoints could not
// be parsed and a bool to indicate whether the endpoints was changed in the
// cache or not.
func (s *ServiceCache) UpdateEndpoints(k8sEndpoints *v1.Endpoints) (ServiceID, *Endpoints) {
	svcID, newEndpoints := ParseEndpoints(k8sEndpoints)

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldEndpoints, ok := s.endpoints[svcID]; ok {
		if oldEndpoints.DeepEquals(newEndpoints) {
			return svcID, newEndpoints
		}
	}

	s.endpoints[svcID] = newEndpoints

	// Check if the corresponding Endpoints resource is already available
	service, ok := s.services[svcID]
	if ok {
		s.Events <- ServiceEvent{
			Action:    UpdateService,
			ID:        svcID,
			Service:   service,
			Endpoints: newEndpoints,
		}
	}

	return svcID, newEndpoints
}

// DeleteEndpoints parses a Kubernetes endpoints and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteEndpoints(k8sEndpoints *v1.Endpoints) ServiceID {
	svcID := ParseEndpointsID(k8sEndpoints)

	s.mutex.Lock()
	service, serviceOK := s.services[svcID]
	endpoints, endpointsOK := s.endpoints[svcID]
	delete(s.endpoints, svcID)
	s.mutex.Unlock()

	if serviceOK && endpointsOK {
		s.Events <- ServiceEvent{
			Action:    DeleteService,
			ID:        svcID,
			Service:   service,
			Endpoints: endpoints,
		}
	}

	return svcID
}

// ListMissingEndpoints returns the list of required endpoints that is not in
// the cache
func (s *ServiceCache) ListMissingEndpoints(requiredEndpoints versioned.Map) versioned.Map {
	type parsedEndpoints struct {
		id        ServiceID
		endpoints *Endpoints
		uuid      versioned.UUID
		object    versioned.Object
	}

	// parse endpoints first to avoid holding the loadBalancer mutex
	parsed := make([]parsedEndpoints, 0, len(requiredEndpoints))
	for uuid, endpointsObj := range requiredEndpoints {
		id, endpoints := ParseEndpoints(endpointsObj.Data.(*v1.Endpoints))
		parsed = append(parsed, parsedEndpoints{
			id:        id,
			endpoints: endpoints,
			uuid:      uuid,
			object:    endpointsObj,
		})
	}

	missing := versioned.NewMap()

	s.mutex.RLock()
	for _, p := range parsed {
		existingEndpoint, ok := s.endpoints[p.id]
		if !ok {
			missing.Add(p.uuid, p.object)
			continue
		}
		if !p.endpoints.DeepEquals(existingEndpoint) {
			missing.Add(p.uuid, p.object)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UpdateIngress parses a Kubernetes ingress and adds or updates it in the
// ServiceCache.
func (s *ServiceCache) UpdateIngress(ingress *v1beta1.Ingress, host net.IP) (ServiceID, error) {
	svcID, newService, err := ParseIngress(ingress, host)
	if err != nil {
		return svcID, err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if oldService, ok := s.ingresses[svcID]; ok {
		if oldService.DeepEquals(newService) {
			return svcID, nil
		}
	}

	s.ingresses[svcID] = newService

	s.Events <- ServiceEvent{
		Action:  UpdateIngress,
		ID:      svcID,
		Service: newService,
	}

	return svcID, nil
}

// DeleteIngress parses a Kubernetes ingress and removes it from the
// ServiceCache
func (s *ServiceCache) DeleteIngress(ingress *v1beta1.Ingress) {
	svcID := ParseIngressID(ingress)

	s.mutex.Lock()
	oldService, ok := s.ingresses[svcID]
	endpoints, _ := s.endpoints[svcID]
	delete(s.ingresses, svcID)
	s.mutex.Unlock()

	if ok {
		s.Events <- ServiceEvent{
			Action:    DeleteIngress,
			ID:        svcID,
			Service:   oldService,
			Endpoints: endpoints,
		}
	}
}

// ListMissingIngresses returns a map of all ingress listed in required that
// are not found in the cache.
func (s *ServiceCache) ListMissingIngresses(required versioned.Map, host net.IP) versioned.Map {
	missing := versioned.NewMap()

	s.mutex.RLock()
	for uuid, svcObj := range required {
		neededSvc := svcObj.Data.(*v1beta1.Ingress)
		id := ParseIngressID(neededSvc)

		existingService, ok := s.ingresses[id]
		if !ok {
			missing.Add(uuid, svcObj)
			continue
		}

		_, newService, err := ParseIngress(neededSvc, host)
		if err != nil || !existingService.DeepEquals(newService) {
			missing.Add(uuid, svcObj)
		}
	}
	s.mutex.RUnlock()

	return missing
}

// UniqueServiceFrontends returns all services known to the service cache as a map, indexed by
// the string representation of a loadbalancer.L3n4Addr
func (s *ServiceCache) UniqueServiceFrontends() map[string]struct{} {
	uniqueFrontends := make(map[string]struct{})

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	for _, svc := range s.services {
		for _, p := range svc.Ports {
			address := loadbalancer.L3n4Addr{
				IP:     svc.FrontendIP,
				L4Addr: *p.L4Addr,
			}

			uniqueFrontends[address.StringWithProtocol()] = struct{}{}
		}
	}

	return uniqueFrontends
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"net/http"
	"testing"

	"github.com/dgrijalva/jwt-go"
	"github.com/drone/drone/pkg/server/recorder"
	"github.com/drone/drone/pkg/server/session"
	"github.com/drone/drone/pkg/settings"
	"github.com/drone/drone/pkg/store/mock"
	"github.com/drone/drone/pkg/types"
	. "github.com/franela/goblin"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/mock"
)

var tokenTests = []struct {
	inLabel  string
	inBody   string
	storeErr error
	outCode  int
	outKind  string
}{
	{"", `{}`, sql.ErrNoRows, 500, ""},
	{"app1", `{"label": "app1"}`, nil, 200, types.TokenUser},
	{"app2", `{"label": "app2"}`, nil, 200, types.TokenUser},
}

func TestToken(t *testing.T) {
	store := new(mocks.Store)

	g := Goblin(t)
	g.Describe("Token", func() {
		g.It("should create tokens", func() {
			for _, test := range tokenTests {
				rw := recorder.New()
				ctx := gin.Context{Engine: gin.Default(), Writer: rw}
				body := bytes.NewBufferString(test.inBody)
				ctx.Request, _ = http.NewRequest("POST", "/api/user/tokens", body)

				ctx.Set("datastore", store)
				ctx.Set("user", &types.User{Login: "Freya"})

				config := settings.Settings{Session: &settings.Session{Secret: "Otto"}}
				ctx.Set("settings", &config)
				ctx.Set("session", session.New(config.Session))

				// prepare the mock
				store.On("AddToken", mock.AnythingOfType("*types.Token")).Return(test.storeErr).Once()
				PostToken(&ctx)

				g.Assert(rw.Code).Equal(test.outCode)
				if test.outCode != 200 {
					continue
				}

				var respjson map[string]interface{}
				json.Unmarshal(rw.Body.Bytes(), &respjson)
				g.Assert(respjson["kind"]).Equal(types.TokenUser)
				g.Assert(respjson["label"]).Equal(test.inLabel)

				// this is probably going too far... maybe just validate hash is not empty?
				jwt.Parse(respjson["hash"].(string), func(token *jwt.Token) (interface{}, error) {
					_, ok := token.Method.(*jwt.SigningMethodHMAC)
					g.Assert(ok).IsTrue()
					g.Assert(token.Claims["label"]).Equal(test.inLabel)
					return nil, nil
				})
			}
		})

		g.It("should delete tokens")
	})
}
<commit_msg>Adding tests for DelToken in pkg/server/token.go<commit_after>package server

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/dgrijalva/jwt-go"
	"github.com/drone/drone/pkg/server/recorder"
	"github.com/drone/drone/pkg/server/session"
	"github.com/drone/drone/pkg/settings"
	"github.com/drone/drone/pkg/store/mock"
	"github.com/drone/drone/pkg/types"
	. "github.com/franela/goblin"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/mock"
)

var createTests = []struct {
	inLabel  string
	inBody   string
	storeErr error
	outCode  int
	outKind  string
}{
	{"", `{}`, sql.ErrNoRows, 500, ""},
	{"app1", `{"label": "app1"}`, nil, 200, types.TokenUser},
	{"app2", `{"label": "app2"}`, nil, 200, types.TokenUser},
}

var deleteTests = []struct {
	inLabel       string
	errTokenLabel error
	errDelToken   error
	outCode       int
	outToken      *types.Token
}{
	{"app1", sql.ErrNoRows, nil, 404, &types.Token{}},
	{"app2", nil, sql.ErrNoRows, 400, &types.Token{Label: "app2"}},
	{"app3", nil, nil, 200, &types.Token{Label: "app2"}},
}

func TestToken(t *testing.T) {
	store := new(mocks.Store)

	g := Goblin(t)
	g.Describe("Token", func() {

		// POST /api/user/tokens
		g.It("should create tokens", func() {
			for _, test := range createTests {
				rw := recorder.New()
				ctx := gin.Context{Engine: gin.Default(), Writer: rw}
				body := bytes.NewBufferString(test.inBody)
				ctx.Request, _ = http.NewRequest("POST", "/api/user/tokens", body)

				ctx.Set("datastore", store)
				ctx.Set("user", &types.User{Login: "Freya"})

				config := settings.Settings{Session: &settings.Session{Secret: "Otto"}}
				ctx.Set("settings", &config)
				ctx.Set("session", session.New(config.Session))

				// prepare the mock
				store.On("AddToken", mock.AnythingOfType("*types.Token")).Return(test.storeErr).Once()
				PostToken(&ctx)

				g.Assert(rw.Code).Equal(test.outCode)
				if test.outCode != 200 {
					continue
				}

				var respjson map[string]interface{}
				json.Unmarshal(rw.Body.Bytes(), &respjson)
				g.Assert(respjson["kind"]).Equal(types.TokenUser)
				g.Assert(respjson["label"]).Equal(test.inLabel)

				// this is probably going too far... maybe just validate hash is not empty?
				jwt.Parse(respjson["hash"].(string), func(token *jwt.Token) (interface{}, error) {
					_, ok := token.Method.(*jwt.SigningMethodHMAC)
					g.Assert(ok).IsTrue()
					g.Assert(token.Claims["label"]).Equal(test.inLabel)
					return nil, nil
				})
			}
		})

		// DELETE /api/user/tokens/:label
		g.It("should delete tokens", func() {
			for _, test := range deleteTests {
				rw := recorder.New()
				ctx := gin.Context{Engine: gin.Default(), Writer: rw}
				ctx.Params = append(ctx.Params, gin.Param{Key: "label", Value: test.inLabel})

				ctx.Set("datastore", store)
				ctx.Set("user", &types.User{Login: "Freya"})

				config := settings.Settings{Session: &settings.Session{Secret: "Otto"}}
				ctx.Set("settings", &config)
				ctx.Set("session", session.New(config.Session))

				// prepare the mock
				store.On("TokenLabel", mock.AnythingOfType("*types.User"), test.inLabel).Return(test.outToken, test.errTokenLabel).Once()

				if test.errTokenLabel == nil {
					// we don't need this expectation if we error on our first
					store.On("DelToken", mock.AnythingOfType("*types.Token")).Return(test.errDelToken).Once()
				}
				fmt.Println(test)
				DelToken(&ctx)

				g.Assert(rw.Code).Equal(test.outCode)
				if test.outCode != 200 {
					continue
				}

				var respjson map[string]interface{}
				json.Unmarshal(rw.Body.Bytes(), &respjson)
				fmt.Println(rw.Code, respjson)
			}
		})
	})
}
<|endoftext|>


<commit_before>// +build integration

package snap

import (
	"errors"
	"fmt"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
	"github.com/intelsdi-x/snap/scheduler/wmap"
	"github.com/intelsdi-x/swan/pkg/executor"
	"io/ioutil"
	"net"
	"os"
	"path"
	"strings"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

type Snapd struct {
	task executor.Task
}

func NewSnapd() *Snapd {
	return &Snapd{}
}

func (s *Snapd) Execute() error {
	l := executor.NewLocal()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("Cannot find GOPATH")
	}

	snapRoot := path.Join(gopath, "src", "github.com", "intelsdi-x", "snap", "build", "bin", "snapd")
	snapCommand := fmt.Sprintf("%s -t 0", snapRoot)

	task, err := l.Execute(snapCommand)
	if err != nil {
		return err
	}

	s.task = task

	return nil
}

func (s *Snapd) Stop() error {
	if s.task == nil {
		return errors.New("Snapd not started: cannot find task")
	}

	return s.task.Stop()
}

func (s *Snapd) Connected() bool {
	retries := 5
	connected := false
	for i := 0; i < retries; i++ {
		conn, err := net.Dial("tcp", "127.0.0.1:8181")
		if err != nil {
			time.Sleep(100 * time.Millisecond)
			continue
		}
		defer conn.Close()
		connected = true
	}

	return connected
}

func TestSnap(t *testing.T) {
	var snapd *Snapd
	var c *client.Client
	var s *Session
	var publisher *wmap.PublishWorkflowMapNode
	var metricsFile string

	Convey("Testing snap session", t, func() {
		Convey("Starting snapd", func() {
			snapd = NewSnapd()
			snapd.Execute()

			// Wait until snap is up.
			So(snapd.Connected(), ShouldBeTrue)
		})

		Convey("Connecting to snapd", func() {
			ct, err := client.New("http://127.0.0.1:8181", "v1", true)

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			c = ct
		})

		Convey("Loading collectors", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)
			err := plugins.Load("snap-plugin-collector-session-test")

			if err != nil {
				fmt.Printf("%v\n", err)
			}

			// Wait until metric is available in namespace.
			retries := 10
			found := false
			for i := 0; i < retries && !found; i++ {
				m := c.GetMetricCatalog()
				So(m.Err, ShouldBeNil)
				for _, metric := range m.Catalog {
					if metric.Namespace == "/intel/swan/session/metric1" {
						found = true
						break
					}
				}
				time.Sleep(500 * time.Millisecond)
			}
			So(found, ShouldBeTrue)
		})

		Convey("Loading publishers", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)

			plugins.Load("snap-plugin-publisher-session-test")

			publisher = wmap.NewPublishNode("session-test", 1)

			So(publisher, ShouldNotBeNil)

			tmpfile, err := ioutil.TempFile("", "session_test")
			tmpfile.Close()
			So(err, ShouldBeNil)

			metricsFile = tmpfile.Name()

			publisher.AddConfigItem("file", metricsFile)
		})

		Convey("Creating a Snap experiment session", func() {
			s = NewSession([]string{"/intel/swan/session/metric1"}, 1*time.Second, c, publisher)
			So(s, ShouldNotBeNil)
		})

		Convey("Starting a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Start("foobar", "barbaz")

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})
		})

		Convey("Contacting snap to get the task status", func() {
			status, err := s.Status()

			So(err, ShouldBeNil)

			Convey("And the task should be running", func() {
				So(status, ShouldEqual, "Running")
			})
		})

		Convey("Reading samples from file", func() {
			retries := 5
			found := false
			for i := 0; i < retries; i++ {
				time.Sleep(500 * time.Millisecond)

				dat, err := ioutil.ReadFile(metricsFile)
				if err != nil {
					continue
				}

				if len(dat) > 0 {
					// Look for tag on metric line.
					lines := strings.Split(string(dat), "\n")
					if len(lines) < 1 {
						continue
					}

					columns := strings.Split(lines[0], "\t")
					if len(columns) < 2 {
						continue
					}

					tags := strings.Split(columns[1], ",")
					if len(tags) < 2 {
						continue
					}

					So(columns[0], ShouldEqual, "/intel/swan/session/metric1")
					So(tags[0], ShouldEqual, "swan_experiment=foobar")
					So(tags[1], ShouldEqual, "swan_phase=barbaz")

					found = true

					break
				}
			}

			So(found, ShouldBeTrue)
		})

		Convey("Stopping a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Stop()

			So(err, ShouldBeNil)

			_, err = s.Status()
			So(err, ShouldNotBeNil)
		})

		Convey("Stopping snapd", func() {
			So(snapd, ShouldNotBeNil)

			if snapd != nil {
				snapd.Stop()
			}
		})
	})
}
<commit_msg>Removed unnecessary test case, since it is failing on some machines because of the bad file permissions. (#87)<commit_after>// +build integration

package snap

import (
	"errors"
	"fmt"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
	"github.com/intelsdi-x/snap/scheduler/wmap"
	"github.com/intelsdi-x/swan/pkg/executor"
	"io/ioutil"
	"net"
	"os"
	"path"
	"strings"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

type Snapd struct {
	task executor.Task
}

func NewSnapd() *Snapd {
	return &Snapd{}
}

func (s *Snapd) Execute() error {
	l := executor.NewLocal()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("Cannot find GOPATH")
	}

	snapRoot := path.Join(gopath, "src", "github.com", "intelsdi-x", "snap", "build", "bin", "snapd")
	snapCommand := fmt.Sprintf("%s -t 0", snapRoot)

	task, err := l.Execute(snapCommand)
	if err != nil {
		return err
	}

	s.task = task

	return nil
}

func (s *Snapd) Stop() error {
	if s.task == nil {
		return errors.New("Snapd not started: cannot find task")
	}

	return s.task.Stop()
}

func (s *Snapd) Connected() bool {
	retries := 5
	connected := false
	for i := 0; i < retries; i++ {
		conn, err := net.Dial("tcp", "127.0.0.1:8181")
		if err != nil {
			time.Sleep(100 * time.Millisecond)
			continue
		}
		defer conn.Close()
		connected = true
	}

	return connected
}

func TestSnap(t *testing.T) {
	var snapd *Snapd
	var c *client.Client
	var s *Session
	var publisher *wmap.PublishWorkflowMapNode
	var metricsFile string

	Convey("Testing snap session", t, func() {
		Convey("Starting snapd", func() {
			snapd = NewSnapd()
			snapd.Execute()

			// Wait until snap is up.
			So(snapd.Connected(), ShouldBeTrue)
		})

		Convey("Connecting to snapd", func() {
			ct, err := client.New("http://127.0.0.1:8181", "v1", true)

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			c = ct
		})

		Convey("Loading collectors", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)
			err := plugins.Load("snap-plugin-collector-session-test")

			if err != nil {
				fmt.Printf("%v\n", err)
			}

			// Wait until metric is available in namespace.
			retries := 10
			found := false
			for i := 0; i < retries && !found; i++ {
				m := c.GetMetricCatalog()
				So(m.Err, ShouldBeNil)
				for _, metric := range m.Catalog {
					if metric.Namespace == "/intel/swan/session/metric1" {
						found = true
						break
					}
				}
				time.Sleep(500 * time.Millisecond)
			}
			So(found, ShouldBeTrue)
		})

		Convey("Loading publishers", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)

			plugins.Load("snap-plugin-publisher-session-test")

			publisher = wmap.NewPublishNode("session-test", 1)

			So(publisher, ShouldNotBeNil)

			tmpfile, err := ioutil.TempFile("", "session_test")
			tmpfile.Close()
			So(err, ShouldBeNil)

			metricsFile = tmpfile.Name()

			publisher.AddConfigItem("file", metricsFile)
		})

		Convey("Creating a Snap experiment session", func() {
			s = NewSession([]string{"/intel/swan/session/metric1"}, 1*time.Second, c, publisher)
			So(s, ShouldNotBeNil)
		})

		Convey("Starting a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Start("foobar", "barbaz")

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})
		})

		Convey("Contacting snap to get the task status", func() {
			status, err := s.Status()

			So(err, ShouldBeNil)

			Convey("And the task should be running", func() {
				So(status, ShouldEqual, "Running")
			})
		})

		Convey("Reading samples from file", func() {
			retries := 5
			found := false
			for i := 0; i < retries; i++ {
				time.Sleep(500 * time.Millisecond)

				dat, err := ioutil.ReadFile(metricsFile)
				if err != nil {
					continue
				}

				if len(dat) > 0 {
					// Look for tag on metric line.
					lines := strings.Split(string(dat), "\n")
					if len(lines) < 1 {
						continue
					}

					columns := strings.Split(lines[0], "\t")
					if len(columns) < 2 {
						continue
					}

					tags := strings.Split(columns[1], ",")
					if len(tags) < 2 {
						continue
					}

					So(columns[0], ShouldEqual, "/intel/swan/session/metric1")
					So(tags[0], ShouldEqual, "swan_experiment=foobar")
					So(tags[1], ShouldEqual, "swan_phase=barbaz")

					found = true

					break
				}
			}

			So(found, ShouldBeTrue)
		})

		Convey("Stopping a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Stop()

			So(err, ShouldBeNil)

			_, err = s.Status()
			So(err, ShouldNotBeNil)
		})

		Convey("Stopping snapd", func() {
			So(snapd, ShouldNotBeNil)

			if snapd != nil {
				snapd.Stop()
			}
		})
	})
}
<|endoftext|>


<commit_before>package docker

import (
	"io"
	"log"
	"strings"
	"sync"

	"github.com/fsouza/go-dockerclient"
	"github.com/openshift/source-to-image/pkg/sti/errors"
)

// Docker is the interface between STI and the Docker client
// It contains higher level operations called from the STI
// build or usage commands
type Docker interface {
	IsImageInLocalRegistry(imageName string) (bool, error)
	RemoveContainer(id string) error
	GetDefaultScriptsUrl(image string) (string, error)
	RunContainer(opts RunContainerOptions) error
	GetImageId(image string) (string, error)
	CommitContainer(opts CommitContainerOptions) (string, error)
	RemoveImage(name string) error
}

// DockerClient contains all methods called on the go Docker
// client.
type DockerClient interface {
	RemoveImage(name string) error
	InspectImage(name string) (*docker.Image, error)
	PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
	CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error)
	AttachToContainer(opts docker.AttachToContainerOptions) error
	StartContainer(id string, hostConfig *docker.HostConfig) error
	WaitContainer(id string) (int, error)
	RemoveContainer(opts docker.RemoveContainerOptions) error
	CommitContainer(opts docker.CommitContainerOptions) (*docker.Image, error)
	CopyFromContainer(opts docker.CopyFromContainerOptions) error
}

type stiDocker struct {
	client  DockerClient
	verbose bool
}

type postExecutor interface {
	PostExecute(containerID string, cmd []string) error
}

// RunContainerOptions are options passed in to the RunContainer method
type RunContainerOptions struct {
	Image        string
	PullImage    bool
	OverwriteCmd bool
	Command      string
	Env          []string
	Stdin        io.Reader
	Stdout       io.Writer
	Stderr       io.Writer
	OnStart      func() error
	PostExec     postExecutor
}

// CommitContainerOptions are options passed in to the CommitContainer method
type CommitContainerOptions struct {
	ContainerID string
	Repository  string
	Command     []string
	Env         []string
}

// NewDocker creates a new implementation of the STI Docker interface
func NewDocker(endpoint string, verbose bool) (Docker, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, err
	}
	return &stiDocker{
		client:  client,
		verbose: verbose,
	}, nil
}

// IsImageInLocalRegistry determines whether the supplied image is in the local registry.
func (d *stiDocker) IsImageInLocalRegistry(imageName string) (bool, error) {
	image, err := d.client.InspectImage(imageName)

	if image != nil {
		return true, nil
	} else if err == docker.ErrNoSuchImage {
		return false, nil
	}

	return false, err
}

// CheckAndPull pulls an image into the local registry if not present
// and returns the image metadata
func (d *stiDocker) CheckAndPull(imageName string) (image *docker.Image, err error) {
	if image, err = d.client.InspectImage(imageName); err != nil &&
		err != docker.ErrNoSuchImage {
		return nil, errors.ErrPullImageFailed
	}

	if image == nil {
		log.Printf("Pulling image %s\n", imageName)

		// TODO: Add authentication support
		if err = d.client.PullImage(docker.PullImageOptions{Repository: imageName},
			docker.AuthConfiguration{}); err != nil {
			return nil, errors.ErrPullImageFailed
		}

		if image, err = d.client.InspectImage(imageName); err != nil {
			return nil, err
		}
	} else if d.verbose {
		log.Printf("Image %s available locally\n", imageName)
	}

	return
}

// RemoveContainer removes a container and its associated volumes.
func (d *stiDocker) RemoveContainer(id string) error {
	return d.client.RemoveContainer(docker.RemoveContainerOptions{id, true, true})
}

// GetDefaultUrl finds a script URL in the given image's metadata
func (d *stiDocker) GetDefaultScriptsUrl(image string) (string, error) {
	imageMetadata, err := d.CheckAndPull(image)
	if err != nil {
		return "", err
	}
	var defaultScriptsUrl string
	env := append(imageMetadata.ContainerConfig.Env, imageMetadata.Config.Env...)
	for _, v := range env {
		if strings.HasPrefix(v, "STI_SCRIPTS_URL=") {
			defaultScriptsUrl = v[len("STI_SCRIPTS_URL="):]
			break
		}
	}
	if d.verbose {
		log.Printf("Image contains default script url '%s'", defaultScriptsUrl)
	}
	return defaultScriptsUrl, nil
}

// RunContainer creates and starts a container using the image specified in the options with the ability
// to stream input or output
func (d *stiDocker) RunContainer(opts RunContainerOptions) (err error) {
	// get info about the specified image
	var imageMetadata *docker.Image
	if opts.PullImage {
		imageMetadata, err = d.CheckAndPull(opts.Image)
	} else {
		imageMetadata, err = d.client.InspectImage(opts.Image)
	}
	if err != nil {
		log.Printf("Error: Unable to get image metadata for %s: %v", opts.Image, err)
		return err
	}

	cmd := imageMetadata.Config.Cmd
	if opts.OverwriteCmd {
		cmd[len(cmd)-1] = opts.Command
	} else {
		cmd = append(cmd, opts.Command)
	}
	config := docker.Config{
		Image: opts.Image,
		Cmd:   cmd,
	}

	if opts.Env != nil {
		config.Env = opts.Env
	}

	if opts.Stdin != nil {
		config.OpenStdin = true
		config.StdinOnce = true
	}

	if opts.Stdout != nil {
		config.AttachStdout = true
	}

	if d.verbose {
		log.Printf("Creating container using config: %+v\n", config)
	}

	container, err := d.client.CreateContainer(docker.CreateContainerOptions{Name: "", Config: &config})
	if err != nil {
		return err
	}
	defer d.RemoveContainer(container.ID)

	if d.verbose {
		log.Printf("Attaching to container")
	}
	attached := make(chan struct{})
	attachOpts := docker.AttachToContainerOptions{
		Container: container.ID,
		Success:   attached,
		Stream:    true,
	}
	if opts.Stdin != nil {
		attachOpts.InputStream = opts.Stdin
		attachOpts.Stdin = true
	} else if opts.Stdout != nil {
		attachOpts.OutputStream = opts.Stdout
		attachOpts.Stdout = true
	}

	wg := sync.WaitGroup{}
	go func() {
		wg.Add(1)
		d.client.AttachToContainer(attachOpts)
		wg.Done()
	}()
	attached <- <-attached

	// If attaching both stdin and stdout, attach stdout in
	// a second goroutine
	if opts.Stdin != nil && opts.Stdout != nil {
		attached2 := make(chan struct{})
		attachOpts2 := docker.AttachToContainerOptions{
			Container:    container.ID,
			Success:      attached2,
			Stream:       true,
			OutputStream: opts.Stdout,
			Stdout:       true,
		}
		if opts.Stderr != nil {
			attachOpts2.Stderr = true
			attachOpts2.ErrorStream = opts.Stderr
		}
		go func() {
			wg.Add(1)
			d.client.AttachToContainer(attachOpts2)
			wg.Done()
		}()
		attached2 <- <-attached2
	}

	if d.verbose {
		log.Printf("Starting container")
	}
	if err = d.client.StartContainer(container.ID, nil); err != nil {
		return err
	}
	if opts.OnStart != nil {
		if err = opts.OnStart(); err != nil {
			return err
		}
	}

	if d.verbose {
		log.Printf("Waiting for container")
	}
	exitCode, err := d.client.WaitContainer(container.ID)
	wg.Wait()
	if err != nil {
		return err
	}
	if d.verbose {
		log.Printf("Container exited")
	}

	if exitCode != 0 {
		return errors.StiContainerError{exitCode}
	}

	if opts.PostExec != nil {
		if d.verbose {
			log.Printf("Invoking postExecution function")
		}
		if err = opts.PostExec.PostExecute(container.ID, imageMetadata.Config.Cmd); err != nil {
			return err
		}
	}
	return nil
}

// GetImageId retrives the ID of the image identified by name
func (d *stiDocker) GetImageId(imageName string) (string, error) {
	if image, err := d.client.InspectImage(imageName); err == nil {
		return image.ID, nil
	} else {
		return "", err
	}
}

// CommitContainer commits a container to an image with a specific tag.
// The new image ID is returned
func (d *stiDocker) CommitContainer(opts CommitContainerOptions) (string, error) {
	dockerOpts := docker.CommitContainerOptions{
		Container:  opts.ContainerID,
		Repository: opts.Repository,
	}
	if opts.Command != nil {
		config := docker.Config{
			Cmd: opts.Command,
			Env: opts.Env,
		}
		dockerOpts.Run = &config
		if d.verbose {
			log.Printf("Commiting container with config: %+v\n", config)
		}
	}

	if image, err := d.client.CommitContainer(dockerOpts); err == nil && image != nil {
		return image.ID, nil
	} else {
		return "", err
	}
}

// RemoveImage removes the image with specified ID
func (d *stiDocker) RemoveImage(imageID string) error {
	return d.client.RemoveImage(imageID)
}
<commit_msg>Fix build tags when including a repository and tag name<commit_after>package docker

import (
	"io"
	"log"
	"strings"
	"sync"

	"github.com/fsouza/go-dockerclient"
	"github.com/openshift/source-to-image/pkg/sti/errors"
)

// Docker is the interface between STI and the Docker client
// It contains higher level operations called from the STI
// build or usage commands
type Docker interface {
	IsImageInLocalRegistry(imageName string) (bool, error)
	RemoveContainer(id string) error
	GetDefaultScriptsUrl(image string) (string, error)
	RunContainer(opts RunContainerOptions) error
	GetImageId(image string) (string, error)
	CommitContainer(opts CommitContainerOptions) (string, error)
	RemoveImage(name string) error
}

// DockerClient contains all methods called on the go Docker
// client.
type DockerClient interface {
	RemoveImage(name string) error
	InspectImage(name string) (*docker.Image, error)
	PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
	CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error)
	AttachToContainer(opts docker.AttachToContainerOptions) error
	StartContainer(id string, hostConfig *docker.HostConfig) error
	WaitContainer(id string) (int, error)
	RemoveContainer(opts docker.RemoveContainerOptions) error
	CommitContainer(opts docker.CommitContainerOptions) (*docker.Image, error)
	CopyFromContainer(opts docker.CopyFromContainerOptions) error
}

type stiDocker struct {
	client  DockerClient
	verbose bool
}

type postExecutor interface {
	PostExecute(containerID string, cmd []string) error
}

// RunContainerOptions are options passed in to the RunContainer method
type RunContainerOptions struct {
	Image        string
	PullImage    bool
	OverwriteCmd bool
	Command      string
	Env          []string
	Stdin        io.Reader
	Stdout       io.Writer
	Stderr       io.Writer
	OnStart      func() error
	PostExec     postExecutor
}

// CommitContainerOptions are options passed in to the CommitContainer method
type CommitContainerOptions struct {
	ContainerID string
	Repository  string
	Command     []string
	Env         []string
}

// NewDocker creates a new implementation of the STI Docker interface
func NewDocker(endpoint string, verbose bool) (Docker, error) {
	client, err := docker.NewClient(endpoint)
	if err != nil {
		return nil, err
	}
	return &stiDocker{
		client:  client,
		verbose: verbose,
	}, nil
}

// IsImageInLocalRegistry determines whether the supplied image is in the local registry.
func (d *stiDocker) IsImageInLocalRegistry(imageName string) (bool, error) {
	image, err := d.client.InspectImage(imageName)

	if image != nil {
		return true, nil
	} else if err == docker.ErrNoSuchImage {
		return false, nil
	}

	return false, err
}

// CheckAndPull pulls an image into the local registry if not present
// and returns the image metadata
func (d *stiDocker) CheckAndPull(imageName string) (image *docker.Image, err error) {
	if image, err = d.client.InspectImage(imageName); err != nil &&
		err != docker.ErrNoSuchImage {
		return nil, errors.ErrPullImageFailed
	}

	if image == nil {
		log.Printf("Pulling image %s\n", imageName)

		// TODO: Add authentication support
		if err = d.client.PullImage(docker.PullImageOptions{Repository: imageName},
			docker.AuthConfiguration{}); err != nil {
			return nil, errors.ErrPullImageFailed
		}

		if image, err = d.client.InspectImage(imageName); err != nil {
			return nil, err
		}
	} else if d.verbose {
		log.Printf("Image %s available locally\n", imageName)
	}

	return
}

// RemoveContainer removes a container and its associated volumes.
func (d *stiDocker) RemoveContainer(id string) error {
	return d.client.RemoveContainer(docker.RemoveContainerOptions{id, true, true})
}

// GetDefaultUrl finds a script URL in the given image's metadata
func (d *stiDocker) GetDefaultScriptsUrl(image string) (string, error) {
	imageMetadata, err := d.CheckAndPull(image)
	if err != nil {
		return "", err
	}
	var defaultScriptsUrl string
	env := append(imageMetadata.ContainerConfig.Env, imageMetadata.Config.Env...)
	for _, v := range env {
		if strings.HasPrefix(v, "STI_SCRIPTS_URL=") {
			defaultScriptsUrl = v[len("STI_SCRIPTS_URL="):]
			break
		}
	}
	if d.verbose {
		log.Printf("Image contains default script url '%s'", defaultScriptsUrl)
	}
	return defaultScriptsUrl, nil
}

// RunContainer creates and starts a container using the image specified in the options with the ability
// to stream input or output
func (d *stiDocker) RunContainer(opts RunContainerOptions) (err error) {
	// get info about the specified image
	var imageMetadata *docker.Image
	if opts.PullImage {
		imageMetadata, err = d.CheckAndPull(opts.Image)
	} else {
		imageMetadata, err = d.client.InspectImage(opts.Image)
	}
	if err != nil {
		log.Printf("Error: Unable to get image metadata for %s: %v", opts.Image, err)
		return err
	}

	cmd := imageMetadata.Config.Cmd
	if opts.OverwriteCmd {
		cmd[len(cmd)-1] = opts.Command
	} else {
		cmd = append(cmd, opts.Command)
	}
	config := docker.Config{
		Image: opts.Image,
		Cmd:   cmd,
	}

	if opts.Env != nil {
		config.Env = opts.Env
	}

	if opts.Stdin != nil {
		config.OpenStdin = true
		config.StdinOnce = true
	}

	if opts.Stdout != nil {
		config.AttachStdout = true
	}

	if d.verbose {
		log.Printf("Creating container using config: %+v\n", config)
	}

	container, err := d.client.CreateContainer(docker.CreateContainerOptions{Name: "", Config: &config})
	if err != nil {
		return err
	}
	defer d.RemoveContainer(container.ID)

	if d.verbose {
		log.Printf("Attaching to container")
	}
	attached := make(chan struct{})
	attachOpts := docker.AttachToContainerOptions{
		Container: container.ID,
		Success:   attached,
		Stream:    true,
	}
	if opts.Stdin != nil {
		attachOpts.InputStream = opts.Stdin
		attachOpts.Stdin = true
	} else if opts.Stdout != nil {
		attachOpts.OutputStream = opts.Stdout
		attachOpts.Stdout = true
	}

	wg := sync.WaitGroup{}
	go func() {
		wg.Add(1)
		d.client.AttachToContainer(attachOpts)
		wg.Done()
	}()
	attached <- <-attached

	// If attaching both stdin and stdout, attach stdout in
	// a second goroutine
	if opts.Stdin != nil && opts.Stdout != nil {
		attached2 := make(chan struct{})
		attachOpts2 := docker.AttachToContainerOptions{
			Container:    container.ID,
			Success:      attached2,
			Stream:       true,
			OutputStream: opts.Stdout,
			Stdout:       true,
		}
		if opts.Stderr != nil {
			attachOpts2.Stderr = true
			attachOpts2.ErrorStream = opts.Stderr
		}
		go func() {
			wg.Add(1)
			d.client.AttachToContainer(attachOpts2)
			wg.Done()
		}()
		attached2 <- <-attached2
	}

	if d.verbose {
		log.Printf("Starting container")
	}
	if err = d.client.StartContainer(container.ID, nil); err != nil {
		return err
	}
	if opts.OnStart != nil {
		if err = opts.OnStart(); err != nil {
			return err
		}
	}

	if d.verbose {
		log.Printf("Waiting for container")
	}
	exitCode, err := d.client.WaitContainer(container.ID)
	wg.Wait()
	if err != nil {
		return err
	}
	if d.verbose {
		log.Printf("Container exited")
	}

	if exitCode != 0 {
		return errors.StiContainerError{exitCode}
	}

	if opts.PostExec != nil {
		if d.verbose {
			log.Printf("Invoking postExecution function")
		}
		if err = opts.PostExec.PostExecute(container.ID, imageMetadata.Config.Cmd); err != nil {
			return err
		}
	}
	return nil
}

// GetImageId retrives the ID of the image identified by name
func (d *stiDocker) GetImageId(imageName string) (string, error) {
	if image, err := d.client.InspectImage(imageName); err == nil {
		return image.ID, nil
	} else {
		return "", err
	}
}

// CommitContainer commits a container to an image with a specific tag.
// The new image ID is returned
func (d *stiDocker) CommitContainer(opts CommitContainerOptions) (string, error) {

	repository, tag := docker.ParseRepositoryTag(opts.Repository)
	dockerOpts := docker.CommitContainerOptions{
		Container:  opts.ContainerID,
		Repository: repository,
		Tag:        tag,
	}
	if opts.Command != nil {
		config := docker.Config{
			Cmd: opts.Command,
			Env: opts.Env,
		}
		dockerOpts.Run = &config
		if d.verbose {
			log.Printf("Commiting container with config: %+v\n", config)
		}
	}

	if image, err := d.client.CommitContainer(dockerOpts); err == nil && image != nil {
		return image.ID, nil
	} else {
		return "", err
	}
}

// RemoveImage removes the image with specified ID
func (d *stiDocker) RemoveImage(imageID string) error {
	return d.client.RemoveImage(imageID)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved

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

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

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

package docker

import (
	"bytes"
	dockerlib "github.com/fsouza/go-dockerclient"
	"github.com/novln/docker-parser"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

// Push will provide methods for interaction with API regarding pushing images
type Push struct {
	Client dockerlib.Client
}

/*
PushImage push a Docker image via the Docker API. Takes the image name,
parses the URL details and then push based on environment authentication
credentials.
*/
func (c *Push) PushImage(fullImageName string) error {
	outputBuffer := bytes.NewBuffer(nil)

	// Using https://github.com/novln/docker-parser in order to parse the appropriate
	// name and registry.
	parsedImage, err := dockerparser.Parse(fullImageName)
	if err != nil {
		return err
	}
	image, registry := parsedImage.Name(), parsedImage.Registry()

	log.Infof("Pushing image '%s' to registry '%s'", image, registry)

	// Let's setup the push and authentication options
	options := dockerlib.PushImageOptions{
		Name:         fullImageName,
		Registry:     parsedImage.Registry(),
		OutputStream: outputBuffer,
	}

	// Retrieve the authentication configuration file
	// Files checked as per https://godoc.org/github.com/fsouza/go-dockerclient#NewAuthConfigurationsFromFile
	// $DOCKER_CONFIG/config.json, $HOME/.docker/config.json , $HOME/.dockercfg
	credentials, err := dockerlib.NewAuthConfigurationsFromDockerCfg()
	if err != nil {
		log.Warn(errors.Wrap(err, "Unable to retrieve .docker/config.json authentication details. Check that 'docker login' works successfully on the command line."))
	}

	// Fallback to unauthenticated access in case if no auth credentials are retrieved
	if credentials == nil || len(credentials.Configs) == 0 {
		log.Info("Authentication credentials are not detected. Will try push without authentication.")
		credentials = &dockerlib.AuthConfigurations{
			Configs: map[string]dockerlib.AuthConfiguration{
				registry: {},
			},
		}
	}

	// Push the image to the repository (based on the URL)
	// We will iterate through all available authentication configurations until we find one that pushes successfully
	// and then return nil.
	if len(credentials.Configs) > 1 {
		log.Info("Multiple authentication credentials detected. Will try each configuration.")
	}

	for k, v := range credentials.Configs {
		log.Infof("Attempting authentication credentials '%s", k)
		err = c.Client.PushImage(options, v)
		if err != nil {
			log.Errorf("Unable to push image '%s' to registry '%s'. Error: %s", image, registry, err)
		} else {
			log.Debugf("Image '%s' push output:\n%s", image, outputBuffer)
			log.Infof("Successfully pushed image '%s' to registry '%s'", image, registry)
			return nil
		}
	}

	return errors.New("unable to push docker image(s). Check that `docker login` works successfully on the command line")
}
<commit_msg>typo fix<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved

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

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

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

package docker

import (
	"bytes"
	dockerlib "github.com/fsouza/go-dockerclient"
	"github.com/novln/docker-parser"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

// Push will provide methods for interaction with API regarding pushing images
type Push struct {
	Client dockerlib.Client
}

/*
PushImage pushes a Docker image via the Docker API. Takes the image name,
parses the URL details and then push based on environment authentication
credentials.
*/
func (c *Push) PushImage(fullImageName string) error {
	outputBuffer := bytes.NewBuffer(nil)

	// Using https://github.com/novln/docker-parser in order to parse the appropriate
	// name and registry.
	parsedImage, err := dockerparser.Parse(fullImageName)
	if err != nil {
		return err
	}
	image, registry := parsedImage.Name(), parsedImage.Registry()

	log.Infof("Pushing image '%s' to registry '%s'", image, registry)

	// Let's setup the push and authentication options
	options := dockerlib.PushImageOptions{
		Name:         fullImageName,
		Registry:     parsedImage.Registry(),
		OutputStream: outputBuffer,
	}

	// Retrieve the authentication configuration file
	// Files checked as per https://godoc.org/github.com/fsouza/go-dockerclient#NewAuthConfigurationsFromFile
	// $DOCKER_CONFIG/config.json, $HOME/.docker/config.json , $HOME/.dockercfg
	credentials, err := dockerlib.NewAuthConfigurationsFromDockerCfg()
	if err != nil {
		log.Warn(errors.Wrap(err, "Unable to retrieve .docker/config.json authentication details. Check that 'docker login' works successfully on the command line."))
	}

	// Fallback to unauthenticated access in case if no auth credentials are retrieved
	if credentials == nil || len(credentials.Configs) == 0 {
		log.Info("Authentication credentials are not detected. Will try push without authentication.")
		credentials = &dockerlib.AuthConfigurations{
			Configs: map[string]dockerlib.AuthConfiguration{
				registry: {},
			},
		}
	}

	// Push the image to the repository (based on the URL)
	// We will iterate through all available authentication configurations until we find one that pushes successfully
	// and then return nil.
	if len(credentials.Configs) > 1 {
		log.Info("Multiple authentication credentials detected. Will try each configuration.")
	}

	for k, v := range credentials.Configs {
		log.Infof("Attempting authentication credentials '%s", k)
		err = c.Client.PushImage(options, v)
		if err != nil {
			log.Errorf("Unable to push image '%s' to registry '%s'. Error: %s", image, registry, err)
		} else {
			log.Debugf("Image '%s' push output:\n%s", image, outputBuffer)
			log.Infof("Successfully pushed image '%s' to registry '%s'", image, registry)
			return nil
		}
	}

	return errors.New("unable to push docker image(s). Check that `docker login` works successfully on the command line")
}
<|endoftext|>