<commit_before>package discovery

import (
	"sort"
	"strings"

	pb_config "github.com/improbable-eng/kedge/protogen/kedge/config"
	pb_resolvers "github.com/improbable-eng/kedge/protogen/kedge/config/common/resolvers"
	pb_grpcbackends "github.com/improbable-eng/kedge/protogen/kedge/config/grpc/backends"
	pb_grpcroutes "github.com/improbable-eng/kedge/protogen/kedge/config/grpc/routes"
	pb_httpbackends "github.com/improbable-eng/kedge/protogen/kedge/config/http/backends"
	pb_httproutes "github.com/improbable-eng/kedge/protogen/kedge/config/http/routes"
	"github.com/pkg/errors"
)

// lastSeenServicesToConfigs constructs director and backendpool configs from lastSeenServices and base configuration files.
// At the end it validates and sorts them.
func (u *updater) lastSeenServicesToConfigs() (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig, error) {
	resultDirector, resultBackendpool := cloneBaseConfigs(u.baseDirectorConfig, u.baseBackendConfig)
	for _, serviceConf := range u.lastSeenServices {
		addRoutingsToDirector(resultDirector, serviceConf.routings)
		addBackendsToBackendpool(resultBackendpool, serviceConf.backends)
	}

	err := resultDirector.Validate()
	if err != nil {
		return nil, nil, errors.Wrap(err, "director config does not pass validation after generation.")
	}

	err = resultBackendpool.Validate()
	if err != nil {
		return nil, nil, errors.Wrap(err, "backendpool config does not pass validation after generation.")
	}

	// Sort it.
	httpDirectorRouteSort(resultDirector.GetHttp().Routes)
	grpcDirectorRouteSort(resultDirector.GetGrpc().Routes)
	httpBackendpoolSort(resultBackendpool.GetHttp().Backends)
	grpcBackendpoolSort(resultBackendpool.GetGrpc().Backends)

	return resultDirector, resultBackendpool, nil
}

func cloneBaseConfigs(baseDirector *pb_config.DirectorConfig, baseBackendpool *pb_config.BackendPoolConfig) (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig) {
	resultDirectorConfig := &pb_config.DirectorConfig{
		Grpc: &pb_config.DirectorConfig_Grpc{},
		Http: &pb_config.DirectorConfig_Http{},
	}
	resultBackendPool := &pb_config.BackendPoolConfig{
		Grpc:             &pb_config.BackendPoolConfig_Grpc{},
		Http:             &pb_config.BackendPoolConfig_Http{},
		TlsServerConfigs: baseBackendpool.TlsServerConfigs,
	}

	// Copy base for HTTP.
	if baseDirector.GetHttp() != nil {
		for _, route := range baseDirector.GetHttp().GetRoutes() {
			resultDirectorConfig.GetHttp().Routes = append(resultDirectorConfig.GetHttp().Routes, route)
		}

		for _, route := range baseDirector.GetHttp().GetAdhocRules() {
			resultDirectorConfig.GetHttp().AdhocRules = append(resultDirectorConfig.GetHttp().AdhocRules, route)
		}
	}
	if baseBackendpool.GetHttp() != nil {
		for _, backend := range baseBackendpool.GetHttp().GetBackends() {
			resultBackendPool.GetHttp().Backends = append(resultBackendPool.GetHttp().Backends, backend)
		}
	}

	// Copy base for gRPC.
	if baseDirector.GetGrpc() != nil {
		for _, route := range baseDirector.GetGrpc().GetRoutes() {
			resultDirectorConfig.GetGrpc().Routes = append(resultDirectorConfig.GetGrpc().Routes, route)
		}
	}
	if baseBackendpool.GetGrpc() != nil {
		for _, backend := range baseBackendpool.GetGrpc().GetBackends() {
			resultBackendPool.GetGrpc().Backends = append(resultBackendPool.GetGrpc().Backends, backend)
		}
	}
	return resultDirectorConfig, resultBackendPool
}

func httpDirectorRouteSort(routes []*pb_httproutes.Route) {
	sort.Slice(routes, func(i int, j int) bool {
		firstRoute := routes[i]
		secondRoute := routes[j]

		if firstRoute.HostMatcher == secondRoute.HostMatcher {
			// This is critical. If they both share one host matcher and one does not have portMatcher, the latter needs to be
			// at the end.
			if firstRoute.PortMatcher == 0 {
				return false
			}

			if secondRoute.PortMatcher == 0 {
				return true
			}

			// Otherwise just sort based on port.
			return firstRoute.PortMatcher < secondRoute.PortMatcher
		}

		return strings.Compare(firstRoute.BackendName, secondRoute.BackendName) <= 0
	})
}

func grpcDirectorRouteSort(routes []*pb_grpcroutes.Route) {
	sort.Slice(routes, func(i int, j int) bool {
		firstRoute := routes[i]
		secondRoute := routes[j]

		if firstRoute.AuthorityHostMatcher == secondRoute.AuthorityHostMatcher {
			// This is critical. If they both share one host matcher and one does not have portMatcher, the latter needs to be
			// at the end.
			if firstRoute.AuthorityPortMatcher == 0 {
				return false
			}

			if secondRoute.AuthorityPortMatcher == 0 {
				return true
			}

			// Otherwise just sort based on port.
			return firstRoute.AuthorityPortMatcher < secondRoute.AuthorityPortMatcher
		}

		// TODO(bplotka): Add sorting based on globbing expression to not hide each one out.
		/// service_name_matcher is a globbing expression that matches a full gRPC service name.
		/// For example a method call to 'com.example.MyService/Create' would be matched by:
		///  - com.example.MyService
		///  - com.example.*
		///  - com.*
		///  - *
		/// If not present, '*' is default.
		return strings.Compare(firstRoute.BackendName, secondRoute.BackendName) <= 0
	})
}

func httpBackendpoolSort(backends []*pb_httpbackends.Backend) {
	sort.Slice(backends, func(i int, j int) bool {
		return strings.Compare(backends[i].Name, backends[j].Name) <= 0
	})
}

func grpcBackendpoolSort(backends []*pb_grpcbackends.Backend) {
	sort.Slice(backends, func(i int, j int) bool {
		return strings.Compare(backends[i].Name, backends[j].Name) <= 0
	})
}

func addRoutingsToDirector(director *pb_config.DirectorConfig, routings serviceRoutings) {
	for backendName, httpRoutes := range routings.http {
		for _, httpRoute := range httpRoutes {
			director.GetHttp().Routes = append(
				director.GetHttp().Routes,
				&pb_httproutes.Route{
					Autogenerated: true,
					BackendName:   backendName.String(),
					HostMatcher:   httpRoute.nameMatcher,
					PortMatcher:   httpRoute.portMatcher,
					ProxyMode:     pb_httproutes.ProxyMode_REVERSE_PROXY,
				},
			)
		}
	}

	for backendName, grpcRoutes := range routings.grpc {
		for _, grpcRoute := range grpcRoutes {
			director.GetGrpc().Routes = append(
				director.GetGrpc().Routes,
				&pb_grpcroutes.Route{
					Autogenerated:        true,
					BackendName:          backendName.String(),
					AuthorityHostMatcher: grpcRoute.nameMatcher,
					AuthorityPortMatcher: grpcRoute.portMatcher,
				},
			)
		}
	}
}

func addBackendsToBackendpool(backendpool *pb_config.BackendPoolConfig, backends serviceBackends) {
	for backendName, domainPort := range backends.httpDomainPorts {
		b := &pb_httpbackends.Backend{
			Autogenerated: true,
			Name:          backendName.String(),
			Resolver: &pb_httpbackends.Backend_K8S{
				K8S: &pb_resolvers.K8SResolver{
					DnsPortName: domainPort,
				},
			},
			Balancer: pb_httpbackends.Balancer_ROUND_ROBIN,
		}

		// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
		if _, isTLS := backends.tlsConfigs[backendName]; isTLS {
			b.Security = &pb_httpbackends.Security{
				InsecureSkipVerify: true,
			}
		}

		backendpool.GetHttp().Backends = append(backendpool.GetHttp().Backends, b)
	}

	for backendName, domainPort := range backends.grpcDomainPorts {
		b := &pb_grpcbackends.Backend{
			Autogenerated: true,
			Name:          backendName.String(),
			Resolver: &pb_grpcbackends.Backend_K8S{
				K8S: &pb_resolvers.K8SResolver{
					DnsPortName: domainPort,
				},
			},
			Balancer: pb_grpcbackends.Balancer_ROUND_ROBIN,
		}

		// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
		if _, isTLS := backends.tlsConfigs[backendName]; isTLS {
			b.Security = &pb_grpcbackends.Security{
				InsecureSkipVerify: true,
			}
		}

		backendpool.GetGrpc().Backends = append(backendpool.GetGrpc().Backends, b)
	}

}
<commit_msg>Do not delete old adhoc gRPC routes on a discovery update.<commit_after>package discovery

import (
	"sort"
	"strings"

	pb_config "github.com/improbable-eng/kedge/protogen/kedge/config"
	pb_resolvers "github.com/improbable-eng/kedge/protogen/kedge/config/common/resolvers"
	pb_grpcbackends "github.com/improbable-eng/kedge/protogen/kedge/config/grpc/backends"
	pb_grpcroutes "github.com/improbable-eng/kedge/protogen/kedge/config/grpc/routes"
	pb_httpbackends "github.com/improbable-eng/kedge/protogen/kedge/config/http/backends"
	pb_httproutes "github.com/improbable-eng/kedge/protogen/kedge/config/http/routes"
	"github.com/pkg/errors"
)

// lastSeenServicesToConfigs constructs director and backendpool configs from lastSeenServices and base configuration files.
// At the end it validates and sorts them.
func (u *updater) lastSeenServicesToConfigs() (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig, error) {
	resultDirector, resultBackendpool := cloneBaseConfigs(u.baseDirectorConfig, u.baseBackendConfig)
	for _, serviceConf := range u.lastSeenServices {
		addRoutingsToDirector(resultDirector, serviceConf.routings)
		addBackendsToBackendpool(resultBackendpool, serviceConf.backends)
	}

	err := resultDirector.Validate()
	if err != nil {
		return nil, nil, errors.Wrap(err, "director config does not pass validation after generation.")
	}

	err = resultBackendpool.Validate()
	if err != nil {
		return nil, nil, errors.Wrap(err, "backendpool config does not pass validation after generation.")
	}

	// Sort it.
	httpDirectorRouteSort(resultDirector.GetHttp().Routes)
	grpcDirectorRouteSort(resultDirector.GetGrpc().Routes)
	httpBackendpoolSort(resultBackendpool.GetHttp().Backends)
	grpcBackendpoolSort(resultBackendpool.GetGrpc().Backends)

	return resultDirector, resultBackendpool, nil
}

