<commit_before>package git

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/argoproj/argo-cd/util"
	certutil "github.com/argoproj/argo-cd/util/cert"
)

type Creds interface {
	Environ() (io.Closer, []string, error)
}

// nop implementation
type NopCloser struct {
}

func (c NopCloser) Close() error {
	return nil
}

type NopCreds struct {
}

func (c NopCreds) Environ() (io.Closer, []string, error) {
	return NopCloser{}, nil, nil
}

// HTTPS creds implementation
type HTTPSCreds struct {
	// Username for authentication
	username string
	// Password for authentication
	password string
	// Whether to ignore invalid server certificates
	insecure bool
	// Client certificate to use
	clientCertData string
	// Client certificate key to use
	clientCertKey string
}

func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool) HTTPSCreds {
	return HTTPSCreds{
		username,
		password,
		insecure,
		clientCertData,
		clientCertKey,
	}
}

// Get additional required environment variables for executing git client to
// access specific repository via HTTPS.
func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
	env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), fmt.Sprintf("GIT_USERNAME=%s", c.username), fmt.Sprintf("GIT_PASSWORD=%s", c.password)}
	httpCloser := authFilePaths(make([]string, 0))

	// GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at
	// all.
	if c.insecure {
		env = append(env, "GIT_SSL_NO_VERIFY=true")
	}

	// In case the repo is configured for using a TLS client cert, we need to make
	// sure git client will use it. The certificate's key must not be password
	// protected.
	if c.clientCertData != "" && c.clientCertKey != "" {
		var certFile, keyFile *os.File

		// We need to actually create two temp files, one for storing cert data and
		// another for storing the key. If we fail to create second fail, the first
		// must be removed.
		certFile, err := ioutil.TempFile(util.TempDir, "")
		if err == nil {
			defer certFile.Close()
			keyFile, err = ioutil.TempFile(util.TempDir, "")
			if err != nil {
				removeErr := os.Remove(certFile.Name())
				if removeErr != nil {
					log.Errorf("Could not remove previously created tempfile %s: %v", certFile.Name(), removeErr)
				}
				return NopCloser{}, nil, err
			}
			defer keyFile.Close()
		} else {
			return NopCloser{}, nil, err
		}

		// We should have both temp files by now
		httpCloser = authFilePaths([]string{certFile.Name(), keyFile.Name()})

		_, err = certFile.WriteString(c.clientCertData)
		if err != nil {
			httpCloser.Close()
			return NopCloser{}, nil, err
		}
		// GIT_SSL_CERT is the full path to a client certificate to be used
		env = append(env, fmt.Sprintf("GIT_SSL_CERT=%s", certFile.Name()))

		_, err = keyFile.WriteString(c.clientCertKey)
		if err != nil {
			httpCloser.Close()
			return NopCloser{}, nil, err
		}
		// GIT_SSL_KEY is the full path to a client certificate's key to be used
		env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name()))

	}
	return httpCloser, env, nil
}

// SSH implementation
type SSHCreds struct {
	sshPrivateKey string
	caPath        string
	insecure      bool
}

func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool) SSHCreds {
	return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey}
}

type sshPrivateKeyFile string

type authFilePaths []string

func (f sshPrivateKeyFile) Close() error {
	return os.Remove(string(f))
}

// Remove a list of files that have been created as temp files while creating
// HTTPCreds object above.
func (f authFilePaths) Close() error {
	var retErr error = nil
	for _, path := range f {
		err := os.Remove(path)
		if err != nil {
			log.Errorf("HTTPSCreds.Close(): Could not remove temp file %s: %v", path, err)
			retErr = err
		}
	}
	return retErr
}

func (c SSHCreds) Environ() (io.Closer, []string, error) {
	// use the SHM temp dir from util, more secure
	file, err := ioutil.TempFile(util.TempDir, "")
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	_, err = file.WriteString(c.sshPrivateKey)
	if err != nil {
		return nil, nil, err
	}

	args := []string{"ssh", "-i", file.Name()}
	var env []string
	if c.caPath != "" {
		env = append(env, fmt.Sprintf("GIT_SSL_CAINFO=%s", c.caPath))
	}
	if c.insecure {
		log.Warn("temporarily disabling strict host key checking (i.e. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'), please don't use in production")
		// StrictHostKeyChecking will add the host to the knownhosts file,  we don't want that - a security issue really,
		// UserKnownHostsFile=/dev/null is therefore used so we write the new insecure host to /dev/null
		args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null")
	} else {
		knownHostsFile := certutil.GetSSHKnownHostsDataPath()
		args = append(args, "-o", "StrictHostKeyChecking=yes", "-o", fmt.Sprintf("UserKnownHostsFile=%s", knownHostsFile))
	}
	env = append(env, []string{fmt.Sprintf("GIT_SSH_COMMAND=%s", strings.Join(args, " "))}...)
	return sshPrivateKeyFile(file.Name()), env, nil
}
<commit_msg>fix: Ensure SSH private key is written out with a final newline character (#2890) (#3064)<commit_after>package git

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/argoproj/argo-cd/util"
	certutil "github.com/argoproj/argo-cd/util/cert"
)

type Creds interface {
	Environ() (io.Closer, []string, error)
}

// nop implementation
type NopCloser struct {
}

func (c NopCloser) Close() error {
	return nil
}

type NopCreds struct {
}

func (c NopCreds) Environ() (io.Closer, []string, error) {
	return NopCloser{}, nil, nil
}

// HTTPS creds implementation
type HTTPSCreds struct {
	// Username for authentication
	username string
	// Password for authentication
	password string
	// Whether to ignore invalid server certificates
	insecure bool
	// Client certificate to use
	clientCertData string
	// Client certificate key to use
	clientCertKey string
}

func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool) HTTPSCreds {
	return HTTPSCreds{
		username,
		password,
		insecure,
		clientCertData,
		clientCertKey,
	}
}

// Get additional required environment variables for executing git client to
// access specific repository via HTTPS.
func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
	env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), fmt.Sprintf("GIT_USERNAME=%s", c.username), fmt.Sprintf("GIT_PASSWORD=%s", c.password)}
	httpCloser := authFilePaths(make([]string, 0))

	// GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at
	// all.
	if c.insecure {
		env = append(env, "GIT_SSL_NO_VERIFY=true")
	}

	// In case the repo is configured for using a TLS client cert, we need to make
	// sure git client will use it. The certificate's key must not be password
	// protected.
	if c.clientCertData != "" && c.clientCertKey != "" {
		var certFile, keyFile *os.File

		// We need to actually create two temp files, one for storing cert data and
		// another for storing the key. If we fail to create second fail, the first
		// must be removed.
		certFile, err := ioutil.TempFile(util.TempDir, "")
		if err == nil {
			defer certFile.Close()
			keyFile, err = ioutil.TempFile(util.TempDir, "")
			if err != nil {
				removeErr := os.Remove(certFile.Name())
				if removeErr != nil {
					log.Errorf("Could not remove previously created tempfile %s: %v", certFile.Name(), removeErr)
				}
				return NopCloser{}, nil, err
			}
			defer keyFile.Close()
		} else {
			return NopCloser{}, nil, err
		}

		// We should have both temp files by now
		httpCloser = authFilePaths([]string{certFile.Name(), keyFile.Name()})

		_, err = certFile.WriteString(c.clientCertData)
		if err != nil {
			httpCloser.Close()
			return NopCloser{}, nil, err
		}
		// GIT_SSL_CERT is the full path to a client certificate to be used
		env = append(env, fmt.Sprintf("GIT_SSL_CERT=%s", certFile.Name()))

		_, err = keyFile.WriteString(c.clientCertKey)
		if err != nil {
			httpCloser.Close()
			return NopCloser{}, nil, err
		}
		// GIT_SSL_KEY is the full path to a client certificate's key to be used
		env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name()))

	}
	return httpCloser, env, nil
}

// SSH implementation
type SSHCreds struct {
	sshPrivateKey string
	caPath        string
	insecure      bool
}

func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool) SSHCreds {
	return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey}
}

type sshPrivateKeyFile string

type authFilePaths []string

func (f sshPrivateKeyFile) Close() error {
	return os.Remove(string(f))
}

// Remove a list of files that have been created as temp files while creating
// HTTPCreds object above.
func (f authFilePaths) Close() error {
	var retErr error = nil
	for _, path := range f {
		err := os.Remove(path)
		if err != nil {
			log.Errorf("HTTPSCreds.Close(): Could not remove temp file %s: %v", path, err)
			retErr = err
		}
	}
	return retErr
}

func (c SSHCreds) Environ() (io.Closer, []string, error) {
	// use the SHM temp dir from util, more secure
	file, err := ioutil.TempFile(util.TempDir, "")
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	_, err = file.WriteString(c.sshPrivateKey + "\n")
	if err != nil {
		return nil, nil, err
	}

	args := []string{"ssh", "-i", file.Name()}
	var env []string
	if c.caPath != "" {
		env = append(env, fmt.Sprintf("GIT_SSL_CAINFO=%s", c.caPath))
	}
	if c.insecure {
		log.Warn("temporarily disabling strict host key checking (i.e. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'), please don't use in production")
		// StrictHostKeyChecking will add the host to the knownhosts file,  we don't want that - a security issue really,
		// UserKnownHostsFile=/dev/null is therefore used so we write the new insecure host to /dev/null
		args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null")
	} else {
		knownHostsFile := certutil.GetSSHKnownHostsDataPath()
		args = append(args, "-o", "StrictHostKeyChecking=yes", "-o", fmt.Sprintf("UserKnownHostsFile=%s", knownHostsFile))
	}
	env = append(env, []string{fmt.Sprintf("GIT_SSH_COMMAND=%s", strings.Join(args, " "))}...)
	return sshPrivateKeyFile(file.Name()), env, nil
}
<|endoftext|>


<commit_before>package util

import "testing"

func TestSigns(t *testing.T) {
	tests := []struct {
		input  Sign
		output string
	}{
		{ClassSign("java.lang.Object"), "Ljava/lang/Object;"},
		{ClassSign("java.lang.String"), "Ljava/lang/String;"},
		{ArraySign(ObjectSign), "[Ljava/lang/Object;"},
		{ArraySign(StringSign), "[Ljava/lang/String;"},
		{ArraySign(IntSign), "[I"},
		{FuncSign(nil, IntSign), "()I"},
		{FuncSign([]Sign{}, IntSign), "()I"},
		{FuncSign([]Sign{BoolSign}, VoidSign), "(Z)V"},
		{FuncSign([]Sign{CharSign, ByteSign, ShortSign}, FloatSign), "(CBS)F"},
		{FuncSign([]Sign{ClassSign("io.veyron.veyron.veyron.testing.misc")}, ClassSign("io.veyron.veyron.veyron.ret")), "(Lio/core/veyron/veyron/testing/misc;)Lio/core/veyron/veyron/ret;"},
		{FuncSign([]Sign{ClassSign("io.veyron.veyron.veyron.testing.misc"), ClassSign("other")}, ClassSign("io.veyron.veyron.veyron.ret")), "(Lio/core/veyron/veyron/testing/misc;Lother;)Lio/core/veyron/veyron/ret;"},
	}
	for _, test := range tests {
		output := string(test.input)
		if output != test.output {
			t.Errorf("expected %v, got %v", test.output, output)
		}
	}
}
<commit_msg>Fix a failing test.<commit_after>package util

