<commit_before>package app

import (
	"errors"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/bind"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/config"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	"github.com/timeredbull/tsuru/repository"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"os/exec"
	"path"
	"strings"
	"time"
)

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

type authorizer interface {
	authorize(*App) error
	setCreds(access string, secret string)
}

type EnvVar bind.EnvVar

func (e *EnvVar) String() string {
	var value, suffix string
	if e.Public {
		value = e.Value
	} else {
		value = "***"
		suffix = " (private variable)"
	}
	return fmt.Sprintf("%s=%s%s", e.Name, value, suffix)
}

type App struct {
	Env         map[string]EnvVar
	Framework   string
	JujuEnv     string
	KeystoneEnv keystoneEnv
	Logs        []Log
	Name        string
	State       string
	Units       []Unit
	Teams       []string
	ec2Auth     authorizer
}

type Log struct {
	Date    time.Time
	Message string
}

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

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

// createApp creates a new app.
//
// Creating a new app is a big process that can be divided in some steps (and
// two scenarios):
//
//   Scenario 1: Multi tenancy enabled
//
//       1. Creates keystone credentials for the app
//       2. Write the juju environment to juju's environments file
//       3. Bootstrap juju environment
//       4. Authorizes ssh and http access to the app instance
//       5. Saves the app in the database
//       6. Deploys juju charm
//
//   Scenario 2: Multi tenancy disabled
//
//       1. Sets app juju env to the default juju env (defined in the
//          tsuru.conf file)
//       2. Saves the app in the database
//       3. Deploys juju charm
//
// Multi tenancy should be configured in tsuru's conf file
// (set the "multi-tenant" flag to true or false, as desired).
func createApp(a *App) error {
	var err error
	isMultiTenant, err := config.GetBool("multi-tenant")
	if err != nil {
		return err
	}
	if isMultiTenant {
		a.KeystoneEnv, err = newKeystoneEnv(a.Name)
		if err != nil {
			return err
		}
		err = newEnviron(a)
		if err != nil {
			return err
		}
		if a.JujuEnv == "" {
			a.JujuEnv = a.Name
		}
		cmd := exec.Command("juju", "bootstrap", "-e", a.JujuEnv)
		log.Printf("bootstraping juju environment %s for the app %s", a.JujuEnv, a.Name)
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Printf("failed to bootstrap juju environment %s:\n%s", a.JujuEnv, out)
			return fmt.Errorf("Failed to bootstrap juju env (%s): %s", err, out)
		}
		authorizer := a.authorizer()
		authorizer.setCreds(a.KeystoneEnv.AccessKey, a.KeystoneEnv.secretKey)
		err = authorizer.authorize(a)
		if err != nil {
			return fmt.Errorf("Failed to create the app, it was not possible to authorize the access to the app: %s", err)
		}
	} else {
		a.JujuEnv, err = config.GetString("juju:default-env")
		if err != nil {
			return err
		}
	}
	a.State = "pending"
	err = db.Session.Apps().Insert(a)
	if err != nil {
		return err
	}
	a.Log(fmt.Sprintf("creating app %s", a.Name))
	cmd := exec.Command("juju", "deploy", "-e", a.JujuEnv, "--repository=/home/charms", "local:"+a.Framework, a.Name)
	log.Printf("deploying %s with name %s on environment %s", a.Framework, a.Name, a.JujuEnv)
	out, err := cmd.CombinedOutput()
	a.Log(string(out))
	if err != nil {
		a.Log(fmt.Sprintf("juju finished with exit status: %s", err.Error()))
		db.Session.Apps().Remove(bson.M{"name": a.Name})
		return errors.New(string(out))
	}
	a.Log(fmt.Sprintf("app %s successfully created", a.Name))
	return nil
}

func (a *App) unbind() error {
	var instances []service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"apps": bson.M{"$in": []string{a.Name}}}).All(&instances)
	if err != nil {
		return err
	}
	var msg string
	var addMsg = func(instanceName string, reason error) {
		if msg == "" {
			msg = "Failed to unbind the following instances:\n"
		}
		msg += fmt.Sprintf("- %s (%s)", instanceName, reason.Error())
	}
	for _, instance := range instances {
		err = instance.Unbind(a)
		if err != nil {
			addMsg(instance.Name, err)
		}
	}
	if msg != "" {
		return errors.New(msg)
	}
	return nil
}