func cloneBaseConfigs(baseDirector *pb_config.DirectorConfig, baseBackendpool *pb_config.BackendPoolConfig) (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig) {
	resultDirectorConfig := &pb_config.DirectorConfig{
		Grpc: &pb_config.DirectorConfig_Grpc{},
		Http: &pb_config.DirectorConfig_Http{},
	}
	resultBackendPool := &pb_config.BackendPoolConfig{
		Grpc:             &pb_config.BackendPoolConfig_Grpc{},
		Http:             &pb_config.BackendPoolConfig_Http{},
		TlsServerConfigs: baseBackendpool.TlsServerConfigs,
	}

	// Copy base for HTTP.
	if baseDirector.GetHttp() != nil {
		for _, route := range baseDirector.GetHttp().GetRoutes() {
			resultDirectorConfig.GetHttp().Routes = append(resultDirectorConfig.GetHttp().Routes, route)
		}

		for _, route := range baseDirector.GetHttp().GetAdhocRules() {
			resultDirectorConfig.GetHttp().AdhocRules = append(resultDirectorConfig.GetHttp().AdhocRules, route)
		}
	}
	if baseBackendpool.GetHttp() != nil {
		for _, backend := range baseBackendpool.GetHttp().GetBackends() {
			resultBackendPool.GetHttp().Backends = append(resultBackendPool.GetHttp().Backends, backend)
		}
	}

	// Copy base for gRPC.
	if baseDirector.GetGrpc() != nil {
		for _, route := range baseDirector.GetGrpc().GetRoutes() {
			resultDirectorConfig.GetGrpc().Routes = append(resultDirectorConfig.GetGrpc().Routes, route)
		}

		for _, route := range baseDirector.GetGrpc().GetAdhocRules() {
			resultDirectorConfig.GetGrpc().AdhocRules = append(resultDirectorConfig.GetGrpc().AdhocRules, route)
		}
	}
	if baseBackendpool.GetGrpc() != nil {
		for _, backend := range baseBackendpool.GetGrpc().GetBackends() {
			resultBackendPool.GetGrpc().Backends = append(resultBackendPool.GetGrpc().Backends, backend)
		}
	}
	return resultDirectorConfig, resultBackendPool
}

func httpDirectorRouteSort(routes []*pb_httproutes.Route) {
	sort.Slice(routes, func(i int, j int) bool {
		firstRoute := routes[i]
		secondRoute := routes[j]

		if firstRoute.HostMatcher == secondRoute.HostMatcher {
			// This is critical. If they both share one host matcher and one does not have portMatcher, the latter needs to be
			// at the end.
			if firstRoute.PortMatcher == 0 {
				return false
			}

			if secondRoute.PortMatcher == 0 {
				return true
			}

			// Otherwise just sort based on port.
			return firstRoute.PortMatcher < secondRoute.PortMatcher
		}

		return strings.Compare(firstRoute.BackendName, secondRoute.BackendName) <= 0
	})
}

func grpcDirectorRouteSort(routes []*pb_grpcroutes.Route) {
	sort.Slice(routes, func(i int, j int) bool {
		firstRoute := routes[i]
		secondRoute := routes[j]

		if firstRoute.AuthorityHostMatcher == secondRoute.AuthorityHostMatcher {
			// This is critical. If they both share one host matcher and one does not have portMatcher, the latter needs to be
			// at the end.
			if firstRoute.AuthorityPortMatcher == 0 {
				return false
			}

			if secondRoute.AuthorityPortMatcher == 0 {
				return true
			}

			// Otherwise just sort based on port.
			return firstRoute.AuthorityPortMatcher < secondRoute.AuthorityPortMatcher
		}

		// TODO(bplotka): Add sorting based on globbing expression to not hide each one out.
		/// service_name_matcher is a globbing expression that matches a full gRPC service name.
		/// For example a method call to 'com.example.MyService/Create' would be matched by:
		///  - com.example.MyService
		///  - com.example.*
		///  - com.*
		///  - *
		/// If not present, '*' is default.
		return strings.Compare(firstRoute.BackendName, secondRoute.BackendName) <= 0
	})
}

func httpBackendpoolSort(backends []*pb_httpbackends.Backend) {
	sort.Slice(backends, func(i int, j int) bool {
		return strings.Compare(backends[i].Name, backends[j].Name) <= 0
	})
}

func grpcBackendpoolSort(backends []*pb_grpcbackends.Backend) {
	sort.Slice(backends, func(i int, j int) bool {
		return strings.Compare(backends[i].Name, backends[j].Name) <= 0
	})
}

func addRoutingsToDirector(director *pb_config.DirectorConfig, routings serviceRoutings) {
	for backendName, httpRoutes := range routings.http {
		for _, httpRoute := range httpRoutes {
			director.GetHttp().Routes = append(
				director.GetHttp().Routes,
				&pb_httproutes.Route{
					Autogenerated: true,
					BackendName:   backendName.String(),
					HostMatcher:   httpRoute.nameMatcher,
					PortMatcher:   httpRoute.portMatcher,
					ProxyMode:     pb_httproutes.ProxyMode_REVERSE_PROXY,
				},
			)
		}
	}

	for backendName, grpcRoutes := range routings.grpc {
		for _, grpcRoute := range grpcRoutes {
			director.GetGrpc().Routes = append(
				director.GetGrpc().Routes,
				&pb_grpcroutes.Route{
					Autogenerated:        true,
					BackendName:          backendName.String(),
					AuthorityHostMatcher: grpcRoute.nameMatcher,
					AuthorityPortMatcher: grpcRoute.portMatcher,
				},
			)
		}
	}
}

func addBackendsToBackendpool(backendpool *pb_config.BackendPoolConfig, backends serviceBackends) {
	for backendName, domainPort := range backends.httpDomainPorts {
		b := &pb_httpbackends.Backend{
			Autogenerated: true,
			Name:          backendName.String(),
			Resolver: &pb_httpbackends.Backend_K8S{
				K8S: &pb_resolvers.K8SResolver{
					DnsPortName: domainPort,
				},
			},
			Balancer: pb_httpbackends.Balancer_ROUND_ROBIN,
		}

		// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
		if _, isTLS := backends.tlsConfigs[backendName]; isTLS {
			b.Security = &pb_httpbackends.Security{
				InsecureSkipVerify: true,
			}
		}

		backendpool.GetHttp().Backends = append(backendpool.GetHttp().Backends, b)
	}

	for backendName, domainPort := range backends.grpcDomainPorts {
		b := &pb_grpcbackends.Backend{
			Autogenerated: true,
			Name:          backendName.String(),
			Resolver: &pb_grpcbackends.Backend_K8S{
				K8S: &pb_resolvers.K8SResolver{
					DnsPortName: domainPort,
				},
			},
			Balancer: pb_grpcbackends.Balancer_ROUND_ROBIN,
		}

		// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
		if _, isTLS := backends.tlsConfigs[backendName]; isTLS {
			b.Security = &pb_grpcbackends.Security{
				InsecureSkipVerify: true,
			}
		}

		backendpool.GetGrpc().Backends = append(backendpool.GetGrpc().Backends, b)
	}

}
<|endoftext|>


<commit_before>package lib

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
)

type encryptedDataBagItem struct {
	ID      string
	Entries map[string]*encryptedDataBagEntry
}

type encryptedDataBagEntry struct {
	Cipher        string
	EncryptedData string
	Iv            string
	Version       float64
}

type version1Wrapper struct {
	Content string `json:"json_wrapper"`
}

// Decrypt a databag item file
func Decrypt(itemPath, secretPath string) string {
	encryptedItem := newEncryptedDataBagItem(readFile(itemPath))
	secretData := readFile(secretPath)
	entries := encryptedItem.decrypt(secretData)
	bytes, e := json.MarshalIndent(entries, "", "  ")
	if e != nil {
		panic("Failed to marshal data bag item")
	}
	return string(bytes)
}

func readFile(path string) []byte {
	content, e := ioutil.ReadFile(path)
	if e != nil {
		panic(fmt.Sprintf("File error: %v\n", e))
	}
	return content
}

func newEncryptedDataBagItem(raw []byte) *encryptedDataBagItem {
	var kvs map[string]interface{}
	if json.Unmarshal(raw, &kvs) != nil {
		panic("Failed to unmarshal data bag item")
	}

	item := new(encryptedDataBagItem)
	item.Entries = make(map[string]*encryptedDataBagEntry)

	for k, v := range kvs {
		switch k {
		case "id":
			item.ID = v.(string)
		default:
			entry := v.(map[string]interface{})
			item.Entries[k] = &encryptedDataBagEntry{
				Version:       entry["version"].(float64),
				Cipher:        entry["cipher"].(string),
				EncryptedData: entry["encrypted_data"].(string),
				Iv:            entry["iv"].(string),
			}
		}
	}

	return item
}

func (encryptedItem *encryptedDataBagItem) decrypt(secretData []byte) map[string]string {
	entries := make(map[string]string, len(encryptedItem.Entries)+1)
	entries["id"] = encryptedItem.ID

	for key, entry := range encryptedItem.Entries {
		if entry.Version != 1 {
			panic(fmt.Sprintf("Not implemented for encrypted bag version %f", entry.Version))
		}

		entries[key] = entry.decrypt(secretData)
	}

	return entries
}

func (entry *encryptedDataBagEntry) decrypt(secretData []byte) string {
	ciphertext := decodeBase64(entry.EncryptedData)
	initVector := decodeBase64(entry.Iv)
	keySha := sha256.Sum256(secretData)

	block, err := aes.NewCipher(keySha[:])
	if err != nil {
		fmt.Println(err)
	}

	if len(ciphertext)%aes.BlockSize != 0 {
		panic("Ciphertext is not a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, initVector)
	mode.CryptBlocks(ciphertext, ciphertext)

	ciphertext = unPKCS7Padding(ciphertext)

	var wrapper version1Wrapper
	if json.Unmarshal(ciphertext, &wrapper) != nil {
		panic("Failed to unmarshal data bag content")
	}

	return wrapper.Content
}

func decodeBase64(str string) []byte {
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		fmt.Println("error:", err)
	}
	return data
}

func unPKCS7Padding(data []byte) []byte {
	dataLen := len(data)
	endIndex := int(data[dataLen-1])

	if 16 > endIndex {
		return data[:dataLen-endIndex]
	}
	return nil
}
<commit_msg>added cipher support guard<commit_after>package lib

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
)

type encryptedDataBagItem struct {
	ID      string
	Entries map[string]*encryptedDataBagEntry
}

type encryptedDataBagEntry struct {
	Cipher        string
	EncryptedData string
	Iv            string
	Version       float64
}

type version1Wrapper struct {
	Content string `json:"json_wrapper"`
}

// Decrypt a databag item file
func Decrypt(itemPath, secretPath string) string {
	encryptedItem := newEncryptedDataBagItem(readFile(itemPath))
	secretData := readFile(secretPath)
	entries := encryptedItem.decrypt(secretData)
	bytes, e := json.MarshalIndent(entries, "", "  ")
	if e != nil {
		panic("Failed to marshal data bag item")
	}
	return string(bytes)
}

func readFile(path string) []byte {
	content, e := ioutil.ReadFile(path)
	if e != nil {
		panic(fmt.Sprintf("File error: %v\n", e))
	}
	return content
}

func newEncryptedDataBagItem(raw []byte) *encryptedDataBagItem {
	var kvs map[string]interface{}
	if json.Unmarshal(raw, &kvs) != nil {
		panic("Failed to unmarshal data bag item")
	}

	item := new(encryptedDataBagItem)
	item.Entries = make(map[string]*encryptedDataBagEntry)

	for k, v := range kvs {
		switch k {
		case "id":
			item.ID = v.(string)
		default:
			entry := v.(map[string]interface{})
			item.Entries[k] = &encryptedDataBagEntry{
				Version:       entry["version"].(float64),
				Cipher:        entry["cipher"].(string),
				EncryptedData: entry["encrypted_data"].(string),
				Iv:            entry["iv"].(string),
			}
		}
	}

	return item
}

