<commit_before>// +build !containers_image_storage_stub

package storage

import (
	"context"
	"strings"

	"github.com/containers/image/docker/reference"
	"github.com/containers/image/types"
	"github.com/containers/storage"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// A storageReference holds an arbitrary name and/or an ID, which is a 32-byte
// value hex-encoded into a 64-character string, and a reference to a Store
// where an image is, or would be, kept.
type storageReference struct {
	transport            storageTransport
	completeReference    reference.Named // may include a tag and/or a digest
	reference            string
	id                   string
	breakDockerReference bool // Possibly set by newImageDestination.  FIXME: Figure out another way.
}

func newReference(transport storageTransport, completeReference reference.Named, reference, id string) *storageReference {
	// We take a copy of the transport, which contains a pointer to the
	// store that it used for resolving this reference, so that the
	// transport that we'll return from Transport() won't be affected by
	// further calls to the original transport's SetStore() method.
	return &storageReference{
		transport:         transport,
		completeReference: completeReference,
		reference:         reference,
		id:                id,
	}
}

// imageMatchesRepo returns true iff image.Names contains an element with the same repo as ref
func imageMatchesRepo(image *storage.Image, ref reference.Named) bool {
	repo := ref.Name()
	for _, name := range image.Names {
		if named, err := reference.ParseNormalizedNamed(name); err == nil {
			if named.Name() == repo {
				return true
			}
		}
	}
	return false
}

// Resolve the reference's name to an image ID in the store, if there's already
// one present with the same name or ID, and return the image.
func (s *storageReference) resolveImage() (*storage.Image, error) {
	if s.id == "" {
		// Look for an image that has the expanded reference name as an explicit Name value.
		image, err := s.transport.store.Image(s.reference)
		if image != nil && err == nil {
			s.id = image.ID
		}
	}
	if s.id == "" && s.completeReference != nil {
		if digested, ok := s.completeReference.(reference.Digested); ok {
			// Look for an image with the specified digest that has the same name,
			// though possibly with a different tag or digest, as a Name value, so
			// that the canonical reference can be implicitly resolved to the image.
			images, err := s.transport.store.ImagesByDigest(digested.Digest())
			if images != nil && err == nil {
				for _, image := range images {
					if imageMatchesRepo(image, s.completeReference) {
						s.id = image.ID
						break
					}
				}
			}
		}
	}
	if s.id == "" {
		logrus.Debugf("reference %q does not resolve to an image ID", s.StringWithinTransport())
		return nil, errors.Wrapf(ErrNoSuchImage, "reference %q does not resolve to an image ID", s.StringWithinTransport())
	}
	img, err := s.transport.store.Image(s.id)
	if err != nil {
		return nil, errors.Wrapf(err, "error reading image %q", s.id)
	}
	if s.completeReference != nil {
		if !imageMatchesRepo(img, s.completeReference) {
			logrus.Errorf("no image matching reference %q found", s.StringWithinTransport())
			return nil, ErrNoSuchImage
		}
	}
	return img, nil
}

// Return a Transport object that defaults to using the same store that we used
// to build this reference object.
func (s storageReference) Transport() types.ImageTransport {
	return &storageTransport{
		store:         s.transport.store,
		defaultUIDMap: s.transport.defaultUIDMap,
		defaultGIDMap: s.transport.defaultGIDMap,
	}
}

// Return a name with a tag or digest, if we have either, else return it bare.
func (s storageReference) DockerReference() reference.Named {
	if s.breakDockerReference {
		return nil
	}
	return s.completeReference
}

// Return a name with a tag, prefixed with the graph root and driver name, to
// disambiguate between images which may be present in multiple stores and
// share only their names.
func (s storageReference) StringWithinTransport() string {
	optionsList := ""
	options := s.transport.store.GraphOptions()
	if len(options) > 0 {
		optionsList = ":" + strings.Join(options, ",")
	}
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "+" + s.transport.store.RunRoot() + optionsList + "]"
	if s.reference == "" {
		return storeSpec + "@" + s.id
	}
	if s.id == "" {
		return storeSpec + s.reference
	}
	return storeSpec + s.reference + "@" + s.id
}

func (s storageReference) PolicyConfigurationIdentity() string {
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
	if s.completeReference == nil {
		return storeSpec + "@" + s.id
	}
	if s.id == "" {
		return storeSpec + s.reference
	}
	return storeSpec + s.reference + "@" + s.id
}

// Also accept policy that's tied to the combination of the graph root and
// driver name, to apply to all images stored in the Store, and to just the
// graph root, in case we're using multiple drivers in the same directory for
// some reason.
func (s storageReference) PolicyConfigurationNamespaces() []string {
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
	driverlessStoreSpec := "[" + s.transport.store.GraphRoot() + "]"
	namespaces := []string{}
	if s.completeReference != nil {
		if s.id != "" {
			// The reference without the ID is also a valid namespace.
			namespaces = append(namespaces, storeSpec+s.reference)
		}
		components := strings.Split(s.completeReference.Name(), "/")
		for len(components) > 0 {
			namespaces = append(namespaces, storeSpec+strings.Join(components, "/"))
			components = components[:len(components)-1]
		}
	}
	namespaces = append(namespaces, storeSpec)
	namespaces = append(namespaces, driverlessStoreSpec)
	return namespaces
}

// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned ImageCloser.
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
func (s storageReference) NewImage(ctx context.Context, sys *types.SystemContext) (types.ImageCloser, error) {
	return newImage(ctx, sys, s)
}

func (s storageReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error {
	img, err := s.resolveImage()
	if err != nil {
		return err
	}
	layers, err := s.transport.store.DeleteImage(img.ID, true)
	if err == nil {
		logrus.Debugf("deleted image %q", img.ID)
		for _, layer := range layers {
			logrus.Debugf("deleted layer %q", layer)
		}
	}
	return err
}

func (s storageReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) {
	return newImageSource(s)
}

func (s storageReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) {
	return newImageDestination(s)
}
<commit_msg>Eliminate all users of storageReference.reference<commit_after>// +build !containers_image_storage_stub

package storage

import (
	"context"
	"strings"

	"github.com/containers/image/docker/reference"
	"github.com/containers/image/types"
	"github.com/containers/storage"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// A storageReference holds an arbitrary name and/or an ID, which is a 32-byte
// value hex-encoded into a 64-character string, and a reference to a Store
// where an image is, or would be, kept.
type storageReference struct {
	transport            storageTransport
	completeReference    reference.Named // may include a tag and/or a digest
	reference            string
	id                   string
	breakDockerReference bool // Possibly set by newImageDestination.  FIXME: Figure out another way.
}

func newReference(transport storageTransport, completeReference reference.Named, reference, id string) *storageReference {
	// We take a copy of the transport, which contains a pointer to the
	// store that it used for resolving this reference, so that the
	// transport that we'll return from Transport() won't be affected by
	// further calls to the original transport's SetStore() method.
	return &storageReference{
		transport:         transport,
		completeReference: completeReference,
		reference:         reference,
		id:                id,
	}
}

// imageMatchesRepo returns true iff image.Names contains an element with the same repo as ref
func imageMatchesRepo(image *storage.Image, ref reference.Named) bool {
	repo := ref.Name()
	for _, name := range image.Names {
		if named, err := reference.ParseNormalizedNamed(name); err == nil {
			if named.Name() == repo {
				return true
			}
		}
	}
	return false
}

// Resolve the reference's name to an image ID in the store, if there's already
// one present with the same name or ID, and return the image.
func (s *storageReference) resolveImage() (*storage.Image, error) {
	if s.id == "" {
		// Look for an image that has the expanded reference name as an explicit Name value.
		image, err := s.transport.store.Image(s.completeReference.String())
		if image != nil && err == nil {
			s.id = image.ID
		}
	}
	if s.id == "" && s.completeReference != nil {
		if digested, ok := s.completeReference.(reference.Digested); ok {
			// Look for an image with the specified digest that has the same name,
			// though possibly with a different tag or digest, as a Name value, so
			// that the canonical reference can be implicitly resolved to the image.
			images, err := s.transport.store.ImagesByDigest(digested.Digest())
			if images != nil && err == nil {
				for _, image := range images {
					if imageMatchesRepo(image, s.completeReference) {
						s.id = image.ID
						break
					}
				}
			}
		}
	}
	if s.id == "" {
		logrus.Debugf("reference %q does not resolve to an image ID", s.StringWithinTransport())
		return nil, errors.Wrapf(ErrNoSuchImage, "reference %q does not resolve to an image ID", s.StringWithinTransport())
	}
	img, err := s.transport.store.Image(s.id)
	if err != nil {
		return nil, errors.Wrapf(err, "error reading image %q", s.id)
	}
	if s.completeReference != nil {
		if !imageMatchesRepo(img, s.completeReference) {
			logrus.Errorf("no image matching reference %q found", s.StringWithinTransport())
			return nil, ErrNoSuchImage
		}
	}
	return img, nil
}

// Return a Transport object that defaults to using the same store that we used
// to build this reference object.
func (s storageReference) Transport() types.ImageTransport {
	return &storageTransport{
		store:         s.transport.store,
		defaultUIDMap: s.transport.defaultUIDMap,
		defaultGIDMap: s.transport.defaultGIDMap,
	}
}

// Return a name with a tag or digest, if we have either, else return it bare.
func (s storageReference) DockerReference() reference.Named {
	if s.breakDockerReference {
		return nil
	}
	return s.completeReference
}

// Return a name with a tag, prefixed with the graph root and driver name, to
// disambiguate between images which may be present in multiple stores and
// share only their names.
func (s storageReference) StringWithinTransport() string {
	optionsList := ""
	options := s.transport.store.GraphOptions()
	if len(options) > 0 {
		optionsList = ":" + strings.Join(options, ",")
	}
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "+" + s.transport.store.RunRoot() + optionsList + "]"
	if s.completeReference == nil {
		return storeSpec + "@" + s.id
	}
	if s.id == "" {
		return storeSpec + s.completeReference.String()
	}
	return storeSpec + s.completeReference.String() + "@" + s.id
}

func (s storageReference) PolicyConfigurationIdentity() string {
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
	if s.completeReference == nil {
		return storeSpec + "@" + s.id
	}
	if s.id == "" {
		return storeSpec + s.completeReference.String()
	}
	return storeSpec + s.completeReference.String() + "@" + s.id
}

// Also accept policy that's tied to the combination of the graph root and
// driver name, to apply to all images stored in the Store, and to just the
// graph root, in case we're using multiple drivers in the same directory for
// some reason.
func (s storageReference) PolicyConfigurationNamespaces() []string {
	storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
	driverlessStoreSpec := "[" + s.transport.store.GraphRoot() + "]"
	namespaces := []string{}
	if s.completeReference != nil {
		if s.id != "" {
			// The reference without the ID is also a valid namespace.
			namespaces = append(namespaces, storeSpec+s.completeReference.String())
		}
		components := strings.Split(s.completeReference.Name(), "/")
		for len(components) > 0 {
			namespaces = append(namespaces, storeSpec+strings.Join(components, "/"))
			components = components[:len(components)-1]
		}
	}
	namespaces = append(namespaces, storeSpec)
	namespaces = append(namespaces, driverlessStoreSpec)
	return namespaces
}

// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned ImageCloser.
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
func (s storageReference) NewImage(ctx context.Context, sys *types.SystemContext) (types.ImageCloser, error) {
	return newImage(ctx, sys, s)
}

func (s storageReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error {
	img, err := s.resolveImage()
	if err != nil {
		return err
	}
	layers, err := s.transport.store.DeleteImage(img.ID, true)
	if err == nil {
		logrus.Debugf("deleted image %q", img.ID)
		for _, layer := range layers {
			logrus.Debugf("deleted layer %q", layer)
		}
	}
	return err
}

func (s storageReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) {
	return newImageSource(s)
}

func (s storageReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) {
	return newImageDestination(s)
}
<|endoftext|>


<commit_before>package datastore

import (
	"database/sql"
	"os"
	"time"

	"github.com/lgtmco/lgtm/store"
	"github.com/lgtmco/lgtm/store/migration"

	"github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/mattn/go-sqlite3"
	"github.com/rubenv/sql-migrate"
	"github.com/russross/meddler"
)

type datastore struct {
	*sql.DB
}

// New creates a database connection for the given driver and datasource
// and returns a new Store.
func New(driver, config string) store.Store {
	db := Open(driver, config)
	return From(db)
}

// From returns a Store using an existing database connection.
func From(db *sql.DB) store.Store {
	return &datastore{db}
}

// Open opens a new database connection with the specified
// driver and connection string and returns a store.
func Open(driver, config string) *sql.DB {
	db, err := sql.Open(driver, config)
	if err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("database connection failed")
	}
	if driver == "mysql" {
		// per issue https://github.com/go-sql-driver/mysql/issues/257
		db.SetMaxIdleConns(0)
	}

	setupMeddler(driver)

	logrus.Debugf("Driver %s", driver)
	logrus.Debugf("Data Source %s", config)

	if err := pingDatabase(db); err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("database ping attempts failed")
	}

	if err := setupDatabase(driver, db); err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("migration failed")
	}
	return db
}

// OpenTest opens a new database connection for testing purposes.
// The database driver and connection string are provided by
// environment variables, with fallback to in-memory sqlite.
func openTest() *sql.DB {
	var (
		driver = "sqlite3"
		config = ":memory:"
	)
	if os.Getenv("DATABASE_DRIVER") != "" {
		driver = os.Getenv("DATABASE_DRIVER")
		config = os.Getenv("DATABASE_DATASOURCE")
	}
	return Open(driver, config)
}

// helper function to ping the database with backoff to ensure
// a connection can be established before we proceed with the
// database setup and migration.
func pingDatabase(db *sql.DB) (err error) {
	for i := 0; i < 30; i++ {
		err = db.Ping()
		if err == nil {
			return
		}
		logrus.Infof("database ping failed. retry in 1s")
		time.Sleep(time.Second)
	}
	return
}

// helper function to setup the databsae by performing
// automated database migration steps.
func setupDatabase(driver string, db *sql.DB) error {
	var migrations = &migrate.AssetMigrationSource{
		Asset:    migration.Asset,
		AssetDir: migration.AssetDir,
		Dir:      driver,
	}
	_, err := migrate.Exec(db, driver, migrations, migrate.Up)
	return err
}

// helper function to setup the meddler default driver
// based on the selected driver name.
func setupMeddler(driver string) {
	switch driver {
	case "sqlite3":
		meddler.Default = meddler.SQLite
	case "mysql":
		meddler.Default = meddler.MySQL
	case "postgres":
		meddler.Default = meddler.PostgreSQL
	}
}
<commit_msg>Remove postgres as a suggested backend<commit_after>package datastore

import (
	"database/sql"
	"os"
	"time"

	"github.com/lgtmco/lgtm/store"
	"github.com/lgtmco/lgtm/store/migration"

	"github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/mattn/go-sqlite3"
	"github.com/rubenv/sql-migrate"
	"github.com/russross/meddler"
)

type datastore struct {
	*sql.DB
}

// New creates a database connection for the given driver and datasource
// and returns a new Store.
func New(driver, config string) store.Store {
	db := Open(driver, config)
	return From(db)
}

// From returns a Store using an existing database connection.
func From(db *sql.DB) store.Store {
	return &datastore{db}
}

// Open opens a new database connection with the specified
// driver and connection string and returns a store.
func Open(driver, config string) *sql.DB {
	db, err := sql.Open(driver, config)
	if err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("database connection failed")
	}
	if driver == "mysql" {
		// per issue https://github.com/go-sql-driver/mysql/issues/257
		db.SetMaxIdleConns(0)
	}

	setupMeddler(driver)

	logrus.Debugf("Driver %s", driver)
	logrus.Debugf("Data Source %s", config)

	if err := pingDatabase(db); err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("database ping attempts failed")
	}

	if err := setupDatabase(driver, db); err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("migration failed")
	}
	return db
}