import "testing"

func TestSigns(t *testing.T) {
	tests := []struct {
		input  Sign
		output string
	}{
		{ClassSign("java.lang.Object"), "Ljava/lang/Object;"},
		{ClassSign("java.lang.String"), "Ljava/lang/String;"},
		{ArraySign(ObjectSign), "[Ljava/lang/Object;"},
		{ArraySign(StringSign), "[Ljava/lang/String;"},
		{ArraySign(IntSign), "[I"},
		{FuncSign(nil, IntSign), "()I"},
		{FuncSign([]Sign{}, IntSign), "()I"},
		{FuncSign([]Sign{BoolSign}, VoidSign), "(Z)V"},
		{FuncSign([]Sign{CharSign, ByteSign, ShortSign}, FloatSign), "(CBS)F"},
		{FuncSign([]Sign{ClassSign("io.v.core.veyron.testing.misc")}, ClassSign("io.v.core.veyron.ret")), "(Lio/v/core/veyron/testing/misc;)Lio/v/core/veyron/ret;"},
		{FuncSign([]Sign{ClassSign("io.v.core.veyron.testing.misc"), ClassSign("other")}, ClassSign("io.v.core.veyron.ret")), "(Lio/v/core/veyron/testing/misc;Lother;)Lio/v/core/veyron/ret;"},
	}
	for _, test := range tests {
		output := string(test.input)
		if output != test.output {
			t.Errorf("expected %v, got %v", test.output, output)
		}
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"strings"
	"time"
	"database/sql"
)

// NullTime represents a time.Time that may be null. NullTime implements the
// sql.Scanner interface so it can be used as a scan destination, similar to
// sql.NullString.
type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (nt *NullTime) Scan(value interface{}) error {
	nt.Time, nt.Valid = value.(time.Time)
	return nil
}

// Value implements the driver Valuer interface.
func (nt NullTime) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	}
	return nt.Time, nil
}

// DbUtils can be used to prepare queries by changing the sql param notations
// as defined by each supported database
type DbUtils struct {
	db     *sql.DB
	dbType string
	prefix string
}

func (u *DbUtils) setDbType(dbType string) {
	if len(dbType) == 0 || (dbType != "postgres" && dbType != "oci8" && dbType != "mysql") {
		panic("DbType must be one of: postgres, oci8 or mysql")
	}

	u.dbType = strings.ToLower(dbType)

	if u.dbType == "postgres" {
		u.prefix = "$"
	} else if u.dbType == "oci8" {
		u.prefix = ":"
	} else {
		u.prefix = ""
	}
}

//
// PQuery prepares query for run by changing params written as ? to $1, $2, etc
// for postgres and :1, :2, etc for oracle
func (u *DbUtils) PQuery(query string) string {
	q := query
	i := 1

	if len(u.prefix) > 0 {
		for {
			idx := strings.Index(q, "?")

			if idx < 0 {
				break
			}

			prm := fmt.Sprintf("%s%d", u.prefix, i)
			i++

			q = strings.Replace(q, "?", prm, 1)
		}
	}

	return q
}

func (u *DbUtils) Connect2Database(db **sql.DB, dbType, dbURL string) error {
	var err error
	u.setDbType(dbType)

	*db, err = sql.Open(dbType, dbURL)
	if err != nil {
		return errors.New("Can't connect to the database, go error " + fmt.Sprintf("%s", err))
	}

	err = (*db).Ping()
	if err != nil {
		return errors.New("Can't ping the database, go error " + fmt.Sprintf("%s", err))
	}

	u.db = *db

	return nil
}
<commit_msg>support sqlite3<commit_after>package utils

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"strings"
	"time"
	"database/sql"
)

// NullTime represents a time.Time that may be null. NullTime implements the
// sql.Scanner interface so it can be used as a scan destination, similar to
// sql.NullString.
type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (nt *NullTime) Scan(value interface{}) error {
	nt.Time, nt.Valid = value.(time.Time)
	return nil
}

// Value implements the driver Valuer interface.
func (nt NullTime) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	}
	return nt.Time, nil
}

// DbUtils can be used to prepare queries by changing the sql param notations
// as defined by each supported database
type DbUtils struct {
	db     *sql.DB
	dbType string
	prefix string
}

func (u *DbUtils) setDbType(dbType string) {
	if len(dbType) == 0 || (dbType != "postgres" && dbType != "oci8"  && dbType != "sqlite3" && dbType != "mysql") {
		panic("DbType must be one of: postgres, oci8, sqlite3 or mysql")
	}

	u.dbType = strings.ToLower(dbType)

	if u.dbType == "postgres" {
		u.prefix = "$"
	} else if u.dbType == "oci8" {
		u.prefix = ":"
	} else {
		u.prefix = ""
	}
}

//
// PQuery prepares query for run by changing params written as ? to $1, $2, etc
// for postgres and :1, :2, etc for oracle
func (u *DbUtils) PQuery(query string) string {
	q := query
	i := 1

	if len(u.prefix) > 0 {
		for {
			idx := strings.Index(q, "?")

			if idx < 0 {
				break
			}

			prm := fmt.Sprintf("%s%d", u.prefix, i)
			i++

			q = strings.Replace(q, "?", prm, 1)
		}
	}

	return q
}

func (u *DbUtils) Connect2Database(db **sql.DB, dbType, dbURL string) error {
	var err error
	u.setDbType(dbType)

	*db, err = sql.Open(dbType, dbURL)
	if err != nil {
		return errors.New("Can't connect to the database, go error " + fmt.Sprintf("%s", err))
	}

	err = (*db).Ping()
	if err != nil {
		return errors.New("Can't ping the database, go error " + fmt.Sprintf("%s", err))
	}

	u.db = *db

	return nil
}
<|endoftext|>


<commit_before>package brew

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

	"github.com/goreleaser/goreleaser/client"
	"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 TestNameWithDash(t *testing.T) {
	assert.Equal(t, formulaNameFor("some-binary"), "SomeBinary")
}

func TestNameWithUnderline(t *testing.T) {
	assert.Equal(t, formulaNameFor("some_binary"), "SomeBinary")
}

func TestSimpleName(t *testing.T) {
	assert.Equal(t, formulaNameFor("binary"), "Binary")
}

var defaultTemplateData = templateData{
	Binary:   "test",
	Desc:     "Some desc",
	Homepage: "https://google.com",
	Name:     "Test",
	Repo: config.Repo{
		Owner: "caarlos0",
		Name:  "test",
	},
	Tag:     "v0.1.3",
	Version: "0.1.3",
	File:    "test_Darwin_x86_64",
	SHA256:  "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68",
	Format:  "tar.gz",
}

func assertDefaultTemplateData(t *testing.T, formulae string) {
	assert := assert.New(t)
	assert.Contains(formulae, "class Test < Formula")
	assert.Contains(formulae, "homepage \"https://google.com\"")
	assert.Contains(formulae, "url \"https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz\"")
	assert.Contains(formulae, "sha256 \"1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68\"")
	assert.Contains(formulae, "version \"0.1.3\"")
}

func TestFullFormulae(t *testing.T) {
	assert := assert.New(t)
	data := defaultTemplateData
	data.Caveats = "Here are some caveats"
	data.Dependencies = []string{"gtk", "git"}
	data.Conflicts = []string{"conflicting_dep"}
	data.Plist = "it works"
	data.Install = []string{"custom install script", "another install script"}
	out, err := doBuildFormula(data)
	assert.NoError(err)
	formulae := out.String()
	assertDefaultTemplateData(t, formulae)
	assert.Contains(formulae, "def caveats")
	assert.Contains(formulae, "Here are some caveats")
	assert.Contains(formulae, "depends_on \"gtk\"")
	assert.Contains(formulae, "depends_on \"git\"")
	assert.Contains(formulae, "conflicts_with \"conflicting_dep\"")
	assert.Contains(formulae, "custom install script")
	assert.Contains(formulae, "another install script")
	assert.Contains(formulae, "def plist;")
}

func TestFormulaeSimple(t *testing.T) {
	assert := assert.New(t)
	out, err := doBuildFormula(defaultTemplateData)
	assert.NoError(err)
	formulae := out.String()
	assertDefaultTemplateData(t, formulae)
	assert.NotContains(formulae, "def caveats")
	assert.NotContains(formulae, "depends_on")
	assert.NotContains(formulae, "def plist;")
}

func TestRunPipe(t *testing.T) {
	assert := assert.New(t)
	folder, err := ioutil.TempDir("", "goreleasertest")
	assert.NoError(err)
	_, err = os.Create(filepath.Join(folder, "bin.tar.gz"))
	assert.NoError(err)
	var ctx = &context.Context{
		Config: config.Project{
			Dist: folder,
			Archive: config.Archive{
				Format: "tar.gz",
			},
			Brew: config.Homebrew{
				GitHub: config.Repo{
					Owner: "test",
					Name:  "test",
				},
			},
		},
		Archives: map[string]string{
			"darwinamd64": "bin",
		},
		Publish: true,
	}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.True(client.CreatedFile)
}

func TestRunPipeBrewNotSetup(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{
		Config: config.Project{
			Archive: config.Archive{
				Format: "tar.gz",
			},
			Brew: config.Homebrew{
				GitHub: config.Repo{
					Owner: "test",
					Name:  "test",
				},
			},
		},
		Publish: true,
	}
	client := &DummyClient{}
	assert.Equal(ErrNoDarwin64Build, doRun(ctx, client))
	assert.False(client.CreatedFile)
}

func TestRunPipeNoDarwinBuild(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.False(client.CreatedFile)
}

func TestRunPipeNoPublish(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{
		Publish: false,
	}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.False(client.CreatedFile)
}

type DummyClient struct {
	CreatedFile bool
}

func (client *DummyClient) GetInfo(ctx *context.Context) (info client.Info, err error) {
	return
}

func (client *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID int, err error) {
	return
}

func (client *DummyClient) CreateFile(ctx *context.Context, content bytes.Buffer, path string) (err error) {
	client.CreatedFile = true
	return
}

func (client *DummyClient) Upload(ctx *context.Context, releaseID int, name string, file *os.File) (err error) {
	return
}
<commit_msg>added more tests<commit_after>package brew

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

	"github.com/goreleaser/goreleaser/client"
	"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 TestNameWithDash(t *testing.T) {
	assert.Equal(t, formulaNameFor("some-binary"), "SomeBinary")
}