func (encryptedItem *encryptedDataBagItem) decrypt(secretData []byte) map[string]string {
	entries := make(map[string]string, len(encryptedItem.Entries)+1)
	entries["id"] = encryptedItem.ID

	for key, entry := range encryptedItem.Entries {
		if entry.Version != 1 {
			panic(fmt.Sprintf("Not implemented for encrypted bag version %f", entry.Version))
		}
		if entry.Cipher != "aes-256-cbc" {
			panic(fmt.Sprintf("Not implemented for encrypted bag cipher %s", entry.Cipher))
		}

		entries[key] = entry.decrypt(secretData)
	}

	return entries
}

// proudly stolen from https://github.com/go-chef/cryptobag/blob/master/decrypter_v1.go
func (entry *encryptedDataBagEntry) decrypt(secretData []byte) string {
	ciphertext := decodeBase64(entry.EncryptedData)
	initVector := decodeBase64(entry.Iv)
	keySha := sha256.Sum256(secretData)

	block, err := aes.NewCipher(keySha[:])
	if err != nil {
		fmt.Println(err)
	}

	if len(ciphertext)%aes.BlockSize != 0 {
		panic("Ciphertext is not a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, initVector)
	mode.CryptBlocks(ciphertext, ciphertext)

	ciphertext = unPKCS7Padding(ciphertext)

	var wrapper version1Wrapper
	if json.Unmarshal(ciphertext, &wrapper) != nil {
		panic("Failed to unmarshal data bag content")
	}

	return wrapper.Content
}

func decodeBase64(str string) []byte {
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		fmt.Println("error:", err)
	}
	return data
}

func unPKCS7Padding(data []byte) []byte {
	dataLen := len(data)
	endIndex := int(data[dataLen-1])

	if 16 > endIndex {
		return data[:dataLen-endIndex]
	}
	return nil
}
<|endoftext|>


<commit_before>package gocode

import (
	"fmt"
	"go/importer"
	"go/types"

	"github.com/sourcegraph/go-langserver/langserver/internal/gocode/gbimporter"
	"github.com/sourcegraph/go-langserver/langserver/internal/gocode/suggest"
)

type AutoCompleteRequest struct {
	Filename string
	Data     []byte
	Cursor   int
	Context  gbimporter.PackedContext
	Source   bool
	Builtin  bool
}

type AutoCompleteReply struct {
	Candidates []suggest.Candidate
	Len        int
}

func AutoComplete(req *AutoCompleteRequest) (res *AutoCompleteReply, err error) {
	res = &AutoCompleteReply{}
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("gocode panic: %s\n\n", err)

			res.Candidates = []suggest.Candidate{
				{Class: "PANIC", Name: "PANIC", Type: "PANIC"},
			}
		}
	}()

	var underlying types.ImporterFrom
	if req.Source {
		underlying = importer.For("source", nil).(types.ImporterFrom)
	} else {
		underlying = importer.Default().(types.ImporterFrom)
	}
	cfg := suggest.Config{
		Importer: gbimporter.New(&req.Context, req.Filename, underlying),
		Builtin:  req.Builtin,
	}

	candidates, d, err := cfg.Suggest(req.Filename, req.Data, req.Cursor)
	if err != nil {
		return nil, err
	}
	res.Candidates, res.Len = candidates, d
	return res, nil
}
<commit_msg>use log for gocode<commit_after>package gocode

import (
	"go/importer"
	"go/types"
	"log"

	"github.com/sourcegraph/go-langserver/langserver/internal/gocode/gbimporter"
	"github.com/sourcegraph/go-langserver/langserver/internal/gocode/suggest"
)

type AutoCompleteRequest struct {
	Filename string
	Data     []byte
	Cursor   int
	Context  gbimporter.PackedContext
	Source   bool
	Builtin  bool
}

type AutoCompleteReply struct {
	Candidates []suggest.Candidate
	Len        int
}

func AutoComplete(req *AutoCompleteRequest) (res *AutoCompleteReply, err error) {
	res = &AutoCompleteReply{}
	defer func() {
		if err := recover(); err != nil {
			log.Printf("gocode panic: %s\n\n", err)

			res.Candidates = []suggest.Candidate{
				{Class: "PANIC", Name: "PANIC", Type: "PANIC"},
			}
		}
	}()

	var underlying types.ImporterFrom
	if req.Source {
		underlying = importer.For("source", nil).(types.ImporterFrom)
	} else {
		underlying = importer.Default().(types.ImporterFrom)
	}
	cfg := suggest.Config{
		Importer: gbimporter.New(&req.Context, req.Filename, underlying),
		Builtin:  req.Builtin,
	}

	candidates, d, err := cfg.Suggest(req.Filename, req.Data, req.Cursor)
	if err != nil {
		return nil, err
	}
	res.Candidates, res.Len = candidates, d
	return res, nil
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"html/template"
	"io"

	"github.com/russross/blackfriday"
)

// CommandBlock groups opaqueCode with its labels.
type CommandBlock struct {
	labels []Label
	prose []byte
	code  OpaqueCode
}

func NewCommandBlock(labels []Label, prose []byte, code OpaqueCode) *CommandBlock {
	if !hasLabel(labels, AnyLabel) {
		labels = append(labels, AnyLabel)
	}
	return &CommandBlock{labels, prose, code}
}

func (x *CommandBlock) Accept(v TutVisitor)  { v.VisitCommandBlock(x) }
func (x *CommandBlock) Name() string         { return string(x.Labels()[0]) }
func (x *CommandBlock) Path() FilePath { return FilePath("notUsingThis") }
func (x *CommandBlock) Children() []Tutorial { return []Tutorial{} }
func (x *CommandBlock) HtmlProse() template.HTML {
	return template.HTML(string(blackfriday.MarkdownCommon(x.Prose())))
}
func (x *CommandBlock) Labels() []Label  { return x.labels }
func (x *CommandBlock) Prose() []byte    { return x.prose }
func (x *CommandBlock) Code() OpaqueCode { return x.code }

func (x *CommandBlock) HasLabel(label Label) bool {
	return xhasLabel(x.Labels(), label)
}

func xhasLabel(labels []Label, label Label) bool {
	for _, l := range labels {
		if l == label {
			return true
		}
	}
	return false
}
func (x *CommandBlock) Print(
	w io.Writer, prefix string, n int, label Label, fileName FilePath) {
	fmt.Fprintf(w, "echo \"%s @%s (block #%d in %s) of %s\"\n\n",
		prefix, x.Name(), n, label, fileName)
	fmt.Fprint(w, x.Code())
	// If the command block has a 'sleep' label, add a brief sleep at the end.
	// This hack gives servers placed in the background time to start, assuming
	// they can do so in the time added!  Yeah, bad.
	if x.HasLabel(SleepLabel) {
		fmt.Fprint(w, "sleep 3s # Added by mdrip\n")
	}
}
<commit_msg>removexhaslabels<commit_after>package model

import (
	"fmt"
	"html/template"
	"io"

	"github.com/russross/blackfriday"
)

// CommandBlock groups opaqueCode with its labels.
type CommandBlock struct {
	labels []Label
	prose []byte
	code  OpaqueCode
}

func NewCommandBlock(labels []Label, prose []byte, code OpaqueCode) *CommandBlock {
	if !hasLabel(labels, AnyLabel) {
		labels = append(labels, AnyLabel)
	}
	return &CommandBlock{labels, prose, code}
}

func (x *CommandBlock) Accept(v TutVisitor)  { v.VisitCommandBlock(x) }
func (x *CommandBlock) Name() string         { return string(x.Labels()[0]) }
func (x *CommandBlock) Path() FilePath { return FilePath("notUsingThis") }
func (x *CommandBlock) Children() []Tutorial { return []Tutorial{} }
func (x *CommandBlock) HtmlProse() template.HTML {
	return template.HTML(string(blackfriday.MarkdownCommon(x.Prose())))
}
func (x *CommandBlock) Labels() []Label  { return x.labels }
func (x *CommandBlock) Prose() []byte    { return x.prose }
func (x *CommandBlock) Code() OpaqueCode { return x.code }

func (x *CommandBlock) HasLabel(label Label) bool {
	return hasLabel(x.Labels(), label)
}

func (x *CommandBlock) Print(
	w io.Writer, prefix string, n int, label Label, fileName FilePath) {
	fmt.Fprintf(w, "echo \"%s @%s (block #%d in %s) of %s\"\n\n",
		prefix, x.Name(), n, label, fileName)
	fmt.Fprint(w, x.Code())
	// If the command block has a 'sleep' label, add a brief sleep at the end.
	// This hack gives servers placed in the background time to start, assuming
	// they can do so in the time added!  Yeah, bad.
	if x.HasLabel(SleepLabel) {
		fmt.Fprint(w, "sleep 3s # Added by mdrip\n")
	}
}
<|endoftext|>


<commit_before>package gohm

import(
	`reflect`
	`testing`
)

type validModel struct {
	ID    string `ohm:"id"`
	Name  string `ohm:"name"`
	Email string `ohm:"email index"`
	UUID  string `ohm:"uuid unique"`
}

type unexportedFieldModel struct {
	ID   string `ohm:"id"`
	name string `ohm:"name"`
}

type noIDModel struct {
	Name string `ohm:"name"`
}

type nonStringIDModel struct {
	Name int `ohm:"name"`
}

func TestValidateModel(t *testing.T) {
	var err error
	if err = ValidateModel(&validModel{}); err != nil {
		t.Error(err)
	}

	if err = ValidateModel(&unexportedFieldModel{}); err != NonExportedAttrError {
		t.Error(`unexported fields with ohm tags should make the model invalid`)
	}

	if err = ValidateModel(&noIDModel{}); err != NoIDError {
		t.Error(`models with no ohm:"id" tag should be invalid`)
	}

	if err = ValidateModel(&nonStringIDModel{}); err != NonStringIDError {
		t.Error(`models should be invalid when their ohm:"id" field is not a string`)
	}
}

func TestModelAttrIndexMap(t *testing.T) {
	attrMap := ModelAttrIndexMap(&validModel{})

	expectedMap := map[string]int{
		`name`:  1,
		`email`: 2,
		`uuid`:  3,
	}

	if !reflect.DeepEqual(expectedMap, attrMap) {
		t.Errorf(`expected %v, got %v`, expectedMap, attrMap)
	}
}

func TestModelID(t *testing.T) {
	u := &validModel{}
	u2 := &validModel{ID: `2`}

	if ModelID(u) != `` {
		t.Errorf(`expected model ID to be empty, but its set to "%v"`, ModelID(u))
	}

	if ModelID(u2) != `2` {
		t.Errorf(`model ID should be 2, but its "%v"`, ModelID(u))
	}
}

func TestModelHasAttribute(t *testing.T) {
	if !ModelHasAttribute(&validModel{}, `email`) {
		t.Error(`model has attribute "email", but the function return false`)
	}

	if ModelHasAttribute(&validModel{}, `palangana`) {
		t.Error(`model doesnt have the attribute "palangana", but the function return true`)
	}
}