// OpenTest opens a new database connection for testing purposes.
// The database driver and connection string are provided by
// environment variables, with fallback to in-memory sqlite.
func openTest() *sql.DB {
	var (
		driver = "sqlite3"
		config = ":memory:"
	)
	if os.Getenv("DATABASE_DRIVER") != "" {
		driver = os.Getenv("DATABASE_DRIVER")
		config = os.Getenv("DATABASE_DATASOURCE")
	}
	return Open(driver, config)
}

// helper function to ping the database with backoff to ensure
// a connection can be established before we proceed with the
// database setup and migration.
func pingDatabase(db *sql.DB) (err error) {
	for i := 0; i < 30; i++ {
		err = db.Ping()
		if err == nil {
			return
		}
		logrus.Infof("database ping failed. retry in 1s")
		time.Sleep(time.Second)
	}
	return
}

// helper function to setup the databsae by performing
// automated database migration steps.
func setupDatabase(driver string, db *sql.DB) error {
	var migrations = &migrate.AssetMigrationSource{
		Asset:    migration.Asset,
		AssetDir: migration.AssetDir,
		Dir:      driver,
	}
	_, err := migrate.Exec(db, driver, migrations, migrate.Up)
	return err
}

// helper function to setup the meddler default driver
// based on the selected driver name.
func setupMeddler(driver string) {
	switch driver {
	case "sqlite3":
		meddler.Default = meddler.SQLite
	case "mysql":
		meddler.Default = meddler.MySQL
	}
}
<|endoftext|>


<commit_before>package pigae

import "google.golang.org/appengine"

// Env encapsulates a Programming-Idioms webapp environment.
type Env struct {
	// Host depends on the target environment.
	// It should not have a trailing slash.
	IsDev           bool
	Host            string
	UseAbsoluteUrls bool
	UseMinifiedCss  bool
	UseMinifiedJs   bool
}

//
// Prod
//

var envProd = Env{
	IsDev:           false,
	Host:            "https://www.programming-idioms.org",
	UseAbsoluteUrls: true,
	UseMinifiedCss:  true,
	UseMinifiedJs:   true,
}

//
// Dev
//

var envDev = Env{
	IsDev:           true,
	Host:            "http://localhost:8080",
	UseAbsoluteUrls: true,
	UseMinifiedCss:  false,
	UseMinifiedJs:   false,
}

// Which one is used ?

var env Env

func initEnv() {
	if appengine.IsDevAppServer() {
		env = envDev
	} else {
		env = envProd
	}
}
<commit_msg>UseAbsoluteUrls: false, to fix resources on programming-idioms.org (without www).<commit_after>package pigae

import "google.golang.org/appengine"

// Env encapsulates a Programming-Idioms webapp environment.
type Env struct {
	// Host depends on the target environment.
	// It should not have a trailing slash.
	IsDev           bool
	Host            string
	UseAbsoluteUrls bool
	UseMinifiedCss  bool
	UseMinifiedJs   bool
}

//
// Prod
//

var envProd = Env{
	IsDev:           false,
	Host:            "https://www.programming-idioms.org",
	UseAbsoluteUrls: false,
	UseMinifiedCss:  true,
	UseMinifiedJs:   true,
}

//
// Dev
//

var envDev = Env{
	IsDev:           true,
	Host:            "http://localhost:8080",
	UseAbsoluteUrls: true,
	UseMinifiedCss:  false,
	UseMinifiedJs:   false,
}

// Which one is used ?

var env Env

func initEnv() {
	if appengine.IsDevAppServer() {
		env = envDev
	} else {
		env = envProd
	}
}
<|endoftext|>


<commit_before>package app

import (
	"errors"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/unit"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	"github.com/timeredbull/tsuru/repository"
	"launchpad.net/goyaml"
	"launchpad.net/mgo/bson"
	"path"
	"strings"
)

const confSep = "========"

type App struct {
	Ip        string
	Machine   int
	Name      string
	Framework string
	State     string
	Teams     []auth.Team
}

type conf struct {
	PreRestart string "pre-restart"
	PosRestart string "pos-restart"
}

func AllApps() ([]App, error) {
	var apps []App
	err := db.Session.Apps().Find(nil).All(&apps)
	return apps, err
}

func (app *App) Get() error {
	return db.Session.Apps().Find(bson.M{"name": app.Name}).One(&app)
}

func (app *App) Create() error {
	app.State = "Pending"
	err := db.Session.Apps().Insert(app)
	if err != nil {
		return err
	}
	u := app.unit()
	err = u.Create()
	if err != nil {
		return err
	}
	return nil
}

func (app *App) Destroy() error {
	err := db.Session.Apps().Remove(app)
	if err != nil {
		return err
	}
	u := app.unit()
	u.Destroy()
	return nil
}

func (app *App) findTeam(team *auth.Team) int {
	for i, t := range app.Teams {
		if t.Name == team.Name {
			return i
		}
	}
	return -1
}

func (app *App) hasTeam(team *auth.Team) bool {
	return app.findTeam(team) > -1
}

func (app *App) GrantAccess(team *auth.Team) error {
	if app.hasTeam(team) {
		return errors.New("This team has already access to this app")
	}
	app.Teams = append(app.Teams, *team)
	return nil
}

func (app *App) RevokeAccess(team *auth.Team) error {
	index := app.findTeam(team)
	if index < 0 {
		return errors.New("This team does not have access to this app")
	}
	last := len(app.Teams) - 1
	app.Teams[index] = app.Teams[last]
	app.Teams = app.Teams[:last]
	return nil
}

func (app *App) CheckUserAccess(user *auth.User) bool {
	for _, team := range app.Teams {
		if team.ContainsUser(user) {
			return true
		}
	}
	return false
}