func (a *App) destroy() error {
	multitenant, err := config.GetBool("multi-tenant")
	if err != nil {
		return err
	}
	app := App{Name: a.Name}
	err = app.Get()
	if err != nil {
		return err
	}
	if multitenant {
		if out, err := exec.Command("juju", "destroy-environment", "-e", app.JujuEnv).CombinedOutput(); err != nil {
			msg := fmt.Sprintf("Failed to destroy juju-environment:\n%s", out)
			log.Print(msg)
			return errors.New(string(out))
		}
		if err = destroyKeystoneEnv(&app.KeystoneEnv); err != nil {
			return err
		}
	} else {
		out, err := app.unit().destroy()
		msg := string(out)
		log.Printf(msg)
		if err != nil {
			return errors.New(msg)
		}
	}
	unbindCh := make(chan error)
	go func() {
		unbindCh <- app.unbind()
	}()
	err = db.Session.Apps().Remove(bson.M{"name": app.Name})<commit_msg>api/app: provide "y" (via stdin) to juju's destroy-environment<commit_after>package app

import (
	"errors"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/bind"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/config"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	"github.com/timeredbull/tsuru/repository"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"os/exec"
	"path"
	"strings"
	"time"
)

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

type authorizer interface {
	authorize(*App) error
	setCreds(access string, secret string)
}

type EnvVar bind.EnvVar

func (e *EnvVar) String() string {
	var value, suffix string
	if e.Public {
		value = e.Value
	} else {
		value = "***"
		suffix = " (private variable)"
	}
	return fmt.Sprintf("%s=%s%s", e.Name, value, suffix)
}

type App struct {
	Env         map[string]EnvVar
	Framework   string
	JujuEnv     string
	KeystoneEnv keystoneEnv
	Logs        []Log
	Name        string
	State       string
	Units       []Unit
	Teams       []string
	ec2Auth     authorizer
}

type Log struct {
	Date    time.Time
	Message string
}

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

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

// createApp creates a new app.
//
// Creating a new app is a big process that can be divided in some steps (and
// two scenarios):
//
//   Scenario 1: Multi tenancy enabled
//
//       1. Creates keystone credentials for the app
//       2. Write the juju environment to juju's environments file
//       3. Bootstrap juju environment
//       4. Authorizes ssh and http access to the app instance
//       5. Saves the app in the database
//       6. Deploys juju charm
//
//   Scenario 2: Multi tenancy disabled
//
//       1. Sets app juju env to the default juju env (defined in the
//          tsuru.conf file)
//       2. Saves the app in the database
//       3. Deploys juju charm
//
// Multi tenancy should be configured in tsuru's conf file
// (set the "multi-tenant" flag to true or false, as desired).
func createApp(a *App) error {
	var err error
	isMultiTenant, err := config.GetBool("multi-tenant")
	if err != nil {
		return err
	}
	if isMultiTenant {
		a.KeystoneEnv, err = newKeystoneEnv(a.Name)
		if err != nil {
			return err
		}
		err = newEnviron(a)
		if err != nil {
			return err
		}
		if a.JujuEnv == "" {
			a.JujuEnv = a.Name
		}
		cmd := exec.Command("juju", "bootstrap", "-e", a.JujuEnv)
		log.Printf("bootstraping juju environment %s for the app %s", a.JujuEnv, a.Name)
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Printf("failed to bootstrap juju environment %s:\n%s", a.JujuEnv, out)
			return fmt.Errorf("Failed to bootstrap juju env (%s): %s", err, out)
		}
		authorizer := a.authorizer()
		authorizer.setCreds(a.KeystoneEnv.AccessKey, a.KeystoneEnv.secretKey)
		err = authorizer.authorize(a)
		if err != nil {
			return fmt.Errorf("Failed to create the app, it was not possible to authorize the access to the app: %s", err)
		}
	} else {
		a.JujuEnv, err = config.GetString("juju:default-env")
		if err != nil {
			return err
		}
	}
	a.State = "pending"
	err = db.Session.Apps().Insert(a)
	if err != nil {
		return err
	}
	a.Log(fmt.Sprintf("creating app %s", a.Name))
	cmd := exec.Command("juju", "deploy", "-e", a.JujuEnv, "--repository=/home/charms", "local:"+a.Framework, a.Name)
	log.Printf("deploying %s with name %s on environment %s", a.Framework, a.Name, a.JujuEnv)
	out, err := cmd.CombinedOutput()
	a.Log(string(out))
	if err != nil {
		a.Log(fmt.Sprintf("juju finished with exit status: %s", err.Error()))
		db.Session.Apps().Remove(bson.M{"name": a.Name})
		return errors.New(string(out))
	}
	a.Log(fmt.Sprintf("app %s successfully created", a.Name))
	return nil
}

func (a *App) unbind() error {
	var instances []service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"apps": bson.M{"$in": []string{a.Name}}}).All(&instances)
	if err != nil {
		return err
	}
	var msg string
	var addMsg = func(instanceName string, reason error) {
		if msg == "" {
			msg = "Failed to unbind the following instances:\n"
		}
		msg += fmt.Sprintf("- %s (%s)", instanceName, reason.Error())
	}
	for _, instance := range instances {
		err = instance.Unbind(a)
		if err != nil {
			addMsg(instance.Name, err)
		}
	}
	if msg != "" {
		return errors.New(msg)
	}
	return nil
}

func (a *App) destroy() error {
	multitenant, err := config.GetBool("multi-tenant")
	if err != nil {
		return err
	}
	app := App{Name: a.Name}
	err = app.Get()
	if err != nil {
		return err
	}
	if multitenant {
		destroyCmd := exec.Command("juju", "destroy-environment", "-e", app.JujuEnv)
		destroyCmd.Stdin = strings.NewReader("y")
		if out, err := destroyCmd.CombinedOutput(); err != nil {
			msg := fmt.Sprintf("Failed to destroy juju-environment:\n%s", out)
			log.Print(msg)
			return errors.New(string(out))
		}
		if err = destroyKeystoneEnv(&app.KeystoneEnv); err != nil {
			return err
		}
	} else {
		out, err := app.unit().destroy()
		msg := string(out)
		log.Printf(msg)
		if err != nil {
			return errors.New(msg)
		}
	}
	unbindCh := make(chan error)
	go func() {
		unbindCh <- app.unbind()
	}()
	err = db.Session.Apps().Remove(bson.M{"name": app.Name})
{'fraction_non_alphanumeric': 0.15198863636363635, 'fraction_numerical': 0.002462121212121212, 'mean_word_length': 3.207569721115538, 'pattern_counts': {'":': 10, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2430651', 'n_tokens_mistral': 4066, 'n_tokens_neox': 3830, 'n_words': 1050}