func TestModelIDFieldName(t *testing.T) {
	if ModelIDFieldName(&validModel{}) != `ID` {
		t.Error(`function is not correctly reporting the ID field name`)
	}
}
<commit_msg>ModelType test<commit_after>package gohm

import(
	`reflect`
	`testing`
)

type validModel struct {
	ID    string `ohm:"id"`
	Name  string `ohm:"name"`
	Email string `ohm:"email index"`
	UUID  string `ohm:"uuid unique"`
}

type unexportedFieldModel struct {
	ID   string `ohm:"id"`
	name string `ohm:"name"`
}

type noIDModel struct {
	Name string `ohm:"name"`
}

type nonStringIDModel struct {
	Name int `ohm:"name"`
}

func TestValidateModel(t *testing.T) {
	var err error
	if err = ValidateModel(&validModel{}); err != nil {
		t.Error(err)
	}

	if err = ValidateModel(&unexportedFieldModel{}); err != NonExportedAttrError {
		t.Error(`unexported fields with ohm tags should make the model invalid`)
	}

	if err = ValidateModel(&noIDModel{}); err != NoIDError {
		t.Error(`models with no ohm:"id" tag should be invalid`)
	}

	if err = ValidateModel(&nonStringIDModel{}); err != NonStringIDError {
		t.Error(`models should be invalid when their ohm:"id" field is not a string`)
	}
}

func TestModelAttrIndexMap(t *testing.T) {
	attrMap := ModelAttrIndexMap(&validModel{})

	expectedMap := map[string]int{
		`name`:  1,
		`email`: 2,
		`uuid`:  3,
	}

	if !reflect.DeepEqual(expectedMap, attrMap) {
		t.Errorf(`expected %v, got %v`, expectedMap, attrMap)
	}
}

func TestModelID(t *testing.T) {
	u := &validModel{}
	u2 := &validModel{ID: `2`}

	if ModelID(u) != `` {
		t.Errorf(`expected model ID to be empty, but its set to "%v"`, ModelID(u))
	}

	if ModelID(u2) != `2` {
		t.Errorf(`model ID should be 2, but its "%v"`, ModelID(u))
	}
}

func TestModelHasAttribute(t *testing.T) {
	if !ModelHasAttribute(&validModel{}, `email`) {
		t.Error(`model has attribute "email", but the function return false`)
	}

	if ModelHasAttribute(&validModel{}, `palangana`) {
		t.Error(`model doesnt have the attribute "palangana", but the function return true`)
	}
}

func TestModelIDFieldName(t *testing.T) {
	if ModelIDFieldName(&validModel{}) != `ID` {
		t.Error(`function is not correctly reporting the ID field name`)
	}
}

func TestModelType(t *testing.T) {
	if ModelType(&validModel{}) != `validModel` {
		t.Error(`function does not return correct model name`)
	}
}
<|endoftext|>


<commit_before>package models

type SessionLog struct {
	LogId         int
	Date          int
	Goal          string
	ActiveWork    int
	RestTime      int
	SetsCompleted int
	RepsCompleted int
	SuccessRate   float64
	Drill         int
}
<commit_msg>added gorp db tags to SessionLog model<commit_after>package models

type SessionLog struct {
	LogId         int     `db:"log_id"`
	Date          int     `db:"date"`
	Goal          string  `db:"goal"`
	ActiveWork    int     `db:"active_work"`
	RestTime      int     `db:"rest_time"`
	SetsCompleted int     `db:"sets_completed"`
	RepsCompleted int     `db:"reps_completed"`
	SuccessRate   float64 `db:"success_rate"`
	Drill         int     `db:"drill"`
}
<|endoftext|>


<commit_before>package signals

import (
	"os"
	"os/signal"
	"syscall"
)

var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
var onlyOneSignalHandler = make(chan struct{})

// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
// which is closed on one of these signals. If a second signal is caught, the program
// is terminated with exit code 1.
func SetupSignalHandler() (stopCh <-chan struct{}) {
	close(onlyOneSignalHandler) // panics when called twice

	stop := make(chan struct{})
	c := make(chan os.Signal, 2)
	signal.Notify(c, shutdownSignals...)
	go func() {
		<-c
		close(stop)
		<-c
		os.Exit(1) // second signal. Exit directly.
	}()

	return stop
}
<commit_msg>refactor(signals): replace stop channel with signal context<commit_after>package signals