/*
* Returns app.conf located at app's git repository
 */
func (a *App) conf() (conf, error) {
	var c conf
	u := a.unit()
	uRepo, err := repository.GetPath()
	if err != nil {
		return c, err
	}
	cPath := path.Join(uRepo, "app.info")
	cmd := fmt.Sprintf(`echo "%s";cat %s`, confSep, cPath)
	o, err := u.Command(cmd)
	data := strings.Split(string(o), confSep)[1]
	err = goyaml.Unmarshal([]byte(data), &c)
	if err != nil {
		return c, err
	}
	return c, nil
}

/*
* preRestart is responsible for running user's pre-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) preRestart(c conf) error {
	if !a.hasRestartHooks(c) {
		return errors.New("app.conf file does not exists or is in the right place.")
	}
	if c.PreRestart == "" {
		log.Printf("pre-restart hook section in app conf does not exists... Skipping...")
		return nil
	}
	u := a.unit()
	out, err := u.Command("/bin/bash", c.PreRestart)
	log.Printf("Executing pre-restart hook...")
	log.Printf(string(out))
	return err
}

/*
* posRestart is responsible for running user's pos-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) posRestart(c conf) error {
	if !a.hasRestartHooks(c) {
		return errors.New("app.conf file does not exists or is in the right place.")
	}
	if c.PosRestart == "" {
		log.Printf("pos-restart hook section in app conf does not exists... Skipping...")
		return nil
	}
	u := a.unit()
	out, err := u.Command("/bin/bash", c.PosRestart)
	log.Printf("Executing pos-restart hook...")
	log.Printf(string(out))
	return err
}

func (a *App) hasRestartHooks(c conf) bool {
	return !(c.PreRestart == "" && c.PosRestart == "")
}

func (a *App) updateHooks() error {
	u := a.unit()
	err := u.ExecuteHook("dependencies")
	if err != nil {
		return err
	}
	err = u.ExecuteHook("reload-gunicorn")
	if err != nil {
		return err
	}
	return nil
}

func (app *App) unit() unit.Unit {
	return unit.Unit{Name: app.Name, Type: app.Framework, Machine: app.Machine}
}
<commit_msg>api/app: changed name of variable used for target to make it coherent<commit_after>package app

import (
	"errors"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/unit"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	"github.com/timeredbull/tsuru/repository"
	"launchpad.net/goyaml"
	"launchpad.net/mgo/bson"
	"path"
	"strings"
)

const confSep = "========"

type App struct {
	Ip        string
	Machine   int
	Name      string
	Framework string
	State     string
	Teams     []auth.Team
}

type conf struct {
	PreRestart string "pre-restart"
	PosRestart string "pos-restart"
}

func AllApps() ([]App, error) {
	var apps []App
	err := db.Session.Apps().Find(nil).All(&apps)
	return apps, err
}

func (a *App) Get() error {
	return db.Session.Apps().Find(bson.M{"name": a.Name}).One(&a)
}

func (a *App) Create() error {
	a.State = "Pending"
	err := db.Session.Apps().Insert(a)
	if err != nil {
		return err
	}
	u := a.unit()
	err = u.Create()
	if err != nil {
		return err
	}
	return nil
}

func (a *App) Destroy() error {
	err := db.Session.Apps().Remove(a)
	if err != nil {
		return err
	}
	u := a.unit()
	u.Destroy()
	return nil
}

func (a *App) findTeam(team *auth.Team) int {
	for i, t := range a.Teams {
		if t.Name == team.Name {
			return i
		}
	}
	return -1
}

func (a *App) hasTeam(team *auth.Team) bool {
	return a.findTeam(team) > -1
}

func (a *App) GrantAccess(team *auth.Team) error {
	if a.hasTeam(team) {
		return errors.New("This team has already access to this a")
	}
	a.Teams = append(a.Teams, *team)
	return nil
}

func (a *App) RevokeAccess(team *auth.Team) error {
	index := a.findTeam(team)
	if index < 0 {
		return errors.New("This team does not have access to this a")
	}
	last := len(a.Teams) - 1
	a.Teams[index] = a.Teams[last]
	a.Teams = a.Teams[:last]
	return nil
}

func (a *App) CheckUserAccess(user *auth.User) bool {
	for _, team := range a.Teams {
		if team.ContainsUser(user) {
			return true
		}
	}
	return false
}

/*
* Returns app.conf located at app's git repository
 */
func (a *App) conf() (conf, error) {
	var c conf
	u := a.unit()
	uRepo, err := repository.GetPath()
	if err != nil {
		return c, err
	}
	cPath := path.Join(uRepo, "app.info")
	cmd := fmt.Sprintf(`echo "%s";cat %s`, confSep, cPath)
	o, err := u.Command(cmd)
	data := strings.Split(string(o), confSep)[1]
	err = goyaml.Unmarshal([]byte(data), &c)
	if err != nil {
		return c, err
	}
	return c, nil
}