func TestNameWithUnderline(t *testing.T) {
	assert.Equal(t, formulaNameFor("some_binary"), "SomeBinary")
}

func TestSimpleName(t *testing.T) {
	assert.Equal(t, formulaNameFor("binary"), "Binary")
}

var defaultTemplateData = templateData{
	Binary:   "test",
	Desc:     "Some desc",
	Homepage: "https://google.com",
	Name:     "Test",
	Repo: config.Repo{
		Owner: "caarlos0",
		Name:  "test",
	},
	Tag:     "v0.1.3",
	Version: "0.1.3",
	File:    "test_Darwin_x86_64",
	SHA256:  "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68",
	Format:  "tar.gz",
}

func assertDefaultTemplateData(t *testing.T, formulae string) {
	assert := assert.New(t)
	assert.Contains(formulae, "class Test < Formula")
	assert.Contains(formulae, "homepage \"https://google.com\"")
	assert.Contains(formulae, "url \"https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz\"")
	assert.Contains(formulae, "sha256 \"1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68\"")
	assert.Contains(formulae, "version \"0.1.3\"")
}

func TestFullFormulae(t *testing.T) {
	assert := assert.New(t)
	data := defaultTemplateData
	data.Caveats = "Here are some caveats"
	data.Dependencies = []string{"gtk", "git"}
	data.Conflicts = []string{"conflicting_dep"}
	data.Plist = "it works"
	data.Install = []string{"custom install script", "another install script"}
	out, err := doBuildFormula(data)
	assert.NoError(err)
	formulae := out.String()
	assertDefaultTemplateData(t, formulae)
	assert.Contains(formulae, "def caveats")
	assert.Contains(formulae, "Here are some caveats")
	assert.Contains(formulae, "depends_on \"gtk\"")
	assert.Contains(formulae, "depends_on \"git\"")
	assert.Contains(formulae, "conflicts_with \"conflicting_dep\"")
	assert.Contains(formulae, "custom install script")
	assert.Contains(formulae, "another install script")
	assert.Contains(formulae, "def plist;")
}

func TestFormulaeSimple(t *testing.T) {
	assert := assert.New(t)
	out, err := doBuildFormula(defaultTemplateData)
	assert.NoError(err)
	formulae := out.String()
	assertDefaultTemplateData(t, formulae)
	assert.NotContains(formulae, "def caveats")
	assert.NotContains(formulae, "depends_on")
	assert.NotContains(formulae, "def plist;")
}

func TestRunPipe(t *testing.T) {
	assert := assert.New(t)
	folder, err := ioutil.TempDir("", "goreleasertest")
	assert.NoError(err)
	_, err = os.Create(filepath.Join(folder, "bin.tar.gz"))
	assert.NoError(err)
	var ctx = &context.Context{
		Config: config.Project{
			Dist: folder,
			Archive: config.Archive{
				Format: "tar.gz",
			},
			Brew: config.Homebrew{
				GitHub: config.Repo{
					Owner: "test",
					Name:  "test",
				},
			},
		},
		Archives: map[string]string{
			"darwinamd64": "bin",
		},
		Publish: true,
	}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.True(client.CreatedFile)
}

func TestRunPipeNoDarwin64Build(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{
		Config: config.Project{
			Archive: config.Archive{
				Format: "tar.gz",
			},
			Brew: config.Homebrew{
				GitHub: config.Repo{
					Owner: "test",
					Name:  "test",
				},
			},
		},
		Publish: true,
	}
	client := &DummyClient{}
	assert.Equal(ErrNoDarwin64Build, doRun(ctx, client))
	assert.False(client.CreatedFile)
}

func TestRunPipeBrewNotSetup(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{
		Config:  config.Project{},
		Publish: true,
	}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.False(client.CreatedFile)
}

func TestRunPipeNoDarwinBuild(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.False(client.CreatedFile)
}

func TestRunPipeNoPublish(t *testing.T) {
	assert := assert.New(t)
	var ctx = &context.Context{
		Publish: false,
	}
	client := &DummyClient{}
	assert.NoError(doRun(ctx, client))
	assert.False(client.CreatedFile)
}

type DummyClient struct {
	CreatedFile bool
}

func (client *DummyClient) GetInfo(ctx *context.Context) (info client.Info, err error) {
	return
}

func (client *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID int, err error) {
	return
}

func (client *DummyClient) CreateFile(ctx *context.Context, content bytes.Buffer, path string) (err error) {
	client.CreatedFile = true
	return
}

func (client *DummyClient) Upload(ctx *context.Context, releaseID int, name string, file *os.File) (err error) {
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 install installs the v1 monolithic api, making it available as an
// option to all of the API encoding/decoding machinery.
package install

import (
	"fmt"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
)

const importPrefix = "k8s.io/kubernetes/pkg/api"

var accessor = meta.NewAccessor()

// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}

func init() {
	registered.RegisterVersions(availableVersions)
	externalVersions := []unversioned.GroupVersion{}
	for _, v := range availableVersions {
		if registered.IsAllowedVersion(v) {
			externalVersions = append(externalVersions, v)
		}
	}
	if len(externalVersions) == 0 {
		glog.V(4).Infof("No version is registered for group %v", api.GroupName)
		return
	}

	if err := registered.EnableVersions(externalVersions...); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
	if err := enableVersions(externalVersions); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
}

// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []unversioned.GroupVersion) error {
	addVersionsToScheme(externalVersions...)
	preferredExternalVersion := externalVersions[0]

	groupMeta := apimachinery.GroupMeta{
		GroupVersion:  preferredExternalVersion,
		GroupVersions: externalVersions,
		RESTMapper:    newRESTMapper(externalVersions),
		SelfLinker:    runtime.SelfLinker(accessor),
		InterfacesFor: interfacesFor,
	}

	if err := registered.RegisterGroup(groupMeta); err != nil {
		return err
	}
	api.RegisterRESTMapper(groupMeta.RESTMapper)
	return nil
}

// userResources is a group of resources mostly used by a kubectl user
var userResources = []string{"rc", "svc", "pods", "pvc"}

func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
	// the list of kinds that are scoped at the root of the api hierarchy
	// if a kind is not enumerated here, it is assumed to have a namespace scope
	rootScoped := sets.NewString(
		"Node",
		"Namespace",
		"PersistentVolume",
		"ComponentStatus",
	)

	// these kinds should be excluded from the list of resources
	ignoredKinds := sets.NewString(
		"ListOptions",
		"DeleteOptions",
		"Status",
		"PodLogOptions",
		"PodExecOptions",
		"PodAttachOptions",
		"PodProxyOptions",
		"NodeProxyOptions",
		"ServiceProxyOptions",
		"ThirdPartyResource",
		"ThirdPartyResourceData",
		"ThirdPartyResourceList")

	mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
	// setup aliases for groups of resources
	mapper.AddResourceAlias("all", userResources...)

	return mapper
}

// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
	switch version {
	case v1.SchemeGroupVersion:
		return &meta.VersionInterfaces{
			ObjectConvertor:  api.Scheme,
			MetadataAccessor: accessor,
		}, nil
	default:
		g, _ := registered.Group(api.GroupName)
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
	}
}