import (
	"context"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

var (
	shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
	signalCtx       context.Context
	cancel          context.CancelFunc
	once            sync.Once
)

// Context returns a Context registered to close on SIGTERM and SIGINT.
// If a second signal is caught, the program is terminated with exit code 1.
func Context() context.Context {
	once.Do(func() {
		c := make(chan os.Signal, 2)
		signal.Notify(c, shutdownSignals...)
		signalCtx, cancel = context.WithCancel(context.Background())
		go func() {
			<-c
			cancel()

			select {
			case <-signalCtx.Done():
			case <-c:
				os.Exit(1) // second signal. Exit directly.
			}
		}()
	})

	return signalCtx
}
<|endoftext|>


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

package storage_test

import (
	"fmt"
	"reflect"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/pkg/base"
	"github.com/cockroachdb/cockroach/pkg/gossip"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/keys"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
	"github.com/cockroachdb/cockroach/pkg/util/log"
	"github.com/cockroachdb/cockroach/pkg/util/retry"
)

func TestGossipFirstRange(t *testing.T) {
	defer leaktest.AfterTest(t)()

	tc := testcluster.StartTestCluster(t, 3,
		base.TestClusterArgs{
			ReplicationMode: base.ReplicationManual,
		})
	defer tc.Stopper().Stop()

	errors := make(chan error)
	descs := make(chan *roachpb.RangeDescriptor)
	unregister := tc.Servers[0].Gossip().RegisterCallback(gossip.KeyFirstRangeDescriptor,
		func(_ string, content roachpb.Value) {
			var desc roachpb.RangeDescriptor
			if err := content.GetProto(&desc); err != nil {
				errors <- err
			} else {
				descs <- &desc
			}
		},
	)
	// Unregister the callback before attempting to stop the stopper to prevent
	// deadlock. This is still flaky in theory since a callback can fire between
	// the last read from the channels and this unregister, but testing has
	// shown this solution to be sufficiently robust for now.
	defer unregister()

	// Wait for the specified descriptor to be gossiped for the first range. We
	// loop because the timing of replica addition and lease transfer can cause
	// extra gossiping of the first range.
	waitForGossip := func(desc roachpb.RangeDescriptor) {
		for {
			select {
			case err := <-errors:
				t.Fatal(err)
			case gossiped := <-descs:
				if reflect.DeepEqual(&desc, gossiped) {
					return
				}
				log.Infof(context.TODO(), "expected\n%+v\nbut found\n%+v", desc, gossiped)
			}
		}
	}

	// Expect an initial callback of the first range descriptor.
	select {
	case err := <-errors:
		t.Fatal(err)
	case <-descs:
	}

	// Add two replicas. The first range descriptor should be gossiped after each
	// addition.
	var desc roachpb.RangeDescriptor
	firstRangeKey := keys.MinKey
	for i := 1; i <= 2; i++ {
		var err error
		if desc, err = tc.AddReplicas(firstRangeKey, tc.Target(i)); err != nil {
			t.Fatal(err)
		}
		waitForGossip(desc)
	}

	// Transfer the lease to a new node. This should cause the first range to be
	// gossiped again.
	if err := tc.TransferRangeLease(desc, tc.Target(1)); err != nil {
		t.Fatal(err)
	}
	waitForGossip(desc)

	// Remove a non-lease holder replica.
	desc, err := tc.RemoveReplicas(firstRangeKey, tc.Target(0))
	if err != nil {
		t.Fatal(err)
	}
	waitForGossip(desc)

	// TODO(peter): Re-enable or remove when we've resolved the discussion
	// about removing the lease-holder replica. See #7872.

	// // Remove the lease holder replica.
	// leaseHolder, err := tc.FindRangeLeaseHolder(desc, nil)
	// desc, err = tc.RemoveReplicas(firstRangeKey, leaseHolder)
	// if err != nil {
	// 	t.Fatal(err)
	// }
	// select {
	// case err := <-errors:
	// 	t.Fatal(err)
	// case gossiped := <-descs:
	// 	if !reflect.DeepEqual(desc, gossiped) {
	// 		t.Fatalf("expected\n%+v\nbut found\n%+v", desc, gossiped)
	// 	}
	// }
}

// TestGossipHandlesReplacedNode tests that we can shut down a node and
// replace it with a new node at the same address (simulating a node getting
// restarted after losing its data) without the cluster breaking.
func TestGossipHandlesReplacedNode(t *testing.T) {
	defer leaktest.AfterTest(t)()
	ctx := context.Background()

	// Shorten the raft tick interval and election timeout to make range leases
	// much shorter than normal. This keeps us from having to wait so long for
	// the replaced node's leases to time out, but has still shown itself to be
	// long enough to avoid flakes.
	serverArgs := base.TestServerArgs{
		RaftTickInterval:         50 * time.Millisecond,
		RaftElectionTimeoutTicks: 10,
		RetryOptions: retry.Options{
			InitialBackoff: 10 * time.Millisecond,
			MaxBackoff:     50 * time.Millisecond,
		},
	}

	tc := testcluster.StartTestCluster(t, 3,
		base.TestClusterArgs{
			ReplicationMode: base.ReplicationAuto,
			ServerArgs:      serverArgs,
		})
	defer tc.Stopper().Stop()

	// Take down a node other than the first node and replace it with a new one.
	// Replacing the first node would be better from an adversarial testing
	// perspective because it typically has the most leases on it, but that also
	// causes the test to take significantly longer as a result.
	oldNodeIdx := 0
	newServerArgs := serverArgs
	newServerArgs.Addr = tc.Servers[oldNodeIdx].ServingAddr()
	newServerArgs.PartOfCluster = true
	newServerArgs.JoinAddr = tc.Servers[1].ServingAddr()
	tc.StopServer(oldNodeIdx)
	tc.AddServer(t, newServerArgs)
	tc.WaitForStores(t, tc.Server(1).Gossip())

	// Ensure that all servers still running are responsive. If the two remaining
	// original nodes don't refresh their connection to the address of the first
	// node, they can get stuck here.
	for i, server := range tc.Servers {
		if i == oldNodeIdx {
			continue
		}
		kvClient := server.KVClient().(*client.DB)
		if err := kvClient.Put(ctx, fmt.Sprintf("%d", i), i); err != nil {
			t.Errorf("failed Put to node %d: %s", i, err)
		}
	}
}
<commit_msg>storage: Use IsolatedTestAddr in TestGossipHandlesReplacedNode<commit_after>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package storage_test

import (
	"fmt"
	"reflect"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/pkg/base"
	"github.com/cockroachdb/cockroach/pkg/gossip"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/keys"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
	"github.com/cockroachdb/cockroach/pkg/util"
	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
	"github.com/cockroachdb/cockroach/pkg/util/log"
	"github.com/cockroachdb/cockroach/pkg/util/retry"
)

func TestGossipFirstRange(t *testing.T) {
	defer leaktest.AfterTest(t)()

	tc := testcluster.StartTestCluster(t, 3,
		base.TestClusterArgs{
			ReplicationMode: base.ReplicationManual,
		})
	defer tc.Stopper().Stop()

	errors := make(chan error)
	descs := make(chan *roachpb.RangeDescriptor)
	unregister := tc.Servers[0].Gossip().RegisterCallback(gossip.KeyFirstRangeDescriptor,
		func(_ string, content roachpb.Value) {
			var desc roachpb.RangeDescriptor
			if err := content.GetProto(&desc); err != nil {
				errors <- err
			} else {
				descs <- &desc
			}
		},
	)
	// Unregister the callback before attempting to stop the stopper to prevent
	// deadlock. This is still flaky in theory since a callback can fire between
	// the last read from the channels and this unregister, but testing has
	// shown this solution to be sufficiently robust for now.
	defer unregister()

	// Wait for the specified descriptor to be gossiped for the first range. We
	// loop because the timing of replica addition and lease transfer can cause
	// extra gossiping of the first range.
	waitForGossip := func(desc roachpb.RangeDescriptor) {
		for {
			select {
			case err := <-errors:
				t.Fatal(err)
			case gossiped := <-descs:
				if reflect.DeepEqual(&desc, gossiped) {
					return
				}
				log.Infof(context.TODO(), "expected\n%+v\nbut found\n%+v", desc, gossiped)
			}
		}
	}

	// Expect an initial callback of the first range descriptor.
	select {
	case err := <-errors:
		t.Fatal(err)
	case <-descs:
	}

	// Add two replicas. The first range descriptor should be gossiped after each
	// addition.
	var desc roachpb.RangeDescriptor
	firstRangeKey := keys.MinKey
	for i := 1; i <= 2; i++ {
		var err error
		if desc, err = tc.AddReplicas(firstRangeKey, tc.Target(i)); err != nil {
			t.Fatal(err)
		}
		waitForGossip(desc)
	}

	// Transfer the lease to a new node. This should cause the first range to be
	// gossiped again.
	if err := tc.TransferRangeLease(desc, tc.Target(1)); err != nil {
		t.Fatal(err)
	}
	waitForGossip(desc)

	// Remove a non-lease holder replica.
	desc, err := tc.RemoveReplicas(firstRangeKey, tc.Target(0))
	if err != nil {
		t.Fatal(err)
	}
	waitForGossip(desc)

	// TODO(peter): Re-enable or remove when we've resolved the discussion
	// about removing the lease-holder replica. See #7872.

	// // Remove the lease holder replica.
	// leaseHolder, err := tc.FindRangeLeaseHolder(desc, nil)
	// desc, err = tc.RemoveReplicas(firstRangeKey, leaseHolder)
	// if err != nil {
	// 	t.Fatal(err)
	// }
	// select {
	// case err := <-errors:
	// 	t.Fatal(err)
	// case gossiped := <-descs:
	// 	if !reflect.DeepEqual(desc, gossiped) {
	// 		t.Fatalf("expected\n%+v\nbut found\n%+v", desc, gossiped)
	// 	}
	// }
}

// TestGossipHandlesReplacedNode tests that we can shut down a node and
// replace it with a new node at the same address (simulating a node getting
// restarted after losing its data) without the cluster breaking.
func TestGossipHandlesReplacedNode(t *testing.T) {
	defer leaktest.AfterTest(t)()
	ctx := context.Background()

	// Shorten the raft tick interval and election timeout to make range leases
	// much shorter than normal. This keeps us from having to wait so long for
	// the replaced node's leases to time out, but has still shown itself to be
	// long enough to avoid flakes.
	serverArgs := base.TestServerArgs{
		Addr:                     util.IsolatedTestAddr.String(),
		Insecure:                 true, // because our certs are only valid for 127.0.0.1
		RaftTickInterval:         50 * time.Millisecond,
		RaftElectionTimeoutTicks: 10,
		RetryOptions: retry.Options{
			InitialBackoff: 10 * time.Millisecond,
			MaxBackoff:     50 * time.Millisecond,
		},
	}

	tc := testcluster.StartTestCluster(t, 3,
		base.TestClusterArgs{
			ReplicationMode: base.ReplicationAuto,
			ServerArgs:      serverArgs,
		})
	defer tc.Stopper().Stop()

	// Take down a node other than the first node and replace it with a new one.
	// Replacing the first node would be better from an adversarial testing
	// perspective because it typically has the most leases on it, but that also
	// causes the test to take significantly longer as a result.
	oldNodeIdx := 0
	newServerArgs := serverArgs
	newServerArgs.Addr = tc.Servers[oldNodeIdx].ServingAddr()
	newServerArgs.PartOfCluster = true
	newServerArgs.JoinAddr = tc.Servers[1].ServingAddr()
	tc.StopServer(oldNodeIdx)
	tc.AddServer(t, newServerArgs)
	tc.WaitForStores(t, tc.Server(1).Gossip())

	// Ensure that all servers still running are responsive. If the two remaining
	// original nodes don't refresh their connection to the address of the first
	// node, they can get stuck here.
	for i, server := range tc.Servers {
		if i == oldNodeIdx {
			continue
		}
		kvClient := server.KVClient().(*client.DB)
		if err := kvClient.Put(ctx, fmt.Sprintf("%d", i), i); err != nil {
			t.Errorf("failed Put to node %d: %s", i, err)
		}
	}
}
<|endoftext|>


<commit_before>package system

import (
	"bytes"
	"syscall"

	"golang.org/x/sys/unix"
)

const (
	// Value is larger than the maximum size allowed
	E2BIG syscall.Errno = unix.E2BIG

	// Operation not supported
	EOPNOTSUPP syscall.Errno = unix.EOPNOTSUPP
)

// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
// It will returns a nil slice and nil error if the xattr is not set.
func Lgetxattr(path string, attr string) ([]byte, error) {
	// Start with a 128 length byte array
	dest := make([]byte, 128)
	sz, errno := unix.Lgetxattr(path, attr, dest)

	switch {
	case errno == unix.ENODATA:
		return nil, nil
	case errno == unix.ERANGE:
		// 128 byte array might just not be good enough. A dummy buffer is used
		// to get the real size of the xattrs on disk
		sz, errno = unix.Lgetxattr(path, attr, []byte{})
		if errno != nil {
			return nil, errno
		}
		dest = make([]byte, sz)
		sz, errno = unix.Lgetxattr(path, attr, dest)
		if errno != nil {
			return nil, errno
		}
	case errno != nil:
		return nil, errno
	}
	return dest[:sz], nil
}

// Lsetxattr sets the value of the extended attribute identified by attr
// and associated with the given path in the file system.
func Lsetxattr(path string, attr string, data []byte, flags int) error {
	return unix.Lsetxattr(path, attr, data, flags)
}

// Llistxattr lists extended attributes associated with the given path
// in the file system.
func Llistxattr(path string) ([]string, error) {
	var dest []byte

	for {
		sz, err := unix.Llistxattr(path, dest)
		if err != nil {
			return nil, err
		}

		if sz > len(dest) {
			dest = make([]byte, sz)
		} else {
			dest = dest[:sz]
			break
		}
	}

	var attrs []string
	for _, token := range bytes.Split(dest, []byte{0}) {
		if len(token) > 0 {
			attrs = append(attrs, string(token))
		}
	}

	return attrs, nil
}
<commit_msg>pkg/system/xattrs: minor cleanup<commit_after>package system

import (
	"bytes"

	"golang.org/x/sys/unix"
)

const (
	// Value is larger than the maximum size allowed
	E2BIG unix.Errno = unix.E2BIG

	// Operation not supported
	EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP
)

// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
// It will returns a nil slice and nil error if the xattr is not set.
func Lgetxattr(path string, attr string) ([]byte, error) {
	// Start with a 128 length byte array
	dest := make([]byte, 128)
	sz, errno := unix.Lgetxattr(path, attr, dest)

	switch {
	case errno == unix.ENODATA:
		return nil, nil
	case errno == unix.ERANGE:
		// 128 byte array might just not be good enough. A dummy buffer is used
		// to get the real size of the xattrs on disk
		sz, errno = unix.Lgetxattr(path, attr, []byte{})
		if errno != nil {
			return nil, errno
		}
		dest = make([]byte, sz)
		sz, errno = unix.Lgetxattr(path, attr, dest)
		if errno != nil {
			return nil, errno
		}
	case errno != nil:
		return nil, errno
	}
	return dest[:sz], nil
}

// Lsetxattr sets the value of the extended attribute identified by attr
// and associated with the given path in the file system.
func Lsetxattr(path string, attr string, data []byte, flags int) error {
	return unix.Lsetxattr(path, attr, data, flags)
}

// Llistxattr lists extended attributes associated with the given path
// in the file system.
func Llistxattr(path string) ([]string, error) {
	var dest []byte

	for {
		sz, err := unix.Llistxattr(path, dest)
		if err != nil {
			return nil, err
		}

		if sz > len(dest) {
			dest = make([]byte, sz)
		} else {
			dest = dest[:sz]
			break
		}
	}

	var attrs []string
	for _, token := range bytes.Split(dest, []byte{0}) {
		if len(token) > 0 {
			attrs = append(attrs, string(token))
		}
	}

	return attrs, nil
}
<|endoftext|>


<commit_before>package git

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"net/http"
	"net/url"
	"io"
	"os"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
	TMPDIR_PREFIX = "octorunner-"
	TMPFILE_PREFIX = "archive-"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(hookPayload){
		"push": handlePush,
	}

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for \"" + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Error("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// The repository that this payload is for might have a secret configured, in which case we expect
	// a signature with the payload. The given signature then needs to match a signature we calculate ourselves.
	// Only then will we call our handler, else we'll log an error and return
	repoSecret := Auth.RequestSecret(payload.Repository.FullName)
	if len(repoSecret) == 0 {
		log.Error("No secret was configured, cannot verify their signature")
	} else {
		signature := r.Header.Get(SIGNATUREHEADER)
		if signature == "" {
			log.Error("Expected signature for payload, but none given")
			return
		}
		log.Debug("Received signature " + signature)
		calculatedSignature := "sha1=" + authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if !authentication.CompareSignatures([]byte(signature), []byte(calculatedSignature)) {
			log.Error("Signatures didn't match")
			return
		}
	}
	eventHandler(payload)
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(payload hookPayload) {
	log.Info("Handling received push event")

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After
	getArchive(repoName, repoOwner, commitId, repoToken)
}

func getArchive(repoName string, repoOwner string, commitId string, repoToken *oauth2.Token) string {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error
	var httpClient *http.Client

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			log.Error("Error while constructing archive URL: ", err)
			return ""
		}
		httpClient = &http.Client{}
	} else {
		httpClient = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(repoToken))
		gitClient := github.NewClient(httpClient)
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			log.Error("Error while getting archive URL: ", err)
			return ""
		}
	}
	log.Debug("Found archive URL ", archiveUrl)

	tmpDir, err := ioutil.TempDir("", TMPDIR_PREFIX)
	if err != nil {
		log.Error("Error while creating temporary directory: ", err)
	}

	log.Debug("Created temporary directory " + tmpDir)
	archivePath, err := downloadFile(httpClient, archiveUrl, tmpDir)
	if err != nil {
		log.Error("Error while downloading archive: ", err)
	}
	defer archivePath.Close()
	log.Debug("Archive downloaded to ", archivePath.Name())

	return "stub"
}