/*
* preRestart is responsible for running user's pre-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) preRestart(c conf) error {
	if !a.hasRestartHooks(c) {
		return errors.New("app.conf file does not exists or is in the right place.")
	}
	if c.PreRestart == "" {
		log.Printf("pre-restart hook section in app conf does not exists... Skipping...")
		return nil
	}
	u := a.unit()
	out, err := u.Command("/bin/bash", c.PreRestart)
	log.Printf("Executing pre-restart hook...")
	log.Printf(string(out))
	return err
}

/*
* posRestart is responsible for running user's pos-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) posRestart(c conf) error {
	if !a.hasRestartHooks(c) {
		return errors.New("app.conf file does not exists or is in the right place.")
	}
	if c.PosRestart == "" {
		log.Printf("pos-restart hook section in app conf does not exists... Skipping...")
		return nil
	}
	u := a.unit()
	out, err := u.Command("/bin/bash", c.PosRestart)
	log.Printf("Executing pos-restart hook...")
	log.Printf(string(out))
	return err
}

func (a *App) hasRestartHooks(c conf) bool {
	return !(c.PreRestart == "" && c.PosRestart == "")
}

func (a *App) updateHooks() error {
	u := a.unit()
	err := u.ExecuteHook("dependencies")
	if err != nil {
		return err
	}
	err = u.ExecuteHook("reload-gunicorn")
	if err != nil {
		return err
	}
	return nil
}

func (a *App) unit() unit.Unit {
	return unit.Unit{Name: a.Name, Type: a.Framework, Machine: a.Machine}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Google LLC

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

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

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

package commands

import (
	"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
	"github.com/docker/docker/builder/dockerfile/instructions"
	"github.com/google/go-containerregistry/v1"
	"github.com/sirupsen/logrus"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
)

type RunCommand struct {
	cmd *instructions.RunCommand
}

func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
	var newCommand []string
	if r.cmd.PrependShell {
		// This is the default shell on Linux
		var shell []string
		if len(config.Shell) > 0 {
			shell = config.Shell
		} else {
			shell = append(shell, "/bin/sh", "-c")
		}

		newCommand = append(shell, strings.Join(r.cmd.CmdLine, " "))
	} else {
		newCommand = r.cmd.CmdLine
	}

	logrus.Infof("cmd: %s", newCommand[0])
	logrus.Infof("args: %s", newCommand[1:])

	cmd := exec.Command(newCommand[0], newCommand[1:]...)
	cmd.Dir = config.WorkingDir
	cmd.Stdout = os.Stdout
	replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
	cmd.Env = replacementEnvs

	// If specified, run the command as a specific user
	if config.User != "" {
		userAndGroup := strings.Split(config.User, ":")
		// uid and gid need to be uint32
		uid64, err := strconv.ParseUint(userAndGroup[0], 10, 32)
		if err != nil {
			return err
		}
		uid := uint32(uid64)
		var gid uint32
		if len(userAndGroup) > 1 {
			gid64, err := strconv.ParseUint(userAndGroup[1], 10, 32)
			if err != nil {
				return err
			}
			gid = uint32(gid64)
		}
		cmd.SysProcAttr = &syscall.SysProcAttr{}
		cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
	}
	return cmd.Run()
}

// FilesToSnapshot returns nil for this command because we don't know which files
// have changed, so we snapshot the entire system.
func (r *RunCommand) FilesToSnapshot() []string {
	return nil
}

// CreatedBy returns some information about the command for the image config
func (r *RunCommand) CreatedBy() string {
	cmdLine := strings.Join(r.cmd.CmdLine, " ")
	if r.cmd.PrependShell {
		// TODO: Support shell command here
		shell := []string{"/bin/sh", "-c"}
		return strings.Join(append(shell, cmdLine), " ")
	}
	return cmdLine
}
<commit_msg>include stderr of RUN commands in log output (#187)<commit_after>/*
Copyright 2018 Google LLC

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

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

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

package commands

import (
	"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
	"github.com/docker/docker/builder/dockerfile/instructions"
	"github.com/google/go-containerregistry/v1"
	"github.com/sirupsen/logrus"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
)

type RunCommand struct {
	cmd *instructions.RunCommand
}

func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
	var newCommand []string
	if r.cmd.PrependShell {
		// This is the default shell on Linux
		var shell []string
		if len(config.Shell) > 0 {
			shell = config.Shell
		} else {
			shell = append(shell, "/bin/sh", "-c")
		}

		newCommand = append(shell, strings.Join(r.cmd.CmdLine, " "))
	} else {
		newCommand = r.cmd.CmdLine
	}

	logrus.Infof("cmd: %s", newCommand[0])
	logrus.Infof("args: %s", newCommand[1:])

	cmd := exec.Command(newCommand[0], newCommand[1:]...)
	cmd.Dir = config.WorkingDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
	cmd.Env = replacementEnvs

	// If specified, run the command as a specific user
	if config.User != "" {
		userAndGroup := strings.Split(config.User, ":")
		// uid and gid need to be uint32
		uid64, err := strconv.ParseUint(userAndGroup[0], 10, 32)
		if err != nil {
			return err
		}
		uid := uint32(uid64)
		var gid uint32
		if len(userAndGroup) > 1 {
			gid64, err := strconv.ParseUint(userAndGroup[1], 10, 32)
			if err != nil {
				return err
			}
			gid = uint32(gid64)
		}
		cmd.SysProcAttr = &syscall.SysProcAttr{}
		cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
	}
	return cmd.Run()
}

// FilesToSnapshot returns nil for this command because we don't know which files
// have changed, so we snapshot the entire system.
func (r *RunCommand) FilesToSnapshot() []string {
	return nil
}

// CreatedBy returns some information about the command for the image config
func (r *RunCommand) CreatedBy() string {
	cmdLine := strings.Join(r.cmd.CmdLine, " ")
	if r.cmd.PrependShell {
		// TODO: Support shell command here
		shell := []string{"/bin/sh", "-c"}
		return strings.Join(append(shell, cmdLine), " ")
	}
	return cmdLine
}
<|endoftext|>


<commit_before>package edit

import (
	"os"

	"src.elv.sh/pkg/cli"
	"src.elv.sh/pkg/cli/histutil"
	"src.elv.sh/pkg/cli/modes"
	"src.elv.sh/pkg/cli/tk"
	"src.elv.sh/pkg/edit/filter"
	"src.elv.sh/pkg/eval"
	"src.elv.sh/pkg/eval/vals"
	"src.elv.sh/pkg/eval/vars"
	"src.elv.sh/pkg/store/storedefs"
)

func initListings(ed *Editor, ev *eval.Evaler, st storedefs.Store, histStore histutil.Store, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	app := ed.app
	nb.AddNs("listing",
		eval.BuildNsNamed("edit:listing").
			AddVar("binding", bindingVar).
			AddGoFns(map[string]interface{}{
				"accept":     func() { listingAccept(app) },
				"up":         func() { listingUp(app) },
				"down":       func() { listingDown(app) },
				"up-cycle":   func() { listingUpCycle(app) },
				"down-cycle": func() { listingDownCycle(app) },
				"page-up":    func() { listingPageUp(app) },
				"page-down":  func() { listingPageDown(app) },
				"start-custom": func(fm *eval.Frame, opts customListingOpts, items interface{}) {
					listingStartCustom(ed, fm, opts, items)
				},
			}))

	initHistlist(ed, ev, histStore, bindingVar, nb)
	initLastcmd(ed, ev, histStore, bindingVar, nb)
	initLocation(ed, ev, st, bindingVar, nb)
}

var filterSpec = modes.FilterSpec{
	Maker: func(f string) func(string) bool {
		q, _ := filter.Compile(f)
		if q == nil {
			return func(string) bool { return true }
		}
		return q.Match
	},
	Highlighter: filter.Highlight,
}

func initHistlist(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	dedup := newBoolVar(true)
	nb.AddNs("histlist",
		eval.BuildNsNamed("edit:histlist").
			AddVar("binding", bindingVar).
			AddGoFns(map[string]interface{}{
				"start": func() {
					w, err := modes.NewHistlist(ed.app, modes.HistlistSpec{
						Bindings: bindings,
						AllCmds:  histStore.AllCmds,
						Dedup: func() bool {
							return dedup.Get().(bool)
						},
						Filter: filterSpec,
					})
					startMode(ed.app, w, err)
				},
				"toggle-dedup": func() {
					dedup.Set(!dedup.Get().(bool))
					listingRefilter(ed.app)
					ed.app.Redraw()
				},
			}))
}

func initLastcmd(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	nb.AddNs("lastcmd",
		eval.BuildNsNamed("edit:lastcmd").
			AddVar("binding", bindingVar).
			AddGoFn("start", func() {
				// TODO: Specify wordifier
				w, err := modes.NewLastcmd(ed.app, modes.LastcmdSpec{
					Bindings: bindings, Store: histStore})
				startMode(ed.app, w, err)
			}))
}

func initLocation(ed *Editor, ev *eval.Evaler, st storedefs.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	pinnedVar := newListVar(vals.EmptyList)
	hiddenVar := newListVar(vals.EmptyList)
	workspacesVar := newMapVar(vals.EmptyMap)

	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	workspaceIterator := modes.LocationWSIterator(
		adaptToIterateStringPair(workspacesVar))

	nb.AddNs("location",
		eval.BuildNsNamed("edit:location").
			AddVars(map[string]vars.Var{
				"binding":    bindingVar,
				"hidden":     hiddenVar,
				"pinned":     pinnedVar,
				"workspaces": workspacesVar,
			}).
			AddGoFn("start", func() {
				w, err := modes.NewLocation(ed.app, modes.LocationSpec{
					Bindings: bindings, Store: dirStore{ev, st},
					IteratePinned:     adaptToIterateString(pinnedVar),
					IterateHidden:     adaptToIterateString(hiddenVar),
					IterateWorkspaces: workspaceIterator,
					Filter:            filterSpec,
				})
				startMode(ed.app, w, err)
			}))
	ev.AddAfterChdir(func(string) {
		wd, err := os.Getwd()
		if err != nil {
			// TODO(xiaq): Surface the error.
			return
		}
		if st != nil {
			st.AddDir(wd, 1)
			kind, root := workspaceIterator.Parse(wd)
			if kind != "" {
				st.AddDir(kind+wd[len(root):], 1)
			}
		}
	})
}

//elvdoc:fn listing:accept
//
// Accepts the current selected listing item.

func listingAccept(app cli.App) {
	if w, ok := activeComboBox(app); ok {
		w.ListBox().Accept()
	}
}

//elvdoc:fn listing:up
//
// Moves the cursor up in listing mode.

func listingUp(app cli.App) { listingSelect(app, tk.Prev) }

//elvdoc:fn listing:down
//
// Moves the cursor down in listing mode.

func listingDown(app cli.App) { listingSelect(app, tk.Next) }

//elvdoc:fn listing:up-cycle
//
// Moves the cursor up in listing mode, or to the last item if the first item is
// currently selected.

func listingUpCycle(app cli.App) { listingSelect(app, tk.PrevWrap) }

//elvdoc:fn listing:down-cycle
//
// Moves the cursor down in listing mode, or to the first item if the last item is
// currently selected.

func listingDownCycle(app cli.App) { listingSelect(app, tk.NextWrap) }

//elvdoc:fn listing:page-up
//
// Moves the cursor up one page.

func listingPageUp(app cli.App) { listingSelect(app, tk.PrevPage) }

//elvdoc:fn listing:page-down
//
// Moves the cursor down one page.

func listingPageDown(app cli.App) { listingSelect(app, tk.NextPage) }

//elvdoc:fn listing:left
//
// Moves the cursor left in listing mode.

func listingLeft(app cli.App) { listingSelect(app, tk.Left) }

//elvdoc:fn listing:right
//
// Moves the cursor right in listing mode.

func listingRight(app cli.App) { listingSelect(app, tk.Right) }

func listingSelect(app cli.App, f func(tk.ListBoxState) int) {
	if w, ok := activeComboBox(app); ok {
		w.ListBox().Select(f)
	}
}

func listingRefilter(app cli.App) {
	if w, ok := activeComboBox(app); ok {
		w.Refilter()
	}
}

//elvdoc:var location:hidden
//
// A list of directories to hide in the location addon.

//elvdoc:var location:pinned
//
// A list of directories to always show at the top of the list of the location
// addon.

//elvdoc:var location:workspaces
//
// A map mapping types of workspaces to their patterns.

func adaptToIterateString(variable vars.Var) func(func(string)) {
	return func(f func(s string)) {
		vals.Iterate(variable.Get(), func(v interface{}) bool {
			f(vals.ToString(v))
			return true
		})
	}
}

func adaptToIterateStringPair(variable vars.Var) func(func(string, string) bool) {
	return func(f func(a, b string) bool) {
		m := variable.Get().(vals.Map)
		for it := m.Iterator(); it.HasElem(); it.Next() {
			k, v := it.Elem()
			ks, kok := k.(string)
			vs, vok := v.(string)
			if kok && vok {
				next := f(ks, vs)
				if !next {
					break
				}
			}
		}
	}
}

// Wraps an Evaler to implement the cli.DirStore interface.
type dirStore struct {
	ev *eval.Evaler
	st storedefs.Store
}

func (d dirStore) Chdir(path string) error {
	return d.ev.Chdir(path)
}

func (d dirStore) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
	return d.st.Dirs(blacklist)
}

func (d dirStore) Getwd() (string, error) {
	return os.Getwd()
}

func startMode(app cli.App, w tk.Widget, err error) {
	if w != nil {
		app.PushAddon(w)
		app.Redraw()
	}
	if err != nil {
		app.Notify(modes.ErrorText(err))
	}
}

func activeComboBox(app cli.App) (tk.ComboBox, bool) {
	w, ok := app.ActiveWidget().(tk.ComboBox)
	return w, ok
}
<commit_msg>No-daemon build don't panic starting location mode<commit_after>package edit

import (
	"os"

	"src.elv.sh/pkg/cli"
	"src.elv.sh/pkg/cli/histutil"
	"src.elv.sh/pkg/cli/modes"
	"src.elv.sh/pkg/cli/tk"
	"src.elv.sh/pkg/edit/filter"
	"src.elv.sh/pkg/eval"
	"src.elv.sh/pkg/eval/vals"
	"src.elv.sh/pkg/eval/vars"
	"src.elv.sh/pkg/store/storedefs"
)

func initListings(ed *Editor, ev *eval.Evaler, st storedefs.Store, histStore histutil.Store, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	app := ed.app
	nb.AddNs("listing",
		eval.BuildNsNamed("edit:listing").
			AddVar("binding", bindingVar).
			AddGoFns(map[string]interface{}{
				"accept":     func() { listingAccept(app) },
				"up":         func() { listingUp(app) },
				"down":       func() { listingDown(app) },
				"up-cycle":   func() { listingUpCycle(app) },
				"down-cycle": func() { listingDownCycle(app) },
				"page-up":    func() { listingPageUp(app) },
				"page-down":  func() { listingPageDown(app) },
				"start-custom": func(fm *eval.Frame, opts customListingOpts, items interface{}) {
					listingStartCustom(ed, fm, opts, items)
				},
			}))

	initHistlist(ed, ev, histStore, bindingVar, nb)
	initLastcmd(ed, ev, histStore, bindingVar, nb)
	initLocation(ed, ev, st, bindingVar, nb)
}

var filterSpec = modes.FilterSpec{
	Maker: func(f string) func(string) bool {
		q, _ := filter.Compile(f)
		if q == nil {
			return func(string) bool { return true }
		}
		return q.Match
	},
	Highlighter: filter.Highlight,
}

func initHistlist(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	dedup := newBoolVar(true)
	nb.AddNs("histlist",
		eval.BuildNsNamed("edit:histlist").
			AddVar("binding", bindingVar).
			AddGoFns(map[string]interface{}{
				"start": func() {
					w, err := modes.NewHistlist(ed.app, modes.HistlistSpec{
						Bindings: bindings,
						AllCmds:  histStore.AllCmds,
						Dedup: func() bool {
							return dedup.Get().(bool)
						},
						Filter: filterSpec,
					})
					startMode(ed.app, w, err)
				},
				"toggle-dedup": func() {
					dedup.Set(!dedup.Get().(bool))
					listingRefilter(ed.app)
					ed.app.Redraw()
				},
			}))
}

func initLastcmd(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	nb.AddNs("lastcmd",
		eval.BuildNsNamed("edit:lastcmd").
			AddVar("binding", bindingVar).
			AddGoFn("start", func() {
				// TODO: Specify wordifier
				w, err := modes.NewLastcmd(ed.app, modes.LastcmdSpec{
					Bindings: bindings, Store: histStore})
				startMode(ed.app, w, err)
			}))
}

func initLocation(ed *Editor, ev *eval.Evaler, st storedefs.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
	bindingVar := newBindingVar(emptyBindingsMap)
	pinnedVar := newListVar(vals.EmptyList)
	hiddenVar := newListVar(vals.EmptyList)
	workspacesVar := newMapVar(vals.EmptyMap)

	bindings := newMapBindings(ed, ev, bindingVar, commonBindingVar)
	workspaceIterator := modes.LocationWSIterator(
		adaptToIterateStringPair(workspacesVar))

	nb.AddNs("location",
		eval.BuildNsNamed("edit:location").
			AddVars(map[string]vars.Var{
				"binding":    bindingVar,
				"hidden":     hiddenVar,
				"pinned":     pinnedVar,
				"workspaces": workspacesVar,
			}).
			AddGoFn("start", func() {
				w, err := modes.NewLocation(ed.app, modes.LocationSpec{
					Bindings: bindings, Store: dirStore{ev, st},
					IteratePinned:     adaptToIterateString(pinnedVar),
					IterateHidden:     adaptToIterateString(hiddenVar),
					IterateWorkspaces: workspaceIterator,
					Filter:            filterSpec,
				})
				startMode(ed.app, w, err)
			}))
	ev.AddAfterChdir(func(string) {
		wd, err := os.Getwd()
		if err != nil {
			// TODO(xiaq): Surface the error.
			return
		}
		if st != nil {
			st.AddDir(wd, 1)
			kind, root := workspaceIterator.Parse(wd)
			if kind != "" {
				st.AddDir(kind+wd[len(root):], 1)
			}
		}
	})
}

//elvdoc:fn listing:accept
//
// Accepts the current selected listing item.

func listingAccept(app cli.App) {
	if w, ok := activeComboBox(app); ok {
		w.ListBox().Accept()
	}
}

//elvdoc:fn listing:up
//
// Moves the cursor up in listing mode.

func listingUp(app cli.App) { listingSelect(app, tk.Prev) }

//elvdoc:fn listing:down
//
// Moves the cursor down in listing mode.

func listingDown(app cli.App) { listingSelect(app, tk.Next) }

//elvdoc:fn listing:up-cycle
//
// Moves the cursor up in listing mode, or to the last item if the first item is
// currently selected.

func listingUpCycle(app cli.App) { listingSelect(app, tk.PrevWrap) }

//elvdoc:fn listing:down-cycle
//
// Moves the cursor down in listing mode, or to the first item if the last item is
// currently selected.

func listingDownCycle(app cli.App) { listingSelect(app, tk.NextWrap) }

//elvdoc:fn listing:page-up
//
// Moves the cursor up one page.

func listingPageUp(app cli.App) { listingSelect(app, tk.PrevPage) }

//elvdoc:fn listing:page-down
//
// Moves the cursor down one page.

func listingPageDown(app cli.App) { listingSelect(app, tk.NextPage) }

//elvdoc:fn listing:left
//
// Moves the cursor left in listing mode.

func listingLeft(app cli.App) { listingSelect(app, tk.Left) }

//elvdoc:fn listing:right
//
// Moves the cursor right in listing mode.

func listingRight(app cli.App) { listingSelect(app, tk.Right) }

func listingSelect(app cli.App, f func(tk.ListBoxState) int) {
	if w, ok := activeComboBox(app); ok {
		w.ListBox().Select(f)
	}
}

func listingRefilter(app cli.App) {
	if w, ok := activeComboBox(app); ok {
		w.Refilter()
	}
}

//elvdoc:var location:hidden
//
// A list of directories to hide in the location addon.

//elvdoc:var location:pinned
//
// A list of directories to always show at the top of the list of the location
// addon.

//elvdoc:var location:workspaces
//
// A map mapping types of workspaces to their patterns.

func adaptToIterateString(variable vars.Var) func(func(string)) {
	return func(f func(s string)) {
		vals.Iterate(variable.Get(), func(v interface{}) bool {
			f(vals.ToString(v))
			return true
		})
	}
}

func adaptToIterateStringPair(variable vars.Var) func(func(string, string) bool) {
	return func(f func(a, b string) bool) {
		m := variable.Get().(vals.Map)
		for it := m.Iterator(); it.HasElem(); it.Next() {
			k, v := it.Elem()
			ks, kok := k.(string)
			vs, vok := v.(string)
			if kok && vok {
				next := f(ks, vs)
				if !next {
					break
				}
			}
		}
	}
}

// Wraps an Evaler to implement the cli.DirStore interface.
type dirStore struct {
	ev *eval.Evaler
	st storedefs.Store
}

func (d dirStore) Chdir(path string) error {
	return d.ev.Chdir(path)
}

func (d dirStore) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
	if d.st == nil {
		// A "no daemon" build won't have have a storedefs.Store object.
		// Fail gracefully rather than panic.
		return []storedefs.Dir{}, nil
	}
	return d.st.Dirs(blacklist)
}

func (d dirStore) Getwd() (string, error) {
	return os.Getwd()
}

func startMode(app cli.App, w tk.Widget, err error) {
	if w != nil {
		app.PushAddon(w)
		app.Redraw()
	}
	if err != nil {
		app.Notify(modes.ErrorText(err))
	}
}

func activeComboBox(app cli.App) (tk.ComboBox, bool) {
	w, ok := app.ActiveWidget().(tk.ComboBox)
	return w, ok
}
<|endoftext|>


<commit_before>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"k8s.io/kubernetes/pkg/api"
	k8slabels "k8s.io/kubernetes/pkg/labels"
	"strings"
)

type IstioRouteRule struct {
	Service string
	Cluster *Cluster
}

// ProcessIstioIngress processes global rules and applies Istio routing rules for ingresses
func (usage *ServiceUsageState) ProcessIstioIngress(noop bool) {
	if len(usage.getResolvedUsage().ComponentProcessingOrder) == 0 || noop {
		return
	}

	fmt.Println("[Route Rules (Istio)]")

	progress := NewProgress()
	progressBar := AddProgressBar(progress, len(usage.getResolvedUsage().ComponentProcessingOrder) + len(usage.Policy.Clusters))

	existingRules := make([]*IstioRouteRule, 0)

	for _, cluster := range usage.Policy.Clusters {
		existingRules = append(existingRules, cluster.getIstioRouteRules()...)

		progressBar.Incr()
	}

	desiredRules := make([]*IstioRouteRule, 0)

	// Process in the right order
	for _, key := range usage.getResolvedUsage().ComponentProcessingOrder {
		rules, err := processComponent(key, usage)
		if err != nil {
			debug.WithFields(log.Fields{
				"key":   key,
				"error": err,
			}).Fatal("Unable to process Istio Ingress for component")
		}
		desiredRules = append(desiredRules, rules...)
		progressBar.Incr()
	}

	deleteRules := make([]*IstioRouteRule, 0)
	createRules := make([]*IstioRouteRule, 0)

	for _, existingRule := range existingRules {
		found := false
		for _, desiredRule := range desiredRules {
			if existingRule.Service == desiredRule.Service && existingRule.Cluster.Name == desiredRule.Cluster.Name {
				found = true
			}
		}
		if !found {
			deleteRules = append(deleteRules, existingRule)
		}
	}

	for _, desiredRule := range desiredRules {
		found := false
		for _, existingRule := range existingRules {
			if desiredRule.Service == existingRule.Service && desiredRule.Cluster.Name == existingRule.Cluster.Name {
				found = true
			}
		}
		if !found {
			createRules = append(createRules, desiredRule)
		}
	}

	tbdLen := len(createRules) + len(deleteRules)

	if tbdLen > 0 {
		progressBar = AddProgressBar(progress, tbdLen)

		for _, rule := range createRules {
			rule.create()
			progressBar.Incr()
		}

		for _, rule := range deleteRules {
			rule.delete()
			progressBar.Incr()
		}
	}

	progress.Stop()

	if tbdLen > 0 {
		for _, rule := range deleteRules {
			fmt.Println("  [-] " + rule.Service)
		}
		for _, rule := range createRules {
			fmt.Println("  [+] " + rule.Service)
		}
	} else {
		fmt.Println("  [*] No changes")
	}

	// todo(slukjanov): add actual istio route rules creation/deletion here
}

func processComponent(key string, usage *ServiceUsageState) ([]*IstioRouteRule, error) {
	serviceName, _, _, componentName := ParseServiceUsageKey(key)
	component := usage.Policy.Services[serviceName].getComponentsMap()[componentName]

	labels := usage.ResolvedUsage.ComponentInstanceMap[key].CalculatedLabels

	// todo(slukjanov): temp hack - expecting that cluster is always passed through the label "cluster"
	var cluster *Cluster
	if clusterLabel, ok := labels.Labels["cluster"]; ok {
		if cluster, ok = usage.Policy.Clusters[clusterLabel]; !ok {
			debug.WithFields(log.Fields{
				"component": key,
				"labels":    labels.Labels,
			}).Fatal("Can't find cluster for component (based on label 'cluster')")
		}
	}

	// get all users who're using service
	userIds := usage.ResolvedUsage.ComponentInstanceMap[key].UserIds
	users := make([]*User, 0)
	for _, userID := range userIds {
		// todo check if user doesn't exists
		users = append(users, usage.users.Users[userID])
	}

	if !usage.Policy.Rules.allowsIngressAccess(labels, users, cluster) && component != nil && component.Code != nil {
		codeExecutor, err := component.Code.GetCodeExecutor(key, component.Code.Metadata, usage.getResolvedUsage().ComponentInstanceMap[key].CalculatedCodeParams, usage.Policy.Clusters)
		if err != nil {
			return nil, err
		}

		if helmCodeExecutor, ok := codeExecutor.(HelmCodeExecutor); ok {
			services, err := helmCodeExecutor.httpServices()
			if err != nil {
				return nil, err
			}

			rules := make([]*IstioRouteRule, 0)

			for _, service := range services {
				rules = append(rules, &IstioRouteRule{service, cluster})
			}

			return rules, nil
		}
	}

	return nil, nil
}

// httpServices returns list of services for the current chart
func (exec HelmCodeExecutor) httpServices() ([]string, error) {
	_, clientset, err := exec.Cluster.newKubeClient()
	if err != nil {
		return nil, err
	}

	coreClient := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := k8slabels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}

	// Check all corresponding services
	services, err := coreClient.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	// Check all corresponding Istio ingresses
	ingresses, err := clientset.Extensions().Ingresses(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	if len(ingresses.Items) > 0 {
		result := make([]string, 0)
		for _, service := range services.Items {
			result = append(result, service.Name)
		}

		return result, nil
	}

	return nil, nil
}

func (cluster *Cluster) getIstioRouteRules() []*IstioRouteRule {
	cmd := "get route-rules"
	rulesStr, err := cluster.runIstioCmd(cmd)
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": cluster.Name,
			"cmd": cmd,
			"error": err,
		}).Fatal("Failed to get route-rules by running bash cmd")
	}

	rules := make([]*IstioRouteRule, 0)

	for _, ruleName := range strings.Split(rulesStr, "\n") {
		if ruleName == "" {
			continue
		}
		rules = append(rules, &IstioRouteRule{ruleName, cluster})
	}

	return rules
}

func (rule *IstioRouteRule) create() {
	content := "type: route-rule\n"
	content += "name: " + rule.Service + "\n"
	content += "spec:\n"
	content += "  destination: " + rule.Service + "." + rule.Cluster.Metadata.Namespace + ".svc.cluster.local\n"
	content += "  httpReqTimeout:\n"
	content += "    simpleTimeout:\n"
	content += "      timeout: 1ms\n"

	ruleFile := writeTempFile("istio-rule", content)

	out, err := rule.Cluster.runIstioCmd("create -f " + ruleFile.Name())
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": rule.Cluster.Name,
			"content": content,
			"out": out,
			"error": err,
		}).Fatal("Failed to create istio rule by running bash script")
	}
}

func (rule *IstioRouteRule) delete() {
	out, err := rule.Cluster.runIstioCmd("delete route-rule " + rule.Service)
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": rule.Cluster.Name,
			"out": out,
			"error": err,
		}).Fatal("Failed to delete istio rule by running bash script")
	}
}

func (cluster *Cluster) runIstioCmd(cmd string) (string, error) {
	istioSvc := cluster.Metadata.istioSvc
	if istioSvc == "" {
		_, clientset, err := cluster.newKubeClient()
		if err != nil {
			return "", err
		}

		coreClient := clientset.Core()

		selector := k8slabels.Set{"app": "istio"}.AsSelector()
		options := api.ListOptions{LabelSelector: selector}

		services, err := coreClient.Services(cluster.Metadata.Namespace).List(options)
		if err != nil {
			return "", err
		}

		for _, service := range services.Items {
			if strings.Contains(service.Name, "pilot") {
				istioSvc = service.Name

				for _, port := range service.Spec.Ports {
					if port.Name == "http-apiserver" {
						istioSvc = fmt.Sprintf("%s:%d", istioSvc, port.Port)
						break
					}
				}

				cluster.Metadata.istioSvc = istioSvc
				break
			}
		}
	}

	content := "set -e\n"
	content += "kubectl config use-context " + cluster.Name + " 1>/dev/null\n"
	content += "istioctl --configAPIService " + cluster.Metadata.istioSvc + " --namespace " + cluster.Metadata.Namespace + " "
	content += cmd + "\n"

	cmdFile := writeTempFile("istioctl-cmd", content)

	out, err := runCmd("bash", cmdFile.Name())
	if err != nil {
		return "", err
	}

	return out, nil
}
<commit_msg>Remove obsolete todo<commit_after>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"k8s.io/kubernetes/pkg/api"
	k8slabels "k8s.io/kubernetes/pkg/labels"
	"strings"
)

type IstioRouteRule struct {
	Service string
	Cluster *Cluster
}

// ProcessIstioIngress processes global rules and applies Istio routing rules for ingresses
func (usage *ServiceUsageState) ProcessIstioIngress(noop bool) {
	if len(usage.getResolvedUsage().ComponentProcessingOrder) == 0 || noop {
		return
	}

	fmt.Println("[Route Rules (Istio)]")

	progress := NewProgress()
	progressBar := AddProgressBar(progress, len(usage.getResolvedUsage().ComponentProcessingOrder) + len(usage.Policy.Clusters))

	existingRules := make([]*IstioRouteRule, 0)

	for _, cluster := range usage.Policy.Clusters {
		existingRules = append(existingRules, cluster.getIstioRouteRules()...)

		progressBar.Incr()
	}

	desiredRules := make([]*IstioRouteRule, 0)

	// Process in the right order
	for _, key := range usage.getResolvedUsage().ComponentProcessingOrder {
		rules, err := processComponent(key, usage)
		if err != nil {
			debug.WithFields(log.Fields{
				"key":   key,
				"error": err,
			}).Fatal("Unable to process Istio Ingress for component")
		}
		desiredRules = append(desiredRules, rules...)
		progressBar.Incr()
	}

	deleteRules := make([]*IstioRouteRule, 0)
	createRules := make([]*IstioRouteRule, 0)

	for _, existingRule := range existingRules {
		found := false
		for _, desiredRule := range desiredRules {
			if existingRule.Service == desiredRule.Service && existingRule.Cluster.Name == desiredRule.Cluster.Name {
				found = true
			}
		}
		if !found {
			deleteRules = append(deleteRules, existingRule)
		}
	}

	for _, desiredRule := range desiredRules {
		found := false
		for _, existingRule := range existingRules {
			if desiredRule.Service == existingRule.Service && desiredRule.Cluster.Name == existingRule.Cluster.Name {
				found = true
			}
		}
		if !found {
			createRules = append(createRules, desiredRule)
		}
	}

	tbdLen := len(createRules) + len(deleteRules)

	if tbdLen > 0 {
		progressBar = AddProgressBar(progress, tbdLen)

		for _, rule := range createRules {
			rule.create()
			progressBar.Incr()
		}

		for _, rule := range deleteRules {
			rule.delete()
			progressBar.Incr()
		}
	}

	progress.Stop()

	if tbdLen > 0 {
		for _, rule := range deleteRules {
			fmt.Println("  [-] " + rule.Service)
		}
		for _, rule := range createRules {
			fmt.Println("  [+] " + rule.Service)
		}
	} else {
		fmt.Println("  [*] No changes")
	}
}

func processComponent(key string, usage *ServiceUsageState) ([]*IstioRouteRule, error) {
	serviceName, _, _, componentName := ParseServiceUsageKey(key)
	component := usage.Policy.Services[serviceName].getComponentsMap()[componentName]

	labels := usage.ResolvedUsage.ComponentInstanceMap[key].CalculatedLabels

	// todo(slukjanov): temp hack - expecting that cluster is always passed through the label "cluster"
	var cluster *Cluster
	if clusterLabel, ok := labels.Labels["cluster"]; ok {
		if cluster, ok = usage.Policy.Clusters[clusterLabel]; !ok {
			debug.WithFields(log.Fields{
				"component": key,
				"labels":    labels.Labels,
			}).Fatal("Can't find cluster for component (based on label 'cluster')")
		}
	}

	// get all users who're using service
	userIds := usage.ResolvedUsage.ComponentInstanceMap[key].UserIds
	users := make([]*User, 0)
	for _, userID := range userIds {
		// todo check if user doesn't exists
		users = append(users, usage.users.Users[userID])
	}

	if !usage.Policy.Rules.allowsIngressAccess(labels, users, cluster) && component != nil && component.Code != nil {
		codeExecutor, err := component.Code.GetCodeExecutor(key, component.Code.Metadata, usage.getResolvedUsage().ComponentInstanceMap[key].CalculatedCodeParams, usage.Policy.Clusters)
		if err != nil {
			return nil, err
		}

		if helmCodeExecutor, ok := codeExecutor.(HelmCodeExecutor); ok {
			services, err := helmCodeExecutor.httpServices()
			if err != nil {
				return nil, err
			}

			rules := make([]*IstioRouteRule, 0)

			for _, service := range services {
				rules = append(rules, &IstioRouteRule{service, cluster})
			}

			return rules, nil
		}
	}

	return nil, nil
}

// httpServices returns list of services for the current chart
func (exec HelmCodeExecutor) httpServices() ([]string, error) {
	_, clientset, err := exec.Cluster.newKubeClient()
	if err != nil {
		return nil, err
	}

	coreClient := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := k8slabels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}

	// Check all corresponding services
	services, err := coreClient.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	// Check all corresponding Istio ingresses
	ingresses, err := clientset.Extensions().Ingresses(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	if len(ingresses.Items) > 0 {
		result := make([]string, 0)
		for _, service := range services.Items {
			result = append(result, service.Name)
		}

		return result, nil
	}

	return nil, nil
}

func (cluster *Cluster) getIstioRouteRules() []*IstioRouteRule {
	cmd := "get route-rules"
	rulesStr, err := cluster.runIstioCmd(cmd)
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": cluster.Name,
			"cmd": cmd,
			"error": err,
		}).Fatal("Failed to get route-rules by running bash cmd")
	}

	rules := make([]*IstioRouteRule, 0)

	for _, ruleName := range strings.Split(rulesStr, "\n") {
		if ruleName == "" {
			continue
		}
		rules = append(rules, &IstioRouteRule{ruleName, cluster})
	}

	return rules
}

func (rule *IstioRouteRule) create() {
	content := "type: route-rule\n"
	content += "name: " + rule.Service + "\n"
	content += "spec:\n"
	content += "  destination: " + rule.Service + "." + rule.Cluster.Metadata.Namespace + ".svc.cluster.local\n"
	content += "  httpReqTimeout:\n"
	content += "    simpleTimeout:\n"
	content += "      timeout: 1ms\n"

	ruleFile := writeTempFile("istio-rule", content)

	out, err := rule.Cluster.runIstioCmd("create -f " + ruleFile.Name())
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": rule.Cluster.Name,
			"content": content,
			"out": out,
			"error": err,
		}).Fatal("Failed to create istio rule by running bash script")
	}
}

func (rule *IstioRouteRule) delete() {
	out, err := rule.Cluster.runIstioCmd("delete route-rule " + rule.Service)
	if err != nil {
		debug.WithFields(log.Fields{
			"cluster": rule.Cluster.Name,
			"out": out,
			"error": err,
		}).Fatal("Failed to delete istio rule by running bash script")
	}
}

func (cluster *Cluster) runIstioCmd(cmd string) (string, error) {
	istioSvc := cluster.Metadata.istioSvc
	if istioSvc == "" {
		_, clientset, err := cluster.newKubeClient()
		if err != nil {
			return "", err
		}

		coreClient := clientset.Core()

		selector := k8slabels.Set{"app": "istio"}.AsSelector()
		options := api.ListOptions{LabelSelector: selector}

		services, err := coreClient.Services(cluster.Metadata.Namespace).List(options)
		if err != nil {
			return "", err
		}

		for _, service := range services.Items {
			if strings.Contains(service.Name, "pilot") {
				istioSvc = service.Name

				for _, port := range service.Spec.Ports {
					if port.Name == "http-apiserver" {
						istioSvc = fmt.Sprintf("%s:%d", istioSvc, port.Port)
						break
					}
				}

				cluster.Metadata.istioSvc = istioSvc
				break
			}
		}
	}

	content := "set -e\n"
	content += "kubectl config use-context " + cluster.Name + " 1>/dev/null\n"
	content += "istioctl --configAPIService " + cluster.Metadata.istioSvc + " --namespace " + cluster.Metadata.Namespace + " "
	content += cmd + "\n"

	cmdFile := writeTempFile("istioctl-cmd", content)

	out, err := runCmd("bash", cmdFile.Name())
	if err != nil {
		return "", err
	}

	return out, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator 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 spec

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"
)

const (
	defaultVersion = "3.1.4"

	TPRKind        = "cluster"
	TPRKindPlural  = "clusters"
	TPRGroup       = "etcd.coreos.com"
	TPRVersion     = "v1beta1"
	TPRDescription = "Managed etcd clusters"
)

var (
	ErrBackupUnsetRestoreSet = errors.New("spec: backup policy must be set if restore policy is set")
)

func TPRName() string {
	return fmt.Sprintf("%s.%s", TPRKind, TPRGroup)
}

type Cluster struct {
	metav1.TypeMeta `json:",inline"`
	Metadata        metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec            ClusterSpec       `json:"spec"`
	Status          ClusterStatus     `json:"status"`
}

func (c *Cluster) AsOwner() metav1.OwnerReference {
	trueVar := true
	// TODO: In 1.6 this is gonna be "k8s.io/kubernetes/pkg/apis/meta/v1"
	// Both api.OwnerReference and metatypes.OwnerReference are combined into that.
	return metav1.OwnerReference{
		APIVersion: c.APIVersion,
		Kind:       c.Kind,
		Name:       c.Metadata.Name,
		UID:        c.Metadata.UID,
		Controller: &trueVar,
	}
}

type ClusterSpec struct {
	// Size is the expected size of the etcd cluster.
	// The etcd-operator will eventually make the size of the running
	// cluster equal to the expected size.
	// The vaild range of the size is from 1 to 7.
	Size int `json:"size"`

	// Version is the expected version of the etcd cluster.
	// The etcd-operator will eventually make the etcd cluster version
	// equal to the expected version.
	//
	// The version must follow the [semver]( http://semver.org) format, for example "3.1.4".
	// Only etcd released versions are supported: https://github.com/coreos/etcd/releases
	//
	// If version is not set, default is "3.1.4".
	Version string `json:"version"`

	// Paused is to pause the control of the operator for the etcd cluster.
	Paused bool `json:"paused,omitempty"`

	// Pod defines the policy to create pod for the etcd pod.
	Pod *PodPolicy `json:"pod,omitempty"`

	// Backup defines the policy to backup data of etcd cluster if not nil.
	// If backup policy is set but restore policy not, and if a previous backup exists,
	// this cluster would face conflict and fail to start.
	Backup *BackupPolicy `json:"backup,omitempty"`

	// Restore defines the policy to restore cluster form existing backup if not nil.
	// It's not allowed if restore policy is set and backup policy not.
	Restore *RestorePolicy `json:"restore,omitempty"`

	// SelfHosted determines if the etcd cluster is used for a self-hosted
	// Kubernetes cluster.
	SelfHosted *SelfHostedPolicy `json:"selfHosted,omitempty"`

	// NOTE: This field is half finished. It will be ignored.
	// etcd cluster TLS configuration
	TLS *TLSPolicy `json:"TLS,omitempty"`
}

// RestorePolicy defines the policy to restore cluster form existing backup if not nil.
type RestorePolicy struct {
	// BackupClusterName is the cluster name of the backup to recover from.
	BackupClusterName string `json:"backupClusterName"`

	// StorageType specifies the type of storage device to store backup files.
	// If not set, the default is "PersistentVolume".
	StorageType BackupStorageType `json:"storageType"`
}

// PodPolicy defines the policy to create pod for the etcd container.
type PodPolicy struct {
	// Labels specifies the labels to attach to pods the operator creates for the
	// etcd cluster.
	// "app" and "etcd_*" labels are reserved for the internal use of the etcd operator.
	// Do not overwrite them.
	Labels map[string]string `json:"labels,omitempty"`

	// NodeSelector specifies a map of key-value pairs. For the pod to be eligible
	// to run on a node, the node must have each of the indicated key-value pairs as
	// labels.
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`

	// AntiAffinity determines if the etcd-operator tries to avoid putting
	// the etcd members in the same cluster onto the same node.
	AntiAffinity bool `json:"antiAffinity"`

	// Resources is the resource requirements for the etcd container.
	// This field cannot be updated once the cluster is created.
	Resources v1.ResourceRequirements `json:"resources"`

	// Tolerations specifies the pod's tolerations.
	Tolerations []v1.Toleration `json:"tolerations"`

	// List of environment variables to set in the etcd container.
	// This is used to configure etcd process. etcd cluster cannot be created, when
	// bad environement variables are provided. Do not overwrite any flags used to
	// bootstrap the cluster (for example `--initial-cluster` flag).
	// This field cannot be updated.
	EtcdEnv []v1.EnvVar `json:"etcdEnv"`
}

func (c *ClusterSpec) Validate() error {
	if c.Backup == nil && c.Restore != nil {
		return ErrBackupUnsetRestoreSet
	}
	if c.Backup != nil && c.Restore != nil {
		if c.Backup.StorageType != c.Restore.StorageType {
			return errors.New("spec: backup and restore storage types are different")
		}
	}
	if c.Backup != nil {
		if err := c.Backup.Validate(); err != nil {
			return err
		}
	}
	if c.TLS != nil {
		if err := c.TLS.Validate(); err != nil {
			return err
		}
	}

	if c.Pod != nil {
		for k := range c.Pod.Labels {
			if k == "app" || strings.HasPrefix(k, "etcd_") {
				return errors.New("spec: pod labels contains reserved label")
			}
		}
	}
	return nil
}

// Cleanup cleans up user passed spec, e.g. defaulting, transforming fields.
// TODO: move this to admission controller
func (c *ClusterSpec) Cleanup() {
	if len(c.Version) == 0 {
		c.Version = defaultVersion
	}
	c.Version = strings.TrimLeft(c.Version, "v")
}

type ClusterPhase string

const (
	ClusterPhaseNone     ClusterPhase = ""
	ClusterPhaseCreating              = "Creating"
	ClusterPhaseRunning               = "Running"
	ClusterPhaseFailed                = "Failed"
)

type ClusterCondition struct {
	Type ClusterConditionType `json:"type"`

	Reason string `json:"reason"`

	TransitionTime string `json:"transitionTime"`
}

type ClusterConditionType string

const (
	ClusterConditionReady = "Ready"

	ClusterConditionRemovingDeadMember = "RemovingDeadMember"

	ClusterConditionRecovering = "Recovering"

	ClusterConditionScalingUp   = "ScalingUp"
	ClusterConditionScalingDown = "ScalingDown"

	ClusterConditionUpgrading = "Upgrading"
)

type ClusterStatus struct {
	// Phase is the cluster running phase
	Phase  ClusterPhase `json:"phase"`
	Reason string       `json:"reason"`

	// ControlPuased indicates the operator pauses the control of the cluster.
	ControlPaused bool `json:"controlPaused"`

	// Condition keeps ten most recent cluster conditions
	Conditions []ClusterCondition `json:"conditions"`

	// Size is the current size of the cluster
	Size int `json:"size"`
	// Members are the etcd members in the cluster
	Members MembersStatus `json:"members"`
	// CurrentVersion is the current cluster version
	CurrentVersion string `json:"currentVersion"`
	// TargetVersion is the version the cluster upgrading to.
	// If the cluster is not upgrading, TargetVersion is empty.
	TargetVersion string `json:"targetVersion"`

	// BackupServiceStatus is the status of the backup service.
	// BackupServiceStatus only exists when backup is enabled in the
	// cluster spec.
	BackupServiceStatus *BackupServiceStatus `json:"backupServiceStatus,omitempty"`
}

type MembersStatus struct {
	// Ready are the etcd members that are ready to serve requests
	// The member names are the same as the etcd pod names
	Ready []string `json:"ready,omitempty"`
	// Unready are the etcd members not ready to serve requests
	Unready []string `json:"unready,omitempty"`
}

func (cs ClusterStatus) Copy() ClusterStatus {
	newCS := ClusterStatus{}
	b, err := json.Marshal(cs)
	if err != nil {
		panic(err)
	}
	err = json.Unmarshal(b, &newCS)
	if err != nil {
		panic(err)
	}
	return newCS
}

func (cs *ClusterStatus) IsFailed() bool {
	if cs == nil {
		return false
	}
	return cs.Phase == ClusterPhaseFailed
}

func (cs *ClusterStatus) SetPhase(p ClusterPhase) {
	cs.Phase = p
}

func (cs *ClusterStatus) PauseControl() {
	cs.ControlPaused = true
}

func (cs *ClusterStatus) Control() {
	cs.ControlPaused = false
}

func (cs *ClusterStatus) UpgradeVersionTo(v string) {
	cs.TargetVersion = v
}

func (cs *ClusterStatus) SetVersion(v string) {
	cs.TargetVersion = ""
	cs.CurrentVersion = v
}

func (cs *ClusterStatus) SetReason(r string) {
	cs.Reason = r
}

func (cs *ClusterStatus) AppendScalingUpCondition(from, to int) {
	c := ClusterCondition{
		Type:           ClusterConditionScalingUp,
		Reason:         scalingReason(from, to),
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendScalingDownCondition(from, to int) {
	c := ClusterCondition{
		Type:           ClusterConditionScalingDown,
		Reason:         scalingReason(from, to),
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendRecoveringCondition() {
	c := ClusterCondition{
		Type:           ClusterConditionRecovering,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendUpgradingCondition(to string, member string) {
	reason := fmt.Sprintf("upgrading cluster member %s version to %v", member, to)

	c := ClusterCondition{
		Type:           ClusterConditionUpgrading,
		Reason:         reason,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendRemovingDeadMember(name string) {
	reason := fmt.Sprintf("removing dead member %s", name)

	c := ClusterCondition{
		Type:           ClusterConditionRemovingDeadMember,
		Reason:         reason,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) SetReadyCondition() {
	c := ClusterCondition{
		Type:           ClusterConditionReady,
		TransitionTime: time.Now().Format(time.RFC3339),
	}

	if len(cs.Conditions) == 0 {
		cs.appendCondition(c)
		return
	}

	lastc := cs.Conditions[len(cs.Conditions)-1]
	if lastc.Type == ClusterConditionReady {
		return
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) appendCondition(c ClusterCondition) {
	cs.Conditions = append(cs.Conditions, c)
	if len(cs.Conditions) > 10 {
		cs.Conditions = cs.Conditions[1:]
	}
}

func scalingReason(from, to int) string {
	return fmt.Sprintf("Current cluster size: %d, desired cluster size: %d", from, to)
}
<commit_msg>spec: add doc for field updates<commit_after>// Copyright 2016 The etcd-operator 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 spec

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/api/v1"
)

const (
	defaultVersion = "3.1.4"

	TPRKind        = "cluster"
	TPRKindPlural  = "clusters"
	TPRGroup       = "etcd.coreos.com"
	TPRVersion     = "v1beta1"
	TPRDescription = "Managed etcd clusters"
)

var (
	ErrBackupUnsetRestoreSet = errors.New("spec: backup policy must be set if restore policy is set")
)

func TPRName() string {
	return fmt.Sprintf("%s.%s", TPRKind, TPRGroup)
}

type Cluster struct {
	metav1.TypeMeta `json:",inline"`
	Metadata        metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec            ClusterSpec       `json:"spec"`
	Status          ClusterStatus     `json:"status"`
}

func (c *Cluster) AsOwner() metav1.OwnerReference {
	trueVar := true
	// TODO: In 1.6 this is gonna be "k8s.io/kubernetes/pkg/apis/meta/v1"
	// Both api.OwnerReference and metatypes.OwnerReference are combined into that.
	return metav1.OwnerReference{
		APIVersion: c.APIVersion,
		Kind:       c.Kind,
		Name:       c.Metadata.Name,
		UID:        c.Metadata.UID,
		Controller: &trueVar,
	}
}

type ClusterSpec struct {
	// Size is the expected size of the etcd cluster.
	// The etcd-operator will eventually make the size of the running
	// cluster equal to the expected size.
	// The vaild range of the size is from 1 to 7.
	Size int `json:"size"`

	// Version is the expected version of the etcd cluster.
	// The etcd-operator will eventually make the etcd cluster version
	// equal to the expected version.
	//
	// The version must follow the [semver]( http://semver.org) format, for example "3.1.4".
	// Only etcd released versions are supported: https://github.com/coreos/etcd/releases
	//
	// If version is not set, default is "3.1.4".
	Version string `json:"version"`

	// Paused is to pause the control of the operator for the etcd cluster.
	Paused bool `json:"paused,omitempty"`

	// Pod defines the policy to create pod for the etcd pod.
	//
	// Updating Pod does not take effect on any existing etcd pods.
	Pod *PodPolicy `json:"pod,omitempty"`

	// Backup defines the policy to backup data of etcd cluster if not nil.
	// If backup policy is set but restore policy not, and if a previous backup exists,
	// this cluster would face conflict and fail to start.
	Backup *BackupPolicy `json:"backup,omitempty"`

	// Restore defines the policy to restore cluster form existing backup if not nil.
	// It's not allowed if restore policy is set and backup policy not.
	//
	// Restore is a cluster initialization configuration. It cannot be updated.
	Restore *RestorePolicy `json:"restore,omitempty"`

	// SelfHosted determines if the etcd cluster is used for a self-hosted
	// Kubernetes cluster.
	//
	// SelfHosted is a cluster initialization configuration. It cannot be updated.
	SelfHosted *SelfHostedPolicy `json:"selfHosted,omitempty"`

	// NOTE: This field is half finished. It will be ignored.
	// etcd cluster TLS configuration
	TLS *TLSPolicy `json:"TLS,omitempty"`
}

// RestorePolicy defines the policy to restore cluster form existing backup if not nil.
type RestorePolicy struct {
	// BackupClusterName is the cluster name of the backup to recover from.
	BackupClusterName string `json:"backupClusterName"`

	// StorageType specifies the type of storage device to store backup files.
	// If not set, the default is "PersistentVolume".
	StorageType BackupStorageType `json:"storageType"`
}

// PodPolicy defines the policy to create pod for the etcd container.
type PodPolicy struct {
	// Labels specifies the labels to attach to pods the operator creates for the
	// etcd cluster.
	// "app" and "etcd_*" labels are reserved for the internal use of the etcd operator.
	// Do not overwrite them.
	Labels map[string]string `json:"labels,omitempty"`

	// NodeSelector specifies a map of key-value pairs. For the pod to be eligible
	// to run on a node, the node must have each of the indicated key-value pairs as
	// labels.
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`

	// AntiAffinity determines if the etcd-operator tries to avoid putting
	// the etcd members in the same cluster onto the same node.
	AntiAffinity bool `json:"antiAffinity"`

	// Resources is the resource requirements for the etcd container.
	// This field cannot be updated once the cluster is created.
	Resources v1.ResourceRequirements `json:"resources"`

	// Tolerations specifies the pod's tolerations.
	Tolerations []v1.Toleration `json:"tolerations"`

	// List of environment variables to set in the etcd container.
	// This is used to configure etcd process. etcd cluster cannot be created, when
	// bad environement variables are provided. Do not overwrite any flags used to
	// bootstrap the cluster (for example `--initial-cluster` flag).
	// This field cannot be updated.
	EtcdEnv []v1.EnvVar `json:"etcdEnv"`
}

func (c *ClusterSpec) Validate() error {
	if c.Backup == nil && c.Restore != nil {
		return ErrBackupUnsetRestoreSet
	}
	if c.Backup != nil && c.Restore != nil {
		if c.Backup.StorageType != c.Restore.StorageType {
			return errors.New("spec: backup and restore storage types are different")
		}
	}
	if c.Backup != nil {
		if err := c.Backup.Validate(); err != nil {
			return err
		}
	}
	if c.TLS != nil {
		if err := c.TLS.Validate(); err != nil {
			return err
		}
	}

	if c.Pod != nil {
		for k := range c.Pod.Labels {
			if k == "app" || strings.HasPrefix(k, "etcd_") {
				return errors.New("spec: pod labels contains reserved label")
			}
		}
	}
	return nil
}

// Cleanup cleans up user passed spec, e.g. defaulting, transforming fields.
// TODO: move this to admission controller
func (c *ClusterSpec) Cleanup() {
	if len(c.Version) == 0 {
		c.Version = defaultVersion
	}
	c.Version = strings.TrimLeft(c.Version, "v")
}

type ClusterPhase string

const (
	ClusterPhaseNone     ClusterPhase = ""
	ClusterPhaseCreating              = "Creating"
	ClusterPhaseRunning               = "Running"
	ClusterPhaseFailed                = "Failed"
)

type ClusterCondition struct {
	Type ClusterConditionType `json:"type"`

	Reason string `json:"reason"`

	TransitionTime string `json:"transitionTime"`
}

type ClusterConditionType string

const (
	ClusterConditionReady = "Ready"

	ClusterConditionRemovingDeadMember = "RemovingDeadMember"

	ClusterConditionRecovering = "Recovering"

	ClusterConditionScalingUp   = "ScalingUp"
	ClusterConditionScalingDown = "ScalingDown"

	ClusterConditionUpgrading = "Upgrading"
)

type ClusterStatus struct {
	// Phase is the cluster running phase
	Phase  ClusterPhase `json:"phase"`
	Reason string       `json:"reason"`

	// ControlPuased indicates the operator pauses the control of the cluster.
	ControlPaused bool `json:"controlPaused"`

	// Condition keeps ten most recent cluster conditions
	Conditions []ClusterCondition `json:"conditions"`

	// Size is the current size of the cluster
	Size int `json:"size"`
	// Members are the etcd members in the cluster
	Members MembersStatus `json:"members"`
	// CurrentVersion is the current cluster version
	CurrentVersion string `json:"currentVersion"`
	// TargetVersion is the version the cluster upgrading to.
	// If the cluster is not upgrading, TargetVersion is empty.
	TargetVersion string `json:"targetVersion"`

	// BackupServiceStatus is the status of the backup service.
	// BackupServiceStatus only exists when backup is enabled in the
	// cluster spec.
	BackupServiceStatus *BackupServiceStatus `json:"backupServiceStatus,omitempty"`
}

type MembersStatus struct {
	// Ready are the etcd members that are ready to serve requests
	// The member names are the same as the etcd pod names
	Ready []string `json:"ready,omitempty"`
	// Unready are the etcd members not ready to serve requests
	Unready []string `json:"unready,omitempty"`
}

func (cs ClusterStatus) Copy() ClusterStatus {
	newCS := ClusterStatus{}
	b, err := json.Marshal(cs)
	if err != nil {
		panic(err)
	}
	err = json.Unmarshal(b, &newCS)
	if err != nil {
		panic(err)
	}
	return newCS
}

func (cs *ClusterStatus) IsFailed() bool {
	if cs == nil {
		return false
	}
	return cs.Phase == ClusterPhaseFailed
}

func (cs *ClusterStatus) SetPhase(p ClusterPhase) {
	cs.Phase = p
}

func (cs *ClusterStatus) PauseControl() {
	cs.ControlPaused = true
}

func (cs *ClusterStatus) Control() {
	cs.ControlPaused = false
}

func (cs *ClusterStatus) UpgradeVersionTo(v string) {
	cs.TargetVersion = v
}

func (cs *ClusterStatus) SetVersion(v string) {
	cs.TargetVersion = ""
	cs.CurrentVersion = v
}

func (cs *ClusterStatus) SetReason(r string) {
	cs.Reason = r
}

func (cs *ClusterStatus) AppendScalingUpCondition(from, to int) {
	c := ClusterCondition{
		Type:           ClusterConditionScalingUp,
		Reason:         scalingReason(from, to),
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendScalingDownCondition(from, to int) {
	c := ClusterCondition{
		Type:           ClusterConditionScalingDown,
		Reason:         scalingReason(from, to),
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendRecoveringCondition() {
	c := ClusterCondition{
		Type:           ClusterConditionRecovering,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendUpgradingCondition(to string, member string) {
	reason := fmt.Sprintf("upgrading cluster member %s version to %v", member, to)

	c := ClusterCondition{
		Type:           ClusterConditionUpgrading,
		Reason:         reason,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) AppendRemovingDeadMember(name string) {
	reason := fmt.Sprintf("removing dead member %s", name)

	c := ClusterCondition{
		Type:           ClusterConditionRemovingDeadMember,
		Reason:         reason,
		TransitionTime: time.Now().Format(time.RFC3339),
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) SetReadyCondition() {
	c := ClusterCondition{
		Type:           ClusterConditionReady,
		TransitionTime: time.Now().Format(time.RFC3339),
	}

	if len(cs.Conditions) == 0 {
		cs.appendCondition(c)
		return
	}

	lastc := cs.Conditions[len(cs.Conditions)-1]
	if lastc.Type == ClusterConditionReady {
		return
	}
	cs.appendCondition(c)
}

func (cs *ClusterStatus) appendCondition(c ClusterCondition) {
	cs.Conditions = append(cs.Conditions, c)
	if len(cs.Conditions) > 10 {
		cs.Conditions = cs.Conditions[1:]
	}
}

func scalingReason(from, to int) string {
	return fmt.Sprintf("Current cluster size: %d, desired cluster size: %d", from, to)
}
<|endoftext|>