func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
	// add the internal version to Scheme
	api.AddToScheme(api.Scheme)
	// add the enabled external versions to Scheme
	for _, v := range externalVersions {
		if !registered.IsEnabledVersion(v) {
			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
			continue
		}
		switch v {
		case v1.SchemeGroupVersion:
			v1.AddToScheme(api.Scheme)
		}
	}
}
<commit_msg>Avoid allocations and a reflect.Call in conversion<commit_after>/*
Copyright 2014 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 install installs the v1 monolithic api, making it available as an
// option to all of the API encoding/decoding machinery.
package install

import (
	"fmt"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/conversion"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
)

const importPrefix = "k8s.io/kubernetes/pkg/api"

var accessor = meta.NewAccessor()

// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}

func init() {
	registered.RegisterVersions(availableVersions)
	externalVersions := []unversioned.GroupVersion{}
	for _, v := range availableVersions {
		if registered.IsAllowedVersion(v) {
			externalVersions = append(externalVersions, v)
		}
	}
	if len(externalVersions) == 0 {
		glog.V(4).Infof("No version is registered for group %v", api.GroupName)
		return
	}

	if err := registered.EnableVersions(externalVersions...); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
	if err := enableVersions(externalVersions); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
}

// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []unversioned.GroupVersion) error {
	addVersionsToScheme(externalVersions...)
	preferredExternalVersion := externalVersions[0]

	groupMeta := apimachinery.GroupMeta{
		GroupVersion:  preferredExternalVersion,
		GroupVersions: externalVersions,
		RESTMapper:    newRESTMapper(externalVersions),
		SelfLinker:    runtime.SelfLinker(accessor),
		InterfacesFor: interfacesFor,
	}

	if err := registered.RegisterGroup(groupMeta); err != nil {
		return err
	}
	api.RegisterRESTMapper(groupMeta.RESTMapper)
	return nil
}

// userResources is a group of resources mostly used by a kubectl user
var userResources = []string{"rc", "svc", "pods", "pvc"}

func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
	// the list of kinds that are scoped at the root of the api hierarchy
	// if a kind is not enumerated here, it is assumed to have a namespace scope
	rootScoped := sets.NewString(
		"Node",
		"Namespace",
		"PersistentVolume",
		"ComponentStatus",
	)

	// these kinds should be excluded from the list of resources
	ignoredKinds := sets.NewString(
		"ListOptions",
		"DeleteOptions",
		"Status",
		"PodLogOptions",
		"PodExecOptions",
		"PodAttachOptions",
		"PodProxyOptions",
		"NodeProxyOptions",
		"ServiceProxyOptions",
		"ThirdPartyResource",
		"ThirdPartyResourceData",
		"ThirdPartyResourceList")

	mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
	// setup aliases for groups of resources
	mapper.AddResourceAlias("all", userResources...)

	return mapper
}

// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
	switch version {
	case v1.SchemeGroupVersion:
		return &meta.VersionInterfaces{
			ObjectConvertor:  api.Scheme,
			MetadataAccessor: accessor,
		}, nil
	default:
		g, _ := registered.Group(api.GroupName)
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
	}
}

func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
	// add the internal version to Scheme
	api.AddToScheme(api.Scheme)
	// add the enabled external versions to Scheme
	for _, v := range externalVersions {
		if !registered.IsEnabledVersion(v) {
			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
			continue
		}
		switch v {
		case v1.SchemeGroupVersion:
			v1.AddToScheme(api.Scheme)
		}
	}

	// This is a "fast-path" that avoids reflection for common types. It focuses on the objects that are
	// converted the most in the cluster.
	// TODO: generate one of these for every external API group - this is to prove the impact
	api.Scheme.AddGenericConversionFunc(func(objA, objB interface{}, s conversion.Scope) (bool, error) {
		switch a := objA.(type) {
		case *v1.Pod:
			switch b := objB.(type) {
			case *api.Pod:
				return true, v1.Convert_v1_Pod_To_api_Pod(a, b, s)
			}
		case *api.Pod:
			switch b := objB.(type) {
			case *v1.Pod:
				return true, v1.Convert_api_Pod_To_v1_Pod(a, b, s)
			}

		case *v1.Event:
			switch b := objB.(type) {
			case *api.Event:
				return true, v1.Convert_v1_Event_To_api_Event(a, b, s)
			}
		case *api.Event:
			switch b := objB.(type) {
			case *v1.Event:
				return true, v1.Convert_api_Event_To_v1_Event(a, b, s)
			}

		case *v1.ReplicationController:
			switch b := objB.(type) {
			case *api.ReplicationController:
				return true, v1.Convert_v1_ReplicationController_To_api_ReplicationController(a, b, s)
			}
		case *api.ReplicationController:
			switch b := objB.(type) {
			case *v1.ReplicationController:
				return true, v1.Convert_api_ReplicationController_To_v1_ReplicationController(a, b, s)
			}

		case *v1.Node:
			switch b := objB.(type) {
			case *api.Node:
				return true, v1.Convert_v1_Node_To_api_Node(a, b, s)
			}
		case *api.Node:
			switch b := objB.(type) {
			case *v1.Node:
				return true, v1.Convert_api_Node_To_v1_Node(a, b, s)
			}

		case *v1.Namespace:
			switch b := objB.(type) {
			case *api.Namespace:
				return true, v1.Convert_v1_Namespace_To_api_Namespace(a, b, s)
			}
		case *api.Namespace:
			switch b := objB.(type) {
			case *v1.Namespace:
				return true, v1.Convert_api_Namespace_To_v1_Namespace(a, b, s)
			}

		case *v1.Service:
			switch b := objB.(type) {
			case *api.Service:
				return true, v1.Convert_v1_Service_To_api_Service(a, b, s)
			}
		case *api.Service:
			switch b := objB.(type) {
			case *v1.Service:
				return true, v1.Convert_api_Service_To_v1_Service(a, b, s)
			}

		case *v1.Endpoints:
			switch b := objB.(type) {
			case *api.Endpoints:
				return true, v1.Convert_v1_Endpoints_To_api_Endpoints(a, b, s)
			}
		case *api.Endpoints:
			switch b := objB.(type) {
			case *v1.Endpoints:
				return true, v1.Convert_api_Endpoints_To_v1_Endpoints(a, b, s)
			}
		}
		return false, nil
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

// Package install installs the v1 monolithic api, making it available as an
// option to all of the API encoding/decoding machinery.
package install

import (
	"fmt"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/conversion"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch/versioned"
)

const importPrefix = "k8s.io/kubernetes/pkg/api"

var accessor = meta.NewAccessor()

// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}

func init() {
	registered.RegisterVersions(availableVersions)
	externalVersions := []unversioned.GroupVersion{}
	for _, v := range availableVersions {
		if registered.IsAllowedVersion(v) {
			externalVersions = append(externalVersions, v)
		}
	}
	if len(externalVersions) == 0 {
		glog.V(4).Infof("No version is registered for group %v", api.GroupName)
		return
	}

	if err := registered.EnableVersions(externalVersions...); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
	if err := enableVersions(externalVersions); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
}

// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []unversioned.GroupVersion) error {
	addVersionsToScheme(externalVersions...)
	preferredExternalVersion := externalVersions[0]

	groupMeta := apimachinery.GroupMeta{
		GroupVersion:  preferredExternalVersion,
		GroupVersions: externalVersions,
		RESTMapper:    newRESTMapper(externalVersions),
		SelfLinker:    runtime.SelfLinker(accessor),
		InterfacesFor: interfacesFor,
	}

	if err := registered.RegisterGroup(groupMeta); err != nil {
		return err
	}
	api.RegisterRESTMapper(groupMeta.RESTMapper)
	return nil
}

// userResources is a group of resources mostly used by a kubectl user
var userResources = []string{"rc", "svc", "pods", "pvc"}

func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
	// the list of kinds that are scoped at the root of the api hierarchy
	// if a kind is not enumerated here, it is assumed to have a namespace scope
	rootScoped := sets.NewString(
		"Node",
		"Namespace",
		"PersistentVolume",
		"ComponentStatus",
	)

	// these kinds should be excluded from the list of resources
	ignoredKinds := sets.NewString(
		"ListOptions",
		"DeleteOptions",
		"Status",
		"PodLogOptions",
		"PodExecOptions",
		"PodAttachOptions",
		"PodProxyOptions",
		"NodeProxyOptions",
		"ServiceProxyOptions",
		"ThirdPartyResource",
		"ThirdPartyResourceData",
		"ThirdPartyResourceList")

	mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
	// setup aliases for groups of resources
	mapper.AddResourceAlias("all", userResources...)

	return mapper
}

// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
	switch version {
	case v1.SchemeGroupVersion:
		return &meta.VersionInterfaces{
			ObjectConvertor:  api.Scheme,
			MetadataAccessor: accessor,
		}, nil
	default:
		g, _ := registered.Group(api.GroupName)
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
	}
}

func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
	// add the internal version to Scheme
	api.AddToScheme(api.Scheme)
	// add the enabled external versions to Scheme
	for _, v := range externalVersions {
		if !registered.IsEnabledVersion(v) {
			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
			continue
		}
		switch v {
		case v1.SchemeGroupVersion:
			v1.AddToScheme(api.Scheme)
		}
	}

	// This is a "fast-path" that avoids reflection for common types. It focuses on the objects that are
	// converted the most in the cluster.
	// TODO: generate one of these for every external API group - this is to prove the impact
	api.Scheme.AddGenericConversionFunc(func(objA, objB interface{}, s conversion.Scope) (bool, error) {
		switch a := objA.(type) {
		case *v1.Pod:
			switch b := objB.(type) {
			case *api.Pod:
				return true, v1.Convert_v1_Pod_To_api_Pod(a, b, s)
			}
		case *api.Pod:
			switch b := objB.(type) {
			case *v1.Pod:
				return true, v1.Convert_api_Pod_To_v1_Pod(a, b, s)
			}

		case *v1.Event:
			switch b := objB.(type) {
			case *api.Event:
				return true, v1.Convert_v1_Event_To_api_Event(a, b, s)
			}
		case *api.Event:
			switch b := objB.(type) {
			case *v1.Event:
				return true, v1.Convert_api_Event_To_v1_Event(a, b, s)
			}

		case *v1.ReplicationController:
			switch b := objB.(type) {
			case *api.ReplicationController:
				return true, v1.Convert_v1_ReplicationController_To_api_ReplicationController(a, b, s)
			}
		case *api.ReplicationController:
			switch b := objB.(type) {
			case *v1.ReplicationController:
				return true, v1.Convert_api_ReplicationController_To_v1_ReplicationController(a, b, s)
			}

		case *v1.Node:
			switch b := objB.(type) {
			case *api.Node:
				return true, v1.Convert_v1_Node_To_api_Node(a, b, s)
			}
		case *api.Node:
			switch b := objB.(type) {
			case *v1.Node:
				return true, v1.Convert_api_Node_To_v1_Node(a, b, s)
			}

		case *v1.Namespace:
			switch b := objB.(type) {
			case *api.Namespace:
				return true, v1.Convert_v1_Namespace_To_api_Namespace(a, b, s)
			}
		case *api.Namespace:
			switch b := objB.(type) {
			case *v1.Namespace:
				return true, v1.Convert_api_Namespace_To_v1_Namespace(a, b, s)
			}

		case *v1.Service:
			switch b := objB.(type) {
			case *api.Service:
				return true, v1.Convert_v1_Service_To_api_Service(a, b, s)
			}
		case *api.Service:
			switch b := objB.(type) {
			case *v1.Service:
				return true, v1.Convert_api_Service_To_v1_Service(a, b, s)
			}

		case *v1.Endpoints:
			switch b := objB.(type) {
			case *api.Endpoints:
				return true, v1.Convert_v1_Endpoints_To_api_Endpoints(a, b, s)
			}
		case *api.Endpoints:
			switch b := objB.(type) {
			case *v1.Endpoints:
				return true, v1.Convert_api_Endpoints_To_v1_Endpoints(a, b, s)
			}

		case *versioned.Event:
			switch b := objB.(type) {
			case *versioned.InternalEvent:
				return true, versioned.Convert_versioned_Event_to_versioned_InternalEvent(a, b, s)
			}
		case *versioned.InternalEvent:
			switch b := objB.(type) {
			case *versioned.Event:
				return true, versioned.Convert_versioned_InternalEvent_to_versioned_Event(a, b, s)
			}
		}
		return false, nil
	})
}
<commit_msg>Extend all to more resources<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

// Package install installs the v1 monolithic api, making it available as an
// option to all of the API encoding/decoding machinery.
package install

import (
	"fmt"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/conversion"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch/versioned"
)

const importPrefix = "k8s.io/kubernetes/pkg/api"

var accessor = meta.NewAccessor()

// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}

func init() {
	registered.RegisterVersions(availableVersions)
	externalVersions := []unversioned.GroupVersion{}
	for _, v := range availableVersions {
		if registered.IsAllowedVersion(v) {
			externalVersions = append(externalVersions, v)
		}
	}
	if len(externalVersions) == 0 {
		glog.V(4).Infof("No version is registered for group %v", api.GroupName)
		return
	}

	if err := registered.EnableVersions(externalVersions...); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
	if err := enableVersions(externalVersions); err != nil {
		glog.V(4).Infof("%v", err)
		return
	}
}

// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []unversioned.GroupVersion) error {
	addVersionsToScheme(externalVersions...)
	preferredExternalVersion := externalVersions[0]

	groupMeta := apimachinery.GroupMeta{
		GroupVersion:  preferredExternalVersion,
		GroupVersions: externalVersions,
		RESTMapper:    newRESTMapper(externalVersions),
		SelfLinker:    runtime.SelfLinker(accessor),
		InterfacesFor: interfacesFor,
	}

	if err := registered.RegisterGroup(groupMeta); err != nil {
		return err
	}
	api.RegisterRESTMapper(groupMeta.RESTMapper)
	return nil
}

func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
	// the list of kinds that are scoped at the root of the api hierarchy
	// if a kind is not enumerated here, it is assumed to have a namespace scope
	rootScoped := sets.NewString(
		"Node",
		"Namespace",
		"PersistentVolume",
		"ComponentStatus",
	)

	// these kinds should be excluded from the list of resources
	ignoredKinds := sets.NewString(
		"ListOptions",
		"DeleteOptions",
		"Status",
		"PodLogOptions",
		"PodExecOptions",
		"PodAttachOptions",
		"PodProxyOptions",
		"NodeProxyOptions",
		"ServiceProxyOptions",
		"ThirdPartyResource",
		"ThirdPartyResourceData",
		"ThirdPartyResourceList")

	return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
}

// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
	switch version {
	case v1.SchemeGroupVersion:
		return &meta.VersionInterfaces{
			ObjectConvertor:  api.Scheme,
			MetadataAccessor: accessor,
		}, nil
	default:
		g, _ := registered.Group(api.GroupName)
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
	}
}

func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
	// add the internal version to Scheme
	api.AddToScheme(api.Scheme)
	// add the enabled external versions to Scheme
	for _, v := range externalVersions {
		if !registered.IsEnabledVersion(v) {
			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
			continue
		}
		switch v {
		case v1.SchemeGroupVersion:
			v1.AddToScheme(api.Scheme)
		}
	}

	// This is a "fast-path" that avoids reflection for common types. It focuses on the objects that are
	// converted the most in the cluster.
	// TODO: generate one of these for every external API group - this is to prove the impact
	api.Scheme.AddGenericConversionFunc(func(objA, objB interface{}, s conversion.Scope) (bool, error) {
		switch a := objA.(type) {
		case *v1.Pod:
			switch b := objB.(type) {
			case *api.Pod:
				return true, v1.Convert_v1_Pod_To_api_Pod(a, b, s)
			}
		case *api.Pod:
			switch b := objB.(type) {
			case *v1.Pod:
				return true, v1.Convert_api_Pod_To_v1_Pod(a, b, s)
			}

		case *v1.Event:
			switch b := objB.(type) {
			case *api.Event:
				return true, v1.Convert_v1_Event_To_api_Event(a, b, s)
			}
		case *api.Event:
			switch b := objB.(type) {
			case *v1.Event:
				return true, v1.Convert_api_Event_To_v1_Event(a, b, s)
			}

		case *v1.ReplicationController:
			switch b := objB.(type) {
			case *api.ReplicationController:
				return true, v1.Convert_v1_ReplicationController_To_api_ReplicationController(a, b, s)
			}
		case *api.ReplicationController:
			switch b := objB.(type) {
			case *v1.ReplicationController:
				return true, v1.Convert_api_ReplicationController_To_v1_ReplicationController(a, b, s)
			}

		case *v1.Node:
			switch b := objB.(type) {
			case *api.Node:
				return true, v1.Convert_v1_Node_To_api_Node(a, b, s)
			}
		case *api.Node:
			switch b := objB.(type) {
			case *v1.Node:
				return true, v1.Convert_api_Node_To_v1_Node(a, b, s)
			}

		case *v1.Namespace:
			switch b := objB.(type) {
			case *api.Namespace:
				return true, v1.Convert_v1_Namespace_To_api_Namespace(a, b, s)
			}
		case *api.Namespace:
			switch b := objB.(type) {
			case *v1.Namespace:
				return true, v1.Convert_api_Namespace_To_v1_Namespace(a, b, s)
			}

		case *v1.Service:
			switch b := objB.(type) {
			case *api.Service:
				return true, v1.Convert_v1_Service_To_api_Service(a, b, s)
			}
		case *api.Service:
			switch b := objB.(type) {
			case *v1.Service:
				return true, v1.Convert_api_Service_To_v1_Service(a, b, s)
			}

		case *v1.Endpoints:
			switch b := objB.(type) {
			case *api.Endpoints:
				return true, v1.Convert_v1_Endpoints_To_api_Endpoints(a, b, s)
			}
		case *api.Endpoints:
			switch b := objB.(type) {
			case *v1.Endpoints:
				return true, v1.Convert_api_Endpoints_To_v1_Endpoints(a, b, s)
			}

		case *versioned.Event:
			switch b := objB.(type) {
			case *versioned.InternalEvent:
				return true, versioned.Convert_versioned_Event_to_versioned_InternalEvent(a, b, s)
			}
		case *versioned.InternalEvent:
			switch b := objB.(type) {
			case *versioned.Event:
				return true, versioned.Convert_versioned_InternalEvent_to_versioned_Event(a, b, s)
			}
		}
		return false, nil
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package meta

import (
	"k8s.io/kubernetes/pkg/api/meta/metatypes"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/types"
)

// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
type VersionInterfaces struct {
	runtime.ObjectConvertor
	MetadataAccessor
}

type ObjectMetaAccessor interface {
	GetObjectMeta() Object
}

// Object lets you work with object metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value.
type Object interface {
	GetNamespace() string
	SetNamespace(namespace string)
	GetName() string
	SetName(name string)
	GetGenerateName() string
	SetGenerateName(name string)
	GetUID() types.UID
	SetUID(uid types.UID)
	GetResourceVersion() string
	SetResourceVersion(version string)
	GetSelfLink() string
	SetSelfLink(selfLink string)
	GetCreationTimestamp() unversioned.Time
	SetCreationTimestamp(timestamp unversioned.Time)
	GetDeletionTimestamp() *unversioned.Time
	SetDeletionTimestamp(timestamp *unversioned.Time)
	GetLabels() map[string]string
	SetLabels(labels map[string]string)
	GetAnnotations() map[string]string
	SetAnnotations(annotations map[string]string)
	GetFinalizers() []string
	SetFinalizers(finalizers []string)
	GetOwnerReferences() []metatypes.OwnerReference
	SetOwnerReferences([]metatypes.OwnerReference)
	GetClusterName() string
	SetClusterName(clusterName string)
}

var _ Object = &runtime.Unstructured{}

type ListMetaAccessor interface {
	GetListMeta() List
}

// List lets you work with list metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field will be a no-op and return a default value.
type List unversioned.List

// Type exposes the type and APIVersion of versioned or internal API objects.
type Type unversioned.Type

// MetadataAccessor lets you work with object and list metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value.
//
// MetadataAccessor exposes Interface in a way that can be used with multiple objects.
type MetadataAccessor interface {
	APIVersion(obj runtime.Object) (string, error)
	SetAPIVersion(obj runtime.Object, version string) error

	Kind(obj runtime.Object) (string, error)
	SetKind(obj runtime.Object, kind string) error

	Namespace(obj runtime.Object) (string, error)
	SetNamespace(obj runtime.Object, namespace string) error

	Name(obj runtime.Object) (string, error)
	SetName(obj runtime.Object, name string) error

	GenerateName(obj runtime.Object) (string, error)
	SetGenerateName(obj runtime.Object, name string) error

	UID(obj runtime.Object) (types.UID, error)
	SetUID(obj runtime.Object, uid types.UID) error

	SelfLink(obj runtime.Object) (string, error)
	SetSelfLink(obj runtime.Object, selfLink string) error

	Labels(obj runtime.Object) (map[string]string, error)
	SetLabels(obj runtime.Object, labels map[string]string) error

	Annotations(obj runtime.Object) (map[string]string, error)
	SetAnnotations(obj runtime.Object, annotations map[string]string) error

	runtime.ResourceVersioner
}

type RESTScopeName string

const (
	RESTScopeNameNamespace RESTScopeName = "namespace"
	RESTScopeNameRoot      RESTScopeName = "root"
)

// RESTScope contains the information needed to deal with REST resources that are in a resource hierarchy
type RESTScope interface {
	// Name of the scope
	Name() RESTScopeName
	// ParamName is the optional name of the parameter that should be inserted in the resource url
	// If empty, no param will be inserted
	ParamName() string
	// ArgumentName is the optional name that should be used for the variable holding the value.
	ArgumentName() string
	// ParamDescription is the optional description to use to document the parameter in api documentation
	ParamDescription() string
}

// RESTMapping contains the information needed to deal with objects of a specific
// resource and kind in a RESTful manner.
type RESTMapping struct {
	// Resource is a string representing the name of this resource as a REST client would see it
	Resource string

	GroupVersionKind unversioned.GroupVersionKind

	// Scope contains the information needed to deal with REST Resources that are in a resource hierarchy
	Scope RESTScope

	runtime.ObjectConvertor
	MetadataAccessor
}

// RESTMapper allows clients to map resources to kind, and map kind and version
// to interfaces for manipulating those objects. It is primarily intended for
// consumers of Kubernetes compatible REST APIs as defined in docs/devel/api-conventions.md.
//
// The Kubernetes API provides versioned resources and object kinds which are scoped
// to API groups. In other words, kinds and resources should not be assumed to be
// unique across groups.
//
// TODO(caesarxuchao): Add proper multi-group support so that kinds & resources are
// scoped to groups. See http://issues.k8s.io/12413 and http://issues.k8s.io/10009.
type RESTMapper interface {
	// KindFor takes a partial resource and returns the single match.  Returns an error if there are multiple matches
	KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error)

	// KindsFor takes a partial resource and returns the list of potential kinds in priority order
	KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error)

	// ResourceFor takes a partial resource and returns the single match.  Returns an error if there are multiple matches
	ResourceFor(input unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error)

	// ResourcesFor takes a partial resource and returns the list of potential resource in priority order
	ResourcesFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error)

	// RESTMapping identifies a preferred resource mapping for the provided group kind.
	RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error)
	// RESTMappings returns all resource mappings for the provided group kind.
	RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error)

	AliasesForResource(resource string) ([]string, bool)
	ResourceSingularizer(resource string) (singular string, err error)
}
<commit_msg>Allow garbage collection to work against different API prefixes<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package meta

import (
	"k8s.io/kubernetes/pkg/api/meta/metatypes"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/types"
)

// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
type VersionInterfaces struct {
	runtime.ObjectConvertor
	MetadataAccessor
}

type ObjectMetaAccessor interface {
	GetObjectMeta() Object
}

// Object lets you work with object metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value.
type Object interface {
	GetNamespace() string
	SetNamespace(namespace string)
	GetName() string
	SetName(name string)
	GetGenerateName() string
	SetGenerateName(name string)
	GetUID() types.UID
	SetUID(uid types.UID)
	GetResourceVersion() string
	SetResourceVersion(version string)
	GetSelfLink() string
	SetSelfLink(selfLink string)
	GetCreationTimestamp() unversioned.Time
	SetCreationTimestamp(timestamp unversioned.Time)
	GetDeletionTimestamp() *unversioned.Time
	SetDeletionTimestamp(timestamp *unversioned.Time)
	GetLabels() map[string]string
	SetLabels(labels map[string]string)
	GetAnnotations() map[string]string
	SetAnnotations(annotations map[string]string)
	GetFinalizers() []string
	SetFinalizers(finalizers []string)
	GetOwnerReferences() []metatypes.OwnerReference
	SetOwnerReferences([]metatypes.OwnerReference)
	GetClusterName() string
	SetClusterName(clusterName string)
}

var _ Object = &runtime.Unstructured{}

type ListMetaAccessor interface {
	GetListMeta() List
}

// List lets you work with list metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field will be a no-op and return a default value.
type List unversioned.List

// Type exposes the type and APIVersion of versioned or internal API objects.
type Type unversioned.Type

// MetadataAccessor lets you work with object and list metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value.
//
// MetadataAccessor exposes Interface in a way that can be used with multiple objects.
type MetadataAccessor interface {
	APIVersion(obj runtime.Object) (string, error)
	SetAPIVersion(obj runtime.Object, version string) error

	Kind(obj runtime.Object) (string, error)
	SetKind(obj runtime.Object, kind string) error

	Namespace(obj runtime.Object) (string, error)
	SetNamespace(obj runtime.Object, namespace string) error

	Name(obj runtime.Object) (string, error)
	SetName(obj runtime.Object, name string) error

	GenerateName(obj runtime.Object) (string, error)
	SetGenerateName(obj runtime.Object, name string) error

	UID(obj runtime.Object) (types.UID, error)
	SetUID(obj runtime.Object, uid types.UID) error

	SelfLink(obj runtime.Object) (string, error)
	SetSelfLink(obj runtime.Object, selfLink string) error

	Labels(obj runtime.Object) (map[string]string, error)
	SetLabels(obj runtime.Object, labels map[string]string) error

	Annotations(obj runtime.Object) (map[string]string, error)
	SetAnnotations(obj runtime.Object, annotations map[string]string) error

	runtime.ResourceVersioner
}

type RESTScopeName string

const (
	RESTScopeNameNamespace RESTScopeName = "namespace"
	RESTScopeNameRoot      RESTScopeName = "root"
)

// RESTScope contains the information needed to deal with REST resources that are in a resource hierarchy
type RESTScope interface {
	// Name of the scope
	Name() RESTScopeName
	// ParamName is the optional name of the parameter that should be inserted in the resource url
	// If empty, no param will be inserted
	ParamName() string
	// ArgumentName is the optional name that should be used for the variable holding the value.
	ArgumentName() string
	// ParamDescription is the optional description to use to document the parameter in api documentation
	ParamDescription() string
}

// RESTMapping contains the information needed to deal with objects of a specific
// resource and kind in a RESTful manner.
type RESTMapping struct {
	// Resource is a string representing the name of this resource as a REST client would see it
	Resource string

	GroupVersionKind unversioned.GroupVersionKind

	// Scope contains the information needed to deal with REST Resources that are in a resource hierarchy
	Scope RESTScope

	runtime.ObjectConvertor
	MetadataAccessor
}

// RESTMapper allows clients to map resources to kind, and map kind and version
// to interfaces for manipulating those objects. It is primarily intended for
// consumers of Kubernetes compatible REST APIs as defined in docs/devel/api-conventions.md.
//
// The Kubernetes API provides versioned resources and object kinds which are scoped
// to API groups. In other words, kinds and resources should not be assumed to be
// unique across groups.
//
// TODO: split into sub-interfaces
type RESTMapper interface {
	// KindFor takes a partial resource and returns the single match.  Returns an error if there are multiple matches
	KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error)

	// KindsFor takes a partial resource and returns the list of potential kinds in priority order
	KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error)

	// ResourceFor takes a partial resource and returns the single match.  Returns an error if there are multiple matches
	ResourceFor(input unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error)

	// ResourcesFor takes a partial resource and returns the list of potential resource in priority order
	ResourcesFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error)

	// RESTMapping identifies a preferred resource mapping for the provided group kind.
	RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error)
	// RESTMappings returns all resource mappings for the provided group kind.
	RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error)

	AliasesForResource(resource string) ([]string, bool)
	ResourceSingularizer(resource string) (singular string, err error)
}
<|endoftext|>


<commit_before>package apl

import (
	"fmt"
	"github.com/dghubble/sling"
	"net/http"
)

// ReleaseService is the service object for release operations
type ReleaseService struct {
	sling    *sling.Sling
	endpoint string
}

// NewReleasesService return a new ReleaseService
func NewReleasesService(sling *sling.Sling) *ReleaseService {
	return &ReleaseService{
		sling:    sling,
		endpoint: "releases",
	}
}

// Release represents a release row
type Release struct {
	ID             string      `json:"id,omitempty"`
	Version        int         `json:"version"`
	StackID        string      `json:"stack_id"`
	StackVersionID string      `json:"stack_version_id"`
	ProjectID      string      `json:"project_id"`
	LocImageID     string      `json:"loc_image_id,omitempty"`
	BuildStatus    string      `json:"build_status,omitempty"`
	Components     interface{} `json:"components"`
	MetaData       interface{} `json:"meta_data,omitempty"`

	LastModified  string `json:"last_modified"`
	CreatedTime   string `json:"created_time"`
	CreatedByUser `json:"created_by_user"`
}

// ReleaseCreateInput is used for the create of releases
type ReleaseCreateInput struct {
	ID             string      `json:"id,omitempty"`
	Version        int         `json:"version,omitempty"`
	StackID        string      `json:"stack_id"`
	StackVersionID string      `json:"stack_version_id"`
	ProjectID      string      `json:"project_id,omitempty"`
	LocImageID     string      `json:"loc_image_id,omitempty"`
	BuildStatus    string      `json:"build_status,omitempty"`
	Components     interface{} `json:"components"`
	MetaData       interface{} `json:"meta_data,omitempty"`
}

type ReleaseOverrideArtifactBase struct {
	StackArtifactID string `json:"stack_artifact_id,omitempty"`
}

type ReleaseOverrideArtifact struct {
	Builder *ReleaseOverrideArtifactBase `json:"builder,omitempty"`
	Code    *ReleaseOverrideArtifactBase `json:"code,omitempty"`
	Image   *ReleaseOverrideArtifactBase `json:"image,omitempty"`
} // `json:"artifacts"`

type ReleaseOverrideRelease struct {
	Artifacts ReleaseOverrideArtifact `json:"artifacts,omitempty"`
} // `json:"release"`

type ReleaseOverrideService struct {
	Name    string                 `json:"name"`
	Release ReleaseOverrideRelease `json:"release"`
} // `json:"services"`

type ReleaseOverrideComponent struct {
	Name             string                   `json:"name"`
	StackComponentID string                   `json:"stack_component_id"`
	Services         []ReleaseOverrideService `json:"services"`
} // `json:"components"`

// ReleaseParams filter parameters used in list operations
type ReleaseParams struct {
	Name           string `url:"name,omitempty"`
	Version        string `url:"version,omitempty"`
	StackID        string `url:"stack_id,omitempty"`
	StackVersionID string `url:"stack_version_id,omitempty"`
	ProjectID      string `url:"project_id,omitempty"`
	LocImageID     string `url:"loc_image_id,omitempty"`
	BuildStatus    string `url:"build_status,omitempty"`
}

// List gets a list of releases with optional filter params
func (c *ReleaseService) List(params *ReleaseParams) ([]Release, *http.Response, error) {
	output := &struct {
		Data []Release `json:"data"`
	}{}
	resp, err := doList(c.sling, c.endpoint, params, output)
	return output.Data, resp, err
}

// Get get a release for the id specified
func (c *ReleaseService) Get(id string) (Release, *http.Response, error) {
	output := &struct {
		Data Release `json:"data"`
	}{}
	path := fmt.Sprintf("%s/%s", c.endpoint, id)
	resp, err := doGet(c.sling, path, output)
	return output.Data, resp, err
}

// Create will create a release
func (c *ReleaseService) Create(input *ReleaseCreateInput) (CreateResult, *http.Response, error) {

	return doCreate(c.sling, c.endpoint, input)
}

// Delete will delete the release for the id specified
func (c *ReleaseService) Delete(id string) (ModifyResult, *http.Response, error) {
	path := fmt.Sprintf("%s/%s", c.endpoint, id)
	return doDelete(c.sling, path)
}
<commit_msg>Fixes for the release service<commit_after>package apl

import (
	"fmt"
	"github.com/dghubble/sling"
	"net/http"
)

// ReleaseService is the service object for release operations
type ReleaseService struct {
	sling    *sling.Sling
	endpoint string
}

// NewReleasesService return a new ReleaseService
func NewReleasesService(sling *sling.Sling) *ReleaseService {
	return &ReleaseService{
		sling:    sling,
		endpoint: "releases",
	}
}

// Release represents a release row
type Release struct {
	ID             string      `json:"id,omitempty"`
	Version        int         `json:"version"`
	StackID        string      `json:"stack_id"`
	StackVersionID string      `json:"stack_version_id"`
	ProjectID      string      `json:"project_id"`
	LocImageID     string      `json:"loc_image_id,omitempty"`
	BuildStatus    string      `json:"build_status,omitempty"`
	Components     interface{} `json:"components"`
	MetaData       interface{} `json:"meta_data,omitempty"`

	LastModified  string `json:"last_modified"`
	CreatedTime   string `json:"created_time"`
	CreatedByUser interface{} `json:"created_by_user"`
}

// ReleaseCreateInput is used for the create of releases
type ReleaseCreateInput struct {
	ID             string      `json:"id,omitempty"`
	Version        int         `json:"version,omitempty"`
	StackID        string      `json:"stack_id"`
	StackVersionID string      `json:"stack_version_id"`
	ProjectID      string      `json:"project_id,omitempty"`
	LocImageID     string      `json:"loc_image_id,omitempty"`
	BuildStatus    string      `json:"build_status,omitempty"`
	Components     interface{} `json:"components"`
	MetaData       interface{} `json:"meta_data,omitempty"`
}

type ReleaseOverrideArtifactBase struct {
	StackArtifactID string `json:"stack_artifact_id,omitempty"`
}

type ReleaseOverrideArtifact struct {
	Builder *ReleaseOverrideArtifactBase `json:"builder,omitempty"`
	Code    *ReleaseOverrideArtifactBase `json:"code,omitempty"`
	Image   *ReleaseOverrideArtifactBase `json:"image,omitempty"`
	Data   *ReleaseOverrideArtifactBase `json:"data,omitempty"`
	Config   *ReleaseOverrideArtifactBase `json:"config,omitempty"`
} // `json:"artifacts"`

type ReleaseOverrideRelease struct {
	Artifacts ReleaseOverrideArtifact `json:"artifacts,omitempty"`
} // `json:"release"`

type ReleaseOverrideService struct {
	Name    string                 `json:"name"`
	Release ReleaseOverrideRelease `json:"release"`
} // `json:"services"`

type ReleaseOverrideComponent struct {
	Name             string                   `json:"name"`
	StackComponentID string                   `json:"stack_component_id"`
	Services         []ReleaseOverrideService `json:"services"`
} // `json:"components"`

// ReleaseParams filter parameters used in list operations
type ReleaseParams struct {
	Name           string `url:"name,omitempty"`
	Version        string `url:"version,omitempty"`
	StackID        string `url:"stack_id,omitempty"`
	StackVersionID string `url:"stack_version_id,omitempty"`
	ProjectID      string `url:"project_id,omitempty"`
	LocImageID     string `url:"loc_image_id,omitempty"`
	BuildStatus    string `url:"build_status,omitempty"`
}

// List gets a list of releases with optional filter params
func (c *ReleaseService) List(params *ReleaseParams) ([]Release, *http.Response, error) {
	output := &struct {
		Data []Release `json:"data"`
	}{}
	resp, err := doList(c.sling, c.endpoint, params, output)
	return output.Data, resp, err
}

// Get get a release for the id specified
func (c *ReleaseService) Get(id string) (Release, *http.Response, error) {
	output := &struct {
		Data Release `json:"data"`
	}{}
	path := fmt.Sprintf("%s/%s", c.endpoint, id)
	resp, err := doGet(c.sling, path, output)
	return output.Data, resp, err
}

// Create will create a release
func (c *ReleaseService) Create(input *ReleaseCreateInput) (CreateResult, *http.Response, error) {

	return doCreate(c.sling, c.endpoint, input)
}

// Delete will delete the release for the id specified
func (c *ReleaseService) Delete(id string) (ModifyResult, *http.Response, error) {
	path := fmt.Sprintf("%s/%s", c.endpoint, id)
	return doDelete(c.sling, path)
}
<|endoftext|>


<commit_before>// Copyright 2017 uSwitch
//
// 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 metadata

import (
	"context"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/rcrowley/go-metrics"
	"github.com/rcrowley/go-metrics/exp"
	log "github.com/sirupsen/logrus"
	"github.com/uswitch/kiam/pkg/aws/sts"
	"github.com/uswitch/kiam/pkg/k8s"
	"github.com/uswitch/kiam/pkg/server"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

type Server struct {
	cfg    *ServerConfig
	server *http.Server
}

type ServerConfig struct {
	ListenPort       int
	MetadataEndpoint string
	AllowIPQuery     bool
}

func NewConfig(port int) *ServerConfig {
	return &ServerConfig{
		MetadataEndpoint: "http://169.254.169.254",
		ListenPort:       port,
		AllowIPQuery:     false,
	}
}

func NewWebServer(config *ServerConfig, finder k8s.RoleFinder, credentials sts.CredentialsProvider, policy server.AssumeRolePolicy) (*Server, error) {
	http, err := buildHTTPServer(config, finder, credentials, policy)
	if err != nil {
		return nil, err
	}
	return &Server{cfg: config, server: http}, nil
}

func buildHTTPServer(config *ServerConfig, finder k8s.RoleFinder, credentials sts.CredentialsProvider, policy server.AssumeRolePolicy) (*http.Server, error) {
	router := mux.NewRouter()
	router.Handle("/metrics", exp.ExpHandler(metrics.DefaultRegistry))
	router.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, "pong") }))

	h := &healthHandler{config.MetadataEndpoint}
	router.Handle("/health", adapt(withMeter("health", h)))

	clientIP := buildClientIP(config)

	r := &roleHandler{
		roleFinder: finder,
		clientIP:   clientIP,
	}
	router.Handle("/{version}/meta-data/iam/security-credentials/", adapt(withMeter("roleName", r)))

	c := &credentialsHandler{
		roleFinder:          finder,
		credentialsProvider: credentials,
		clientIP:            clientIP,
		policy:              policy,
	}
	router.Handle("/{version}/meta-data/iam/security-credentials/{role:.*}", adapt(withMeter("credentials", c)))

	metadataURL, err := url.Parse(config.MetadataEndpoint)
	if err != nil {
		return nil, err
	}
	router.Handle("/{path:.*}", httputil.NewSingleHostReverseProxy(metadataURL))

	listen := fmt.Sprintf(":%d", config.ListenPort)
	return &http.Server{Addr: listen, Handler: loggingHandler(router)}, nil
}

func buildClientIP(config *ServerConfig) clientIPFunc {
	remote := func(req *http.Request) (string, error) {
		return ParseClientIP(req.RemoteAddr)
	}

	if config.AllowIPQuery {
		return func(req *http.Request) (string, error) {
			ip := req.Form.Get("ip")
			if ip != "" {
				return ip, nil
			}
			return remote(req)
		}
	}

	return remote
}

func (s *Server) Serve() error {
	log.Infof("listening :%d", s.cfg.ListenPort)
	return s.server.ListenAndServe()
}

func (s *Server) Stop(ctx context.Context) {
	log.Infoln("starting server shutdown")
	c, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	s.server.Shutdown(c)
	log.Infoln("gracefully shutdown server")
}

func ParseClientIP(addr string) (string, error) {
	parts := strings.Split(addr, ":")
	if len(parts) < 2 {
		return "", fmt.Errorf("incorrect format, expected ip:port, was: %s", addr)
	}

	return strings.Join(parts[0:len(parts)-1], ":"), nil
}
<commit_msg>Allow trailing / on security-credentials endpoint<commit_after>// Copyright 2017 uSwitch
//
// 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 metadata

import (
	"context"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/rcrowley/go-metrics"
	"github.com/rcrowley/go-metrics/exp"
	log "github.com/sirupsen/logrus"
	"github.com/uswitch/kiam/pkg/aws/sts"
	"github.com/uswitch/kiam/pkg/k8s"
	"github.com/uswitch/kiam/pkg/server"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

type Server struct {
	cfg    *ServerConfig
	server *http.Server
}

type ServerConfig struct {
	ListenPort       int
	MetadataEndpoint string
	AllowIPQuery     bool
}

func NewConfig(port int) *ServerConfig {
	return &ServerConfig{
		MetadataEndpoint: "http://169.254.169.254",
		ListenPort:       port,
		AllowIPQuery:     false,
	}
}

func NewWebServer(config *ServerConfig, finder k8s.RoleFinder, credentials sts.CredentialsProvider, policy server.AssumeRolePolicy) (*Server, error) {
	http, err := buildHTTPServer(config, finder, credentials, policy)
	if err != nil {
		return nil, err
	}
	return &Server{cfg: config, server: http}, nil
}

func buildHTTPServer(config *ServerConfig, finder k8s.RoleFinder, credentials sts.CredentialsProvider, policy server.AssumeRolePolicy) (*http.Server, error) {
	router := mux.NewRouter()
	router.Handle("/metrics", exp.ExpHandler(metrics.DefaultRegistry))
	router.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, "pong") }))

	h := &healthHandler{config.MetadataEndpoint}
	router.Handle("/health", adapt(withMeter("health", h)))

	clientIP := buildClientIP(config)

	r := &roleHandler{
		roleFinder: finder,
		clientIP:   clientIP,
	}

	securityCredsHandler := adapt(withMeter("roleName", r))
	router.Handle("/{version}/meta-data/iam/security-credentials", securityCredsHandler)
	router.Handle("/{version}/meta-data/iam/security-credentials/", securityCredsHandler)

	c := &credentialsHandler{
		roleFinder:          finder,
		credentialsProvider: credentials,
		clientIP:            clientIP,
		policy:              policy,
	}
	router.Handle("/{version}/meta-data/iam/security-credentials/{role:.*}", adapt(withMeter("credentials", c)))

	metadataURL, err := url.Parse(config.MetadataEndpoint)
	if err != nil {
		return nil, err
	}
	router.Handle("/{path:.*}", httputil.NewSingleHostReverseProxy(metadataURL))

	listen := fmt.Sprintf(":%d", config.ListenPort)
	return &http.Server{Addr: listen, Handler: loggingHandler(router)}, nil
}

func buildClientIP(config *ServerConfig) clientIPFunc {
	remote := func(req *http.Request) (string, error) {
		return ParseClientIP(req.RemoteAddr)
	}

	if config.AllowIPQuery {
		return func(req *http.Request) (string, error) {
			ip := req.Form.Get("ip")
			if ip != "" {
				return ip, nil
			}
			return remote(req)
		}
	}

	return remote
}

func (s *Server) Serve() error {
	log.Infof("listening :%d", s.cfg.ListenPort)
	return s.server.ListenAndServe()
}

func (s *Server) Stop(ctx context.Context) {
	log.Infoln("starting server shutdown")
	c, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	s.server.Shutdown(c)
	log.Infoln("gracefully shutdown server")
}

func ParseClientIP(addr string) (string, error) {
	parts := strings.Split(addr, ":")
	if len(parts) < 2 {
		return "", fmt.Errorf("incorrect format, expected ip:port, was: %s", addr)
	}

	return strings.Join(parts[0:len(parts)-1], ":"), nil
}
<|endoftext|>


<commit_before>/*
 * Minimalist Object Storage, (C) 2015 Minio, 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 client

import (
	"errors"
	"io"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/minio-io/donut"
)

// donutDriver - creates a new single disk drivers driver using donut
type donutDriver struct {
	donut donut.Donut
}

// Object split blockSize defaulted at 10MB
const (
	blockSize = 10 * 1024 * 1024
)

// IsValidBucketName reports whether bucket is a valid bucket name, per Amazon's naming restrictions.
// See http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
func IsValidBucketName(bucket string) bool {
	if len(bucket) < 3 || len(bucket) > 63 {
		return false
	}
	if bucket[0] == '.' || bucket[len(bucket)-1] == '.' {
		return false
	}
	if match, _ := regexp.MatchString("\\.\\.", bucket); match == true {
		return false
	}
	// We don't support buckets with '.' in them
	match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\-]+[a-zA-Z0-9]$", bucket)
	return match
}

// GetNewClient returns an initialized donut driver
func GetNewClient(donutName string, nodeDiskMap map[string][]string) (Client, error) {
	var err error

	d := new(donutDriver)
	d.donut, err = donut.NewDonut(donutName, nodeDiskMap)
	if err != nil {
		return nil, err
	}
	return d, nil
}

// byBucketName is a type for sorting bucket metadata by bucket name
type byBucketName []*Bucket

func (b byBucketName) Len() int           { return len(b) }
func (b byBucketName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byBucketName) Less(i, j int) bool { return b[i].Name < b[j].Name }

// ListBuckets returns a list of buckets
func (d *donutDriver) ListBuckets() (results []*Bucket, err error) {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, err
	}
	for name := range buckets {
		t := XMLTime{
			Time: time.Now(),
		}
		result := &Bucket{
			Name: name,
			// TODO Add real created date
			CreationDate: t,
		}
		results = append(results, result)
	}
	sort.Sort(byBucketName(results))
	return results, nil
}

// PutBucket creates a new bucket
func (d *donutDriver) PutBucket(bucketName string) error {
	if IsValidBucketName(bucketName) && !strings.Contains(bucketName, ".") {
		return d.donut.MakeBucket(bucketName)
	}
	return errors.New("Invalid bucket")
}

// Get retrieves an object and writes it to a writer
func (d *donutDriver) Get(bucketName, objectName string) (body io.ReadCloser, size int64, err error) {
	if bucketName == "" || strings.TrimSpace(bucketName) == "" {
		return nil, 0, errors.New("invalid argument")
	}
	if objectName == "" || strings.TrimSpace(objectName) == "" {
		return nil, 0, errors.New("invalid argument")
	}
	reader, writer := io.Pipe()
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, 0, err
	}
	if _, ok := buckets[bucketName]; !ok {
		return nil, 0, errors.New("bucket does not exist")
	}
	objects, err := buckets[bucketName].ListObjects()
	if _, ok := objects[objectName]; !ok {
		return nil, 0, errors.New("object does not exist")
	}
	donutObjectMetadata, err := objects[objectName].GetDonutObjectMetadata()
	if err != nil {
		return nil, 0, err
	}
	size, err = strconv.ParseInt(donutObjectMetadata["size"], 10, 64)
	if err != nil {
		return nil, 0, err
	}
	go buckets[bucketName].GetObject(objectName, writer, donutObjectMetadata)
	return reader, size, nil
}

// Put creates a new object
func (d *donutDriver) Put(bucketName, objectKey string, size int64, contents io.Reader) error {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return err
	}
	objects, err := buckets[bucketName].ListObjects()
	if _, ok := objects[objectKey]; ok {
		return errors.New("Object exists")
	}
	err = buckets[bucketName].PutObject(objectKey, contents)
	if err != nil {
		return err
	}
	return nil
}

// GetPartial retrieves an object range and writes it to a writer
func (d *donutDriver) GetPartial(bucket, object string, start, length int64) (body io.ReadCloser, size int64, err error) {
	return nil, 0, errors.New("Not Implemented")
}

// Stat - gets metadata information about the object
func (d *donutDriver) Stat(bucket, object string) (size int64, date time.Time, err error) {
	return 0, time.Time{}, errors.New("Not Implemented")
}

// bySize implements sort.Interface for []Item based on the Size field.
type bySize []*Item

func (a bySize) Len() int           { return len(a) }
func (a bySize) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a bySize) Less(i, j int) bool { return a[i].Size < a[j].Size }

// ListObjects - returns list of objects
func (d *donutDriver) ListObjects(bucketName, startAt, prefix, delimiter string, maxKeys int) (items []*Item, prefixes []*Prefix, err error) {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, nil, err
	}
	objectList, err := buckets[bucketName].ListObjects()
	if err != nil {
		return nil, nil, err
	}
	var objects []string
	for key := range objectList {
		objects = append(objects, key)
	}
	sort.Strings(objects)
	if prefix != "" {
		objects = filterPrefix(objects, prefix)
		objects = removePrefix(objects, prefix)
	}
	if maxKeys <= 0 || maxKeys > 1000 {
		maxKeys = 1000
	}
	var actualObjects []string
	var commonPrefixes []string
	if strings.TrimSpace(delimiter) != "" {
		actualObjects = filterDelimited(objects, delimiter)
		commonPrefixes = filterNotDelimited(objects, delimiter)
		commonPrefixes = extractDir(commonPrefixes, delimiter)
		commonPrefixes = uniqueObjects(commonPrefixes)
	} else {
		actualObjects = objects
	}

	for _, prefix := range commonPrefixes {
		prefixes = append(prefixes, &Prefix{Prefix: prefix})
	}
	for _, object := range actualObjects {
		metadata, err := objectList[object].GetDonutObjectMetadata()
		if err != nil {
			return nil, nil, err
		}
		t1, err := time.Parse(time.RFC3339Nano, metadata["created"])
		if err != nil {
			return nil, nil, err
		}
		t := XMLTime{
			Time: t1,
		}
		size, err := strconv.ParseInt(metadata["size"], 10, 64)
		if err != nil {
			return nil, nil, err
		}
		item := &Item{
			Key:          object,
			LastModified: t,
			Size:         size,
		}
		items = append(items, item)
	}
	sort.Sort(bySize(items))
	return items, prefixes, nil
}
<commit_msg>Implement Stat()<commit_after>/*
 * Minimalist Object Storage, (C) 2015 Minio, 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 client

import (
	"errors"
	"io"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/minio-io/donut"
)

// donutDriver - creates a new single disk drivers driver using donut
type donutDriver struct {
	donut donut.Donut
}

// Object split blockSize defaulted at 10MB
const (
	blockSize = 10 * 1024 * 1024
)

// IsValidBucketName reports whether bucket is a valid bucket name, per Amazon's naming restrictions.
// See http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
func IsValidBucketName(bucket string) bool {
	if len(bucket) < 3 || len(bucket) > 63 {
		return false
	}
	if bucket[0] == '.' || bucket[len(bucket)-1] == '.' {
		return false
	}
	if match, _ := regexp.MatchString("\\.\\.", bucket); match == true {
		return false
	}
	// We don't support buckets with '.' in them
	match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\-]+[a-zA-Z0-9]$", bucket)
	return match
}

// GetNewClient returns an initialized donut driver
func GetNewClient(donutName string, nodeDiskMap map[string][]string) (Client, error) {
	var err error

	d := new(donutDriver)
	d.donut, err = donut.NewDonut(donutName, nodeDiskMap)
	if err != nil {
		return nil, err
	}
	return d, nil
}

// byBucketName is a type for sorting bucket metadata by bucket name
type byBucketName []*Bucket

func (b byBucketName) Len() int           { return len(b) }
func (b byBucketName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byBucketName) Less(i, j int) bool { return b[i].Name < b[j].Name }

// ListBuckets returns a list of buckets
func (d *donutDriver) ListBuckets() (results []*Bucket, err error) {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, err
	}
	for name := range buckets {
		t := XMLTime{
			Time: time.Now(),
		}
		result := &Bucket{
			Name: name,
			// TODO Add real created date
			CreationDate: t,
		}
		results = append(results, result)
	}
	sort.Sort(byBucketName(results))
	return results, nil
}

// PutBucket creates a new bucket
func (d *donutDriver) PutBucket(bucketName string) error {
	if IsValidBucketName(bucketName) && !strings.Contains(bucketName, ".") {
		return d.donut.MakeBucket(bucketName)
	}
	return errors.New("Invalid bucket")
}

// Get retrieves an object and writes it to a writer
func (d *donutDriver) Get(bucketName, objectName string) (body io.ReadCloser, size int64, err error) {
	if bucketName == "" || strings.TrimSpace(bucketName) == "" {
		return nil, 0, errors.New("invalid argument")
	}
	if objectName == "" || strings.TrimSpace(objectName) == "" {
		return nil, 0, errors.New("invalid argument")
	}
	reader, writer := io.Pipe()
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, 0, err
	}
	if _, ok := buckets[bucketName]; !ok {
		return nil, 0, errors.New("bucket does not exist")
	}
	objects, err := buckets[bucketName].ListObjects()
	if _, ok := objects[objectName]; !ok {
		return nil, 0, errors.New("object does not exist")
	}
	donutObjectMetadata, err := objects[objectName].GetDonutObjectMetadata()
	if err != nil {
		return nil, 0, err
	}
	size, err = strconv.ParseInt(donutObjectMetadata["size"], 10, 64)
	if err != nil {
		return nil, 0, err
	}
	go buckets[bucketName].GetObject(objectName, writer, donutObjectMetadata)
	return reader, size, nil
}

// Put creates a new object
func (d *donutDriver) Put(bucketName, objectKey string, size int64, contents io.Reader) error {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return err
	}
	objects, err := buckets[bucketName].ListObjects()
	if _, ok := objects[objectKey]; ok {
		return errors.New("Object exists")
	}
	err = buckets[bucketName].PutObject(objectKey, contents)
	if err != nil {
		return err
	}
	return nil
}

// GetPartial retrieves an object range and writes it to a writer
func (d *donutDriver) GetPartial(bucket, object string, start, length int64) (body io.ReadCloser, size int64, err error) {
	return nil, 0, errors.New("Not Implemented")
}

// Stat - gets metadata information about the object
func (d *donutDriver) Stat(bucketName, objectName string) (size int64, date time.Time, err error) {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return 0, time.Time{}, err
	}
	objectList, err := buckets[bucketName].ListObjects()
	if err != nil {
		return 0, time.Time{}, err
	}
	if _, ok := objectList[objectName]; !ok {
		return 0, time.Time{}, os.ErrNotExist
	}
	metadata, err := objectList[objectName].GetDonutObjectMetadata()
	if err != nil {
		return 0, time.Time{}, err
	}
	t1, err := time.Parse(time.RFC3339Nano, metadata["created"])
	if err != nil {
		return 0, time.Time{}, err
	}
	s, err := strconv.ParseInt(metadata["size"], 10, 64)
	if err != nil {
		return 0, time.Time{}, err
	}
	return s, t1, nil
}

// bySize implements sort.Interface for []Item based on the Size field.
type bySize []*Item

func (a bySize) Len() int           { return len(a) }
func (a bySize) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a bySize) Less(i, j int) bool { return a[i].Size < a[j].Size }

// ListObjects - returns list of objects
func (d *donutDriver) ListObjects(bucketName, startAt, prefix, delimiter string, maxKeys int) (items []*Item, prefixes []*Prefix, err error) {
	buckets, err := d.donut.ListBuckets()
	if err != nil {
		return nil, nil, err
	}
	objectList, err := buckets[bucketName].ListObjects()
	if err != nil {
		return nil, nil, err
	}
	var objects []string
	for key := range objectList {
		objects = append(objects, key)
	}
	sort.Strings(objects)
	if prefix != "" {
		objects = filterPrefix(objects, prefix)
	}
	if maxKeys <= 0 || maxKeys > 1000 {
		maxKeys = 1000
	}
	var actualObjects []string
	var commonPrefixes []string
	if strings.TrimSpace(delimiter) != "" {
		actualObjects = filterDelimited(objects, delimiter)
		commonPrefixes = filterNotDelimited(objects, delimiter)
		commonPrefixes = extractDir(commonPrefixes, delimiter)
		commonPrefixes = uniqueObjects(commonPrefixes)
	} else {
		actualObjects = objects
	}

	for _, prefix := range commonPrefixes {
		prefixes = append(prefixes, &Prefix{Prefix: prefix})
	}
	for _, object := range actualObjects {
		metadata, err := objectList[object].GetDonutObjectMetadata()
		if err != nil {
			return nil, nil, err
		}
		t1, err := time.Parse(time.RFC3339Nano, metadata["created"])
		if err != nil {
			return nil, nil, err
		}
		t := XMLTime{
			Time: t1,
		}
		size, err := strconv.ParseInt(metadata["size"], 10, 64)
		if err != nil {
			return nil, nil, err
		}
		item := &Item{
			Key:          object,
			LastModified: t,
			Size:         size,
		}
		items = append(items, item)
	}
	sort.Sort(bySize(items))
	return items, prefixes, nil
}
<|endoftext|>