func downloadFile(httpClient *http.Client, url *url.URL, downloadDirectory string) (*os.File, error) {
	log.Debug("Downloading \"" + url.String() + "\"")
	filePath, err := ioutil.TempFile(downloadDirectory, TMPFILE_PREFIX)
	if err != nil {
		return nil, err
	}

	resp, err := httpClient.Get(url.String())
	defer resp.Body.Close()
	n, err := io.Copy(filePath, resp.Body)
	if err != nil {
		return nil, err
	} else {
		log.Debugf("Downloaded %d bytes", n)
		return filePath, nil
	}
}
<commit_msg>Finished getRepository function<commit_after>package git

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	authentication "github.com/boyvanduuren/octorunner/lib/auth"
	zip "github.com/boyvanduuren/octorunner/lib/zip"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"net/http"
	"net/url"
	"io"
	"os"
	"path"
)

const (
	EVENTHEADER     = "X-GitHub-Event"
	FORWARDEDHEADER = "X-Forwarded-For"
	SIGNATUREHEADER = "X-Hub-Signature"
	TMPDIR_PREFIX = "octorunner-"
	TMPFILE_PREFIX = "archive-"
)

var Auth authentication.AuthMethod

type hookPayload struct {
	Ref, Before, After, Compare string
	Created, Deleted, Forced    bool
	Repository                  struct {
		Id       int
		Name     string
		FullName string `json:"full_name"`
		Owner    struct {
			Name string `json:"name"`
		} `json:"owner"`
		Private bool
	} `json:"repository"`
	Pusher struct {
		Name, Email string
	} `json:"pusher"`
	Sender struct {
		Login string
		Id    int
	} `json:"sender"`
}

// HandleWebhook is called when we receive a request on our listener and is responsible
// for decoding the payload and passing it to the appropriate handler for that particular event.
// If the received event is not supported we log an error and return without doing anything.
func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Map Github webhook events to functions that handle them
	supportedEvents := map[string]func(hookPayload){
		"push": handlePush,
	}

	log.Info("Received request on listener")
	// Request might be proxied, so check if there's an X-Forwarded-For header
	forwardedFor := r.Header.Get(FORWARDEDHEADER)
	var remoteAddr string
	if forwardedFor != "" {
		remoteAddr = forwardedFor
	} else {
		remoteAddr = r.RemoteAddr
	}
	log.Debug("Request from " + r.UserAgent() + " at " + remoteAddr)

	// Check which event we received and assign the appropriate handler to eventHandler
	var eventHandler func(hookPayload)
	event := r.Header.Get(EVENTHEADER)
	if event == "" {
		log.Error("Header \"" + EVENTHEADER + "\" not set, returning")
		return
	} else if val, exists := supportedEvents[event]; exists {
		eventHandler = val
		log.Debug("Found appropriate handler for \"" + event + "\" event")
	} else {
		log.Error("Received \"" + EVENTHEADER + "\", but found no supporting handler for \"" +
			event + "\" event, returning")
		return
	}

	// Read the body of the request
	payloadBody, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Error("Error while reading payload: %v", err)
	} else {
		log.Debug("Received body ", string(payloadBody))
	}

	// Try to decode the payload
	jsonDecoder := json.NewDecoder(bytes.NewReader(payloadBody))
	var payload hookPayload
	err = jsonDecoder.Decode(&payload)
	if err != nil {
		log.Error("Error while decoding payload: ", err)
		return
	}
	log.Debug("Decoded payload to ", payload)

	// The repository that this payload is for might have a secret configured, in which case we expect
	// a signature with the payload. The given signature then needs to match a signature we calculate ourselves.
	// Only then will we call our handler, else we'll log an error and return
	repoSecret := Auth.RequestSecret(payload.Repository.FullName)
	if len(repoSecret) == 0 {
		log.Error("No secret was configured, cannot verify their signature")
	} else {
		signature := r.Header.Get(SIGNATUREHEADER)
		if signature == "" {
			log.Error("Expected signature for payload, but none given")
			return
		}
		log.Debug("Received signature " + signature)
		calculatedSignature := "sha1=" + authentication.CalculateSignature(repoSecret, payloadBody)
		log.Debug("Calculated signature ", calculatedSignature)
		if !authentication.CompareSignatures([]byte(signature), []byte(calculatedSignature)) {
			log.Error("Signatures didn't match")
			return
		}
	}
	eventHandler(payload)
}

// Handle a push event to a Github repository. We will need to look at the settings for octorunner
// in this repository and take action accordingly.
func handlePush(payload hookPayload) {
	log.Info("Handling received push event")

	repoPrivate := payload.Repository.Private
	repoFullName := payload.Repository.FullName
	repoToken := Auth.RequestToken(repoFullName)

	log.Info("Repository \"" + repoFullName + "\" was pushed to")

	// In case of a private repository we'll need to see if we have credentials for it, because if we don't
	// we cannot download the repository from github
	if repoPrivate {
		log.Debug("Repository is private, looking up credentials")
		if repoToken == nil {
			log.Error("No token found for repository \"" + repoFullName + "\", returning")
			return
		}
	}

	repoName := payload.Repository.Name
	repoOwner := payload.Repository.Owner.Name
	commitId := payload.After
	getRepository(repoName, repoOwner, commitId, repoToken)
}

func getRepository(repoName string, repoOwner string, commitId string, repoToken *oauth2.Token) string {
	const GITHUB_ARCHIVE_URL = "https://github.com/%s/%s/archive/%s.zip"
	const GITHUB_ARCHIVE_ROOTDIR = "%s-%s-%s"
	const GITHUB_ARCHIVE_FORMAT = "zipball"
	var archiveUrl *url.URL
	var err error
	var httpClient *http.Client

	log.Info("Downloading archive of latest commit in push")
	if repoToken == nil {
		// no repoToken, so this is a public repository
		archiveUrl, err = url.Parse(fmt.Sprintf(GITHUB_ARCHIVE_URL, repoOwner, repoName, commitId))
		if err != nil {
			log.Error("Error while constructing archive URL: ", err)
			return ""
		}
		httpClient = &http.Client{}
	} else {
		httpClient = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(repoToken))
		gitClient := github.NewClient(httpClient)
		log.Debug("Getting archive URL for \"" + repoOwner + "/" + repoName + "\", ref \"" + commitId + "\"")
		archiveUrl, _, err = gitClient.Repositories.GetArchiveLink(repoOwner, repoName, GITHUB_ARCHIVE_FORMAT,
			&github.RepositoryContentGetOptions{Ref: commitId})
		if err != nil {
			log.Error("Error while getting archive URL: ", err)
			return ""
		}
	}
	log.Debug("Found archive URL ", archiveUrl)

	tmpDir, err := ioutil.TempDir("", TMPDIR_PREFIX)
	if err != nil {
		log.Error("Error while creating temporary directory: ", err)
		return ""
	}

	log.Debug("Created temporary directory " + tmpDir)
	archivePath, err := downloadFile(httpClient, archiveUrl, tmpDir)
	if err != nil {
		log.Error("Error while downloading archive: ", err)
		return ""
	}
	log.Debug("Archive downloaded to ", archivePath.Name())

	err = zip.Unzip(archivePath.Name(), tmpDir)
	if err != nil {
		log.Error("Error while unpacking archive: ", err)
		return ""
	}

	// cleanup the archive
	archivePath.Close()
	os.Remove(archivePath.Name())

	// we should now have a copy of the repository at the latest commit
	repoDir := path.Join(tmpDir, fmt.Sprintf(GITHUB_ARCHIVE_ROOTDIR, repoOwner, repoName, commitId))
	if s, err := os.Stat(repoDir); os.IsNotExist(err) == true || !s.IsDir() {
		log.Error("Repository not found at expected directory ", repoDir, " after unpacking")
		return ""
	}

	log.Debug("Repository unpacked to ", repoDir)

	return repoDir
}

func downloadFile(httpClient *http.Client, url *url.URL, downloadDirectory string) (*os.File, error) {
	log.Debug("Downloading \"" + url.String() + "\"")
	filePath, err := ioutil.TempFile(downloadDirectory, TMPFILE_PREFIX)
	if err != nil {
		return nil, err
	}

	resp, err := httpClient.Get(url.String())
	defer resp.Body.Close()
	n, err := io.Copy(filePath, resp.Body)
	if err != nil {
		return nil, err
	} else {
		log.Debugf("Downloaded %d bytes", n)
		return filePath, nil
	}
}

<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"time"

	biagentclient "github.com/cloudfoundry/bosh-init/deployment/agentclient"
	bias "github.com/cloudfoundry/bosh-init/deployment/applyspec"
	bihttpclient "github.com/cloudfoundry/bosh-init/deployment/httpclient"
	bosherr "github.com/cloudfoundry/bosh-utils/errors"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshretry "github.com/cloudfoundry/bosh-utils/retrystrategy"
)

type agentClient struct {
	agentRequest agentRequest
	getTaskDelay time.Duration
	logger       boshlog.Logger
	logTag       string
}

func NewAgentClient(
	endpoint string,
	directorID string,
	getTaskDelay time.Duration,
	httpClient bihttpclient.HTTPClient,
	logger boshlog.Logger,
) biagentclient.AgentClient {
	// if this were NATS, we would need the agentID, but since it's http, the endpoint is unique to the agent
	agentEndpoint := fmt.Sprintf("%s/agent", endpoint)
	agentRequest := agentRequest{
		directorID: directorID,
		endpoint:   agentEndpoint,
		httpClient: httpClient,
	}
	return &agentClient{
		agentRequest: agentRequest,
		getTaskDelay: getTaskDelay,
		logger:       logger,
		logTag:       "httpAgentClient",
	}
}

func (c *agentClient) Ping() (string, error) {
	var response SimpleTaskResponse
	err := c.agentRequest.Send("ping", []interface{}{}, &response)
	if err != nil {
		return "", bosherr.WrapError(err, "Sending ping to the agent")
	}

	return response.Value, nil
}

func (c *agentClient) Stop() error {
	_, err := c.sendAsyncTaskMessage("stop", []interface{}{})
	return err
}

func (c *agentClient) Apply(spec bias.ApplySpec) error {
	_, err := c.sendAsyncTaskMessage("apply", []interface{}{spec})
	return err
}

func (c *agentClient) Start() error {
	var response SimpleTaskResponse
	err := c.agentRequest.Send("start", []interface{}{}, &response)
	if err != nil {
		return bosherr.WrapError(err, "Starting agent services")
	}

	if response.Value != "started" {
		return bosherr.Errorf("Failed to start agent services with response: '%s'", response)
	}

	return nil
}

func (c *agentClient) GetState() (biagentclient.AgentState, error) {
	var response StateResponse
	err := c.agentRequest.Send("get_state", []interface{}{}, &response)
	if err != nil {
		return biagentclient.AgentState{}, bosherr.WrapError(err, "Sending get_state to the agent")
	}

	agentState := biagentclient.AgentState{
		JobState: response.Value.JobState,
	}
	return agentState, nil
}

func (c *agentClient) ListDisk() ([]string, error) {
	var response ListResponse
	err := c.agentRequest.Send("list_disk", []interface{}{}, &response)
	if err != nil {
		return []string{}, bosherr.WrapError(err, "Sending 'list_disk' to the agent")
	}

	return response.Value, nil
}

func (c *agentClient) MountDisk(diskCID string) error {
	_, err := c.sendAsyncTaskMessage("mount_disk", []interface{}{diskCID})
	return err
}

func (c *agentClient) UnmountDisk(diskCID string) error {
	_, err := c.sendAsyncTaskMessage("unmount_disk", []interface{}{diskCID})
	return err
}

func (c *agentClient) MigrateDisk() error {
	_, err := c.sendAsyncTaskMessage("migrate_disk", []interface{}{})
	return err
}

func (c *agentClient) sendAsyncTaskMessage(method string, arguments []interface{}) (value map[string]interface{}, err error) {
	var response TaskResponse
	err = c.agentRequest.Send(method, arguments, &response)
	if err != nil {
		return value, bosherr.WrapErrorf(err, "Sending '%s' to the agent", method)
	}

	agentTaskID, err := response.TaskID()
	if err != nil {
		return value, bosherr.WrapError(err, "Getting agent task id")
	}

	getTaskRetryable := boshretry.NewRetryable(func() (bool, error) {
		var response TaskResponse
		err = c.agentRequest.Send("get_task", []interface{}{agentTaskID}, &response)
		if err != nil {
			return false, bosherr.WrapError(err, "Sending 'get_task' to the agent")
		}

		c.logger.Debug(c.logTag, "get_task response value: %#v", response.Value)

		taskState, err := response.TaskState()
		if err != nil {
			return false, bosherr.WrapError(err, "Getting task state")
		}

		if taskState != "running" {
			var ok bool
			value, ok = response.Value.(map[string]interface{})
			if !ok {
				c.logger.Warn(c.logTag, "Unable to parse get_task response value: %#v", response.Value)
			}
			return true, nil
		}

		return true, bosherr.Errorf("Task %s is still running", method)
	})

	getTaskRetryStrategy := boshretry.NewUnlimitedRetryStrategy(c.getTaskDelay, getTaskRetryable, c.logger)
	return value, getTaskRetryStrategy.Try()
}

func (c *agentClient) CompilePackage(packageSource biagentclient.BlobRef, compiledPackageDependencies []biagentclient.BlobRef) (compiledPackageRef biagentclient.BlobRef, err error) {
	dependencies := make(map[string]BlobRef, len(compiledPackageDependencies))
	for _, dependency := range compiledPackageDependencies {
		dependencies[dependency.Name] = BlobRef{
			Name:        dependency.Name,
			Version:     dependency.Version,
			SHA1:        dependency.SHA1,
			BlobstoreID: dependency.BlobstoreID,
		}
	}

	args := []interface{}{
		packageSource.BlobstoreID,
		packageSource.SHA1,
		packageSource.Name,
		packageSource.Version,
		dependencies,
	}

	responseValue, err := c.sendAsyncTaskMessage("compile_package", args)
	if err != nil {
		return biagentclient.BlobRef{}, bosherr.WrapError(err, "Sending 'compile_package' to the agent")
	}

	result, ok := responseValue["result"].(map[string]interface{})
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	sha1, ok := result["sha1"].(string)
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	blobstoreID, ok := result["blobstore_id"].(string)
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	compiledPackageRef = biagentclient.BlobRef{
		Name:        packageSource.Name,
		Version:     packageSource.Version,
		SHA1:        sha1,
		BlobstoreID: blobstoreID,
	}

	return compiledPackageRef, nil
}
<commit_msg>Fix issue with closure in gccgo (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66431 for details)<commit_after>package http

import (
	"fmt"
	"time"

	biagentclient "github.com/cloudfoundry/bosh-init/deployment/agentclient"
	bias "github.com/cloudfoundry/bosh-init/deployment/applyspec"
	bihttpclient "github.com/cloudfoundry/bosh-init/deployment/httpclient"
	bosherr "github.com/cloudfoundry/bosh-utils/errors"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshretry "github.com/cloudfoundry/bosh-utils/retrystrategy"
)

type agentClient struct {
	agentRequest agentRequest
	getTaskDelay time.Duration
	logger       boshlog.Logger
	logTag       string
}

func NewAgentClient(
	endpoint string,
	directorID string,
	getTaskDelay time.Duration,
	httpClient bihttpclient.HTTPClient,
	logger boshlog.Logger,
) biagentclient.AgentClient {
	// if this were NATS, we would need the agentID, but since it's http, the endpoint is unique to the agent
	agentEndpoint := fmt.Sprintf("%s/agent", endpoint)
	agentRequest := agentRequest{
		directorID: directorID,
		endpoint:   agentEndpoint,
		httpClient: httpClient,
	}
	return &agentClient{
		agentRequest: agentRequest,
		getTaskDelay: getTaskDelay,
		logger:       logger,
		logTag:       "httpAgentClient",
	}
}

func (c *agentClient) Ping() (string, error) {
	var response SimpleTaskResponse
	err := c.agentRequest.Send("ping", []interface{}{}, &response)
	if err != nil {
		return "", bosherr.WrapError(err, "Sending ping to the agent")
	}

	return response.Value, nil
}

func (c *agentClient) Stop() error {
	_, err := c.sendAsyncTaskMessage("stop", []interface{}{})
	return err
}

func (c *agentClient) Apply(spec bias.ApplySpec) error {
	_, err := c.sendAsyncTaskMessage("apply", []interface{}{spec})
	return err
}

func (c *agentClient) Start() error {
	var response SimpleTaskResponse
	err := c.agentRequest.Send("start", []interface{}{}, &response)
	if err != nil {
		return bosherr.WrapError(err, "Starting agent services")
	}

	if response.Value != "started" {
		return bosherr.Errorf("Failed to start agent services with response: '%s'", response)
	}

	return nil
}

func (c *agentClient) GetState() (biagentclient.AgentState, error) {
	var response StateResponse
	err := c.agentRequest.Send("get_state", []interface{}{}, &response)
	if err != nil {
		return biagentclient.AgentState{}, bosherr.WrapError(err, "Sending get_state to the agent")
	}

	agentState := biagentclient.AgentState{
		JobState: response.Value.JobState,
	}
	return agentState, nil
}

func (c *agentClient) ListDisk() ([]string, error) {
	var response ListResponse
	err := c.agentRequest.Send("list_disk", []interface{}{}, &response)
	if err != nil {
		return []string{}, bosherr.WrapError(err, "Sending 'list_disk' to the agent")
	}

	return response.Value, nil
}

func (c *agentClient) MountDisk(diskCID string) error {
	_, err := c.sendAsyncTaskMessage("mount_disk", []interface{}{diskCID})
	return err
}

func (c *agentClient) UnmountDisk(diskCID string) error {
	_, err := c.sendAsyncTaskMessage("unmount_disk", []interface{}{diskCID})
	return err
}

func (c *agentClient) MigrateDisk() error {
	_, err := c.sendAsyncTaskMessage("migrate_disk", []interface{}{})
	return err
}

func (c *agentClient) sendAsyncTaskMessage(method string, arguments []interface{}) (value map[string]interface{}, err error) {
	var response TaskResponse
	err = c.agentRequest.Send(method, arguments, &response)
	if err != nil {
		return value, bosherr.WrapErrorf(err, "Sending '%s' to the agent", method)
	}

	agentTaskID, err := response.TaskID()
	if err != nil {
		return value, bosherr.WrapError(err, "Getting agent task id")
	}

	getTaskRetryable := boshretry.NewRetryable(func() (bool, error) {
		var response TaskResponse
		err = c.agentRequest.Send("get_task", []interface{}{agentTaskID}, &response)
		if err != nil {
			return false, bosherr.WrapError(err, "Sending 'get_task' to the agent")
		}

		c.logger.Debug(c.logTag, "get_task response value: %#v", response.Value)

		taskState, err := response.TaskState()
		if err != nil {
			return false, bosherr.WrapError(err, "Getting task state")
		}

		if taskState != "running" {
			var ok bool
			value, ok = response.Value.(map[string]interface{})
			if !ok {
				c.logger.Warn(c.logTag, "Unable to parse get_task response value: %#v", response.Value)
			}
			return true, nil
		}

		return true, bosherr.Errorf("Task %s is still running", method)
	})

	getTaskRetryStrategy := boshretry.NewUnlimitedRetryStrategy(c.getTaskDelay, getTaskRetryable, c.logger)
	// cannot call getTaskRetryStrategy.Try in the return statement due to gccgo
	// execution order issues: https://code.google.com/p/go/issues/detail?id=8698&thanks=8698&ts=1410376474
	err = getTaskRetryStrategy.Try()
	return value, err
}

func (c *agentClient) CompilePackage(packageSource biagentclient.BlobRef, compiledPackageDependencies []biagentclient.BlobRef) (compiledPackageRef biagentclient.BlobRef, err error) {
	dependencies := make(map[string]BlobRef, len(compiledPackageDependencies))
	for _, dependency := range compiledPackageDependencies {
		dependencies[dependency.Name] = BlobRef{
			Name:        dependency.Name,
			Version:     dependency.Version,
			SHA1:        dependency.SHA1,
			BlobstoreID: dependency.BlobstoreID,
		}
	}

	args := []interface{}{
		packageSource.BlobstoreID,
		packageSource.SHA1,
		packageSource.Name,
		packageSource.Version,
		dependencies,
	}

	responseValue, err := c.sendAsyncTaskMessage("compile_package", args)
	if err != nil {
		return biagentclient.BlobRef{}, bosherr.WrapError(err, "Sending 'compile_package' to the agent")
	}

	result, ok := responseValue["result"].(map[string]interface{})
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	sha1, ok := result["sha1"].(string)
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	blobstoreID, ok := result["blobstore_id"].(string)
	if !ok {
		return biagentclient.BlobRef{}, bosherr.Errorf("Unable to parse 'compile_package' response from the agent: %#v", responseValue)
	}

	compiledPackageRef = biagentclient.BlobRef{
		Name:        packageSource.Name,
		Version:     packageSource.Version,
		SHA1:        sha1,
		BlobstoreID: blobstoreID,
	}

	return compiledPackageRef, nil
}
<|endoftext|>


<commit_before>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, 2014 all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package isvcs

import (
	"github.com/fsouza/go-dockerclient"
	"github.com/zenoss/glog"

	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strings"
	"time"
)

type containerOp int

const (
	containerOpStart containerOp = iota
	containerOpStop
)

type containerOpRequest struct {
	op       containerOp
	response chan error
}

var ErrNotRunning error
var ErrRunning error
var ErrBadContainerSpec error
var volumesDir string

func init() {
	ErrNotRunning = errors.New("container: not running")
	ErrRunning = errors.New("container: already running")
	ErrBadContainerSpec = errors.New("container: bad container specification")

	if user, err := user.Current(); err != nil {
		volumesDir = "/tmp/serviced/isvcs_volumes"
	} else {
		volumesDir = fmt.Sprintf("/tmp/serviced-%s/isvcs_volumes", user.Username)
	}
}

type ContainerDescription struct {
	Name          string                              // name of the container (used for docker named containers)
	Repo          string                              // the repository the image for this container uses
	Tag           string                              // the repository tag this container uses
	Command       string                              // the actual command to run inside the container
	Volumes       map[string]string                   // Volumes to bind mount in to the containers
	Ports         []int                               // Ports to expose to the host
	HealthCheck   func() error                        // A function to verify that the service is healthy
	Configuration interface{}                         // A container specific configuration
	Reload        func(*Container, interface{}) error // A function to run when asked to reload configuration
}

type Container struct {
	ContainerDescription
	ops chan containerOpRequest // channel for communicating to the container's loop
}

func NewContainer(cd ContainerDescription) (*Container, error) {
	if len(cd.Name) == 0 || len(cd.Repo) == 0 || len(cd.Tag) == 0 || len(cd.Command) == 0 {
		return nil, ErrBadContainerSpec
	}
	c := Container{
		ContainerDescription: cd,
		ops:                  make(chan containerOpRequest),
	}
	go c.loop()
	return &c, nil
}

// loop maintains the state of the container; it handles requests to start() &
// stop() containers as well as detect container failures.
func (c *Container) loop() {

	var exitChan chan error
	var cmd *exec.Cmd

	for {
		select {
		case req := <-c.ops:
			switch req.op {
			case containerOpStop:
				glog.Infof("containerOpStop(): %s", c.Name)
				if exitChan == nil {
					req.response <- ErrNotRunning
					continue
				}
				oldCmd := cmd
				cmd = nil
				exitChan = nil
				oldCmd.Process.Kill()
				c.stop()
				c.rm()
				req.response <- nil

			case containerOpStart:
				glog.Infof("containerOpStart(): %s", c.Name)
				if cmd != nil {
					req.response <- ErrRunning
					continue
				}
				c.stop()
				c.rm()
				cmd, exitChan = c.run()
				if c.HealthCheck != nil {
					req.response <- c.HealthCheck()
				} else {
					req.response <- nil
				}

			}
		case exitErr := <-exitChan:
			docker := exec.Command("docker", "logs", c.Name)
			output, _ := docker.CombinedOutput()
			glog.Errorf("isvc:%s, %s", c.Name, string(output))
			glog.Errorf("Unexpected failure of %s, got %s", c.Name, exitErr)
			time.Sleep(time.Second * 30)
			glog.Fatalf("iscv:%s, process exited: %s", cmd.ProcessState.Exited())
			cmd, exitChan = c.run()
		}
	}
}

// attempt to stop all matching containers
func (c *Container) stop() error {
	client, err := newDockerClient("unix:///var/run/docker.sock")
	if err != nil {
		glog.Errorf("Could not create docker client: %s", err)
		return err
	}
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}
	for _, container := range containers {
		for _, name := range container.Names {
			if strings.HasPrefix(name, "/"+c.Name) {
				client.StopContainer(container.ID, 20)
			}
		}
	}
	return nil
}

// attempt to remove all matching containers
func (c *Container) rm() error {
	client, err := newDockerClient("unix:///var/run/docker.sock")
	if err != nil {
		glog.Errorf("Could not create docker client: %s", err)
		return err
	}
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}
	for _, container := range containers {
		for _, name := range container.Names {
			if strings.HasPrefix(name, "/"+c.Name) {
				err = client.RemoveContainer(container.ID)
			}
		}
	}
	return nil
}

// Run() an instance of this container and return it's exec.Command reference and a
// channel that sends the exit code, when the container exits
func (c *Container) run() (*exec.Cmd, chan error) {

	// the container name is semi random because containers can get wedged
	// in docker and can not be removed until a reboot (or aufs trickery)
	containerName := c.Name + "-" + uuid()

	exitChan := make(chan error, 1)
	args := make([]string, 0)
	args = append(args, "run", "-rm", "-name", containerName)

	// attach all exported ports
	for _, port := range c.Ports {
		args = append(args, "-p", fmt.Sprintf("%d:%d", port, port))
	}

	// attach resources directory to all containers
	args = append(args, "-v", resourcesDir()+":"+"/usr/local/serviced/resources")

	// attach all exported volumes
	for name, volume := range c.Volumes {
		hostDir := path.Join(volumesDir, c.Name, name)
		if exists, _ := isDir(hostDir); !exists {
			if err := os.MkdirAll(hostDir, 0777); err != nil {
				glog.Errorf("could not create %s on host: %s", hostDir, err)
				exitChan <- err
				return nil, exitChan
			}
		}
		args = append(args, "-v", hostDir+":"+volume)
	}

	// set the image and command to run
	args = append(args, c.Repo+":"+c.Tag, "/bin/sh", "-c", c.Command)

	glog.V(1).Infof("Executing docker %s", args)
	cmd := exec.Command("docker", args...)
	go func() {
		exitChan <- cmd.Run()
	}()
	return cmd, exitChan
}

// Start() a container by sending the loop() a request
func (c *Container) Start() error {
	req := containerOpRequest{
		op:       containerOpStart,
		response: make(chan error),
	}
	c.ops <- req
	return <-req.response
}

// Stop() a container by sending the loop() a request
func (c *Container) Stop() error {
	req := containerOpRequest{
		op:       containerOpStop,
		response: make(chan error),
	}
	c.ops <- req
	return <-req.response
}
<commit_msg>update comments around container loop()<commit_after>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, 2014 all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package isvcs

import (
	"github.com/fsouza/go-dockerclient"
	"github.com/zenoss/glog"

	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strings"
	"time"
)

type containerOp int

const (
	containerOpStart containerOp = iota
	containerOpStop
)

type containerOpRequest struct {
	op       containerOp
	response chan error
}

var ErrNotRunning error
var ErrRunning error
var ErrBadContainerSpec error
var volumesDir string

func init() {
	ErrNotRunning = errors.New("container: not running")
	ErrRunning = errors.New("container: already running")
	ErrBadContainerSpec = errors.New("container: bad container specification")

	if user, err := user.Current(); err != nil {
		volumesDir = "/tmp/serviced/isvcs_volumes"
	} else {
		volumesDir = fmt.Sprintf("/tmp/serviced-%s/isvcs_volumes", user.Username)
	}
}

type ContainerDescription struct {
	Name          string                              // name of the container (used for docker named containers)
	Repo          string                              // the repository the image for this container uses
	Tag           string                              // the repository tag this container uses
	Command       string                              // the actual command to run inside the container
	Volumes       map[string]string                   // Volumes to bind mount in to the containers
	Ports         []int                               // Ports to expose to the host
	HealthCheck   func() error                        // A function to verify that the service is healthy
	Configuration interface{}                         // A container specific configuration
	Reload        func(*Container, interface{}) error // A function to run when asked to reload configuration
}

type Container struct {
	ContainerDescription
	ops chan containerOpRequest // channel for communicating to the container's loop
}

func NewContainer(cd ContainerDescription) (*Container, error) {
	if len(cd.Name) == 0 || len(cd.Repo) == 0 || len(cd.Tag) == 0 || len(cd.Command) == 0 {
		return nil, ErrBadContainerSpec
	}
	c := Container{
		ContainerDescription: cd,
		ops:                  make(chan containerOpRequest),
	}
	go c.loop()
	return &c, nil
}

// loop maintains the state of the container; it handles requests to start() &
// stop() containers as well as detect container failures.
func (c *Container) loop() {

	var exitChan chan error
	var cmd *exec.Cmd

	for {
		select {
		case req := <-c.ops:
			switch req.op {
			case containerOpStop:
				glog.Infof("containerOpStop(): %s", c.Name)
				if exitChan == nil {
					req.response <- ErrNotRunning
					continue
				}
				oldCmd := cmd
				cmd = nil
				exitChan = nil // setting extChan to nil will disable reading from it in the select()
				oldCmd.Process.Kill() // kill the docker run() wrapper
				c.stop()              // stop the container if it's not already stopped
				c.rm()                // remove the container if it's not already gone
				req.response <- nil

			case containerOpStart:
				glog.Infof("containerOpStart(): %s", c.Name)
				if cmd != nil {
					req.response <- ErrRunning
					continue
				}
				c.stop() // stop the container, if it's not stoppped
				c.rm()   // remove it if it was not already removed
				cmd, exitChan = c.run() // run the actual container
				if c.HealthCheck != nil {
					req.response <- c.HealthCheck()  // run the HealthCheck if it exists
				} else {
					req.response <- nil
				}

			}
		case exitErr := <-exitChan:
			docker := exec.Command("docker", "logs", c.Name)
			output, _ := docker.CombinedOutput()
			glog.Errorf("isvc:%s, %s", c.Name, string(output))
			glog.Errorf("Unexpected failure of %s, got %s", c.Name, exitErr)
			glog.Fatalf("iscv:%s, process exited: %s", cmd.ProcessState.Exited())
		}
	}
}

// attempt to stop all matching containers
func (c *Container) stop() error {
	client, err := newDockerClient("unix:///var/run/docker.sock")
	if err != nil {
		glog.Errorf("Could not create docker client: %s", err)
		return err
	}
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}
	for _, container := range containers {
		for _, name := range container.Names {
			if strings.HasPrefix(name, "/"+c.Name) {
				client.StopContainer(container.ID, 20)
			}
		}
	}
	return nil
}

// attempt to remove all matching containers
func (c *Container) rm() error {
	client, err := newDockerClient("unix:///var/run/docker.sock")
	if err != nil {
		glog.Errorf("Could not create docker client: %s", err)
		return err
	}
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}
	for _, container := range containers {
		for _, name := range container.Names {
			if strings.HasPrefix(name, "/"+c.Name) {
				err = client.RemoveContainer(container.ID)
			}
		}
	}
	return nil
}

// Run() an instance of this container and return it's exec.Command reference and a
// channel that sends the exit code, when the container exits
func (c *Container) run() (*exec.Cmd, chan error) {

	// the container name is semi random because containers can get wedged
	// in docker and can not be removed until a reboot (or aufs trickery)
	containerName := c.Name + "-" + uuid()

	exitChan := make(chan error, 1)
	args := make([]string, 0)
	args = append(args, "run", "-rm", "-name", containerName)

	// attach all exported ports
	for _, port := range c.Ports {
		args = append(args, "-p", fmt.Sprintf("%d:%d", port, port))
	}

	// attach resources directory to all containers
	args = append(args, "-v", resourcesDir()+":"+"/usr/local/serviced/resources")

	// attach all exported volumes
	for name, volume := range c.Volumes {
		hostDir := path.Join(volumesDir, c.Name, name)
		if exists, _ := isDir(hostDir); !exists {
			if err := os.MkdirAll(hostDir, 0777); err != nil {
				glog.Errorf("could not create %s on host: %s", hostDir, err)
				exitChan <- err
				return nil, exitChan
			}
		}
		args = append(args, "-v", hostDir+":"+volume)
	}

	// set the image and command to run
	args = append(args, c.Repo+":"+c.Tag, "/bin/sh", "-c", c.Command)

	glog.V(1).Infof("Executing docker %s", args)
	cmd := exec.Command("docker", args...)
	go func() {
		exitChan <- cmd.Run()
	}()
	return cmd, exitChan
}

// Start() a container by sending the loop() a request
func (c *Container) Start() error {
	req := containerOpRequest{
		op:       containerOpStart,
		response: make(chan error),
	}
	c.ops <- req
	return <-req.response
}

// Stop() a container by sending the loop() a request
func (c *Container) Stop() error {
	req := containerOpRequest{
		op:       containerOpStop,
		response: make(chan error),
	}
	c.ops <- req
	return <-req.response
}
<|endoftext|>