<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/litl/galaxy/log"
	"github.com/litl/galaxy/registry"
	"github.com/litl/galaxy/runtime"
	"github.com/litl/galaxy/utils"
)

var (
	stopCutoff      int64
	app             string
	redisHost       string
	env             string
	pool            string
	loop            bool
	shuttleHost     string
	statsdHost      string
	debug           bool
	runOnce         bool
	version         bool
	buildVersion    string
	serviceConfigs  []*registry.ServiceConfig
	serviceRegistry *registry.ServiceRegistry
	serviceRuntime  *runtime.ServiceRuntime
	workerChans     map[string]chan string
	wg              sync.WaitGroup
)

func initOrDie() {

	serviceRegistry = registry.NewServiceRegistry(
		env,
		pool,
		"",
		registry.DefaultTTL,
		"",
	)

	serviceRegistry.Connect(redisHost)
	serviceRuntime = runtime.NewServiceRuntime(serviceRegistry, shuttleHost, statsdHost)

	apps, err := serviceRegistry.ListAssignments(pool)
	if err != nil {
		log.Fatalf("ERROR: Could not retrieve service configs for /%s/%s: %s\n", env, pool, err)
	}

	workerChans = make(map[string]chan string)
	for _, app := range apps {
		serviceConfig, err := serviceRegistry.GetServiceConfig(app)
		if err != nil {
			log.Fatalf("ERROR: Could not retrieve service config for /%s/%s: %s\n", env, pool, err)
		}

		workerChans[serviceConfig.Name] = make(chan string)
	}
}

func pullAllImages() error {
	serviceConfigs, err := serviceRegistry.ListApps()
	if err != nil {
		log.Errorf("ERROR: Could not retrieve service configs for /%s/%s: %s\n", env, pool, err)
		return err
	}

	if len(serviceConfigs) == 0 {
		log.Printf("No services configured for /%s/%s\n", env, pool)
		return err
	}

	errChan := make(chan error)
	for _, serviceConfig := range serviceConfigs {
		go func(serviceConfig registry.ServiceConfig, errChan chan error) {
			// err logged via pullImage
			_, err := pullImage(&serviceConfig)
			if err != nil {
				errChan <- err
				return
			}
			errChan <- nil

		}(serviceConfig, errChan)
	}

	for i := 0; i < len(serviceConfigs); i++ {
		err := <-errChan
		if err != nil {
			// return the first error we got to signal that one of the pulls failed
			return err
		}
	}
	return nil
}

func pullImage(serviceConfig *registry.ServiceConfig) (*docker.Image, error) {
	log.Printf("Pulling %s version %s\n", serviceConfig.Name, serviceConfig.Version())
	image, err := serviceRuntime.PullImage(serviceConfig.Version(), true)
	if image == nil || err != nil {
		log.Errorf("ERROR: Could not pull image %s: %s\n",
			serviceConfig.Version(), err)
		return nil, err
	}
	log.Printf("Pulled %s\n", serviceConfig.Version())
	return image, nil
}

func startService(serviceConfig *registry.ServiceConfig, logStatus bool) {
	started, container, err := serviceRuntime.StartIfNotRunning(serviceConfig)
	if err != nil {
		log.Errorf("ERROR: Could not start containers: %s\n", err)
		return
	}

	if started {
		log.Printf("Started %s version %s as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
	}

	if logStatus && !debug {
		log.Printf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
	}

	log.Debugf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])

	err = serviceRuntime.StopAllButLatestService(serviceConfig, stopCutoff)
	if err != nil {
		log.Errorf("ERROR: Could not stop containers: %s\n", err)
	}
}

func appAssigned(app string) (bool, error) {
	assignments, err := serviceRegistry.ListAssignments(pool)
	if err != nil {
		return false, err
	}

	if !utils.StringInSlice(app, assignments) {
		return false, nil
	}
	return true, nil
}

func restartContainers(app string, cmdChan chan string) {
	defer wg.Done()
	logOnce := true

	ticker := time.NewTicker(10 * time.Second)

	for {

		select {

		case cmd := <-cmdChan:

			assigned, err := appAssigned(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving assignments for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if !assigned {
				continue
			}

			serviceConfig, err := serviceRegistry.GetServiceConfig(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if serviceConfig.Version() == "" {
				if !loop {
					return
				}
				continue
			}

			if cmd == "deploy" {
				_, err = pullImage(serviceConfig)
				if err != nil {
					if !loop {
						return
					}

					// if we can't pull the image, leave whatever is running alone
					continue
				}

			}

			if cmd == "restart" {
				err := serviceRuntime.Stop(serviceConfig)
				if err != nil {
					log.Errorf("ERROR: Could not stop %s: %s\n",
						serviceConfig.Version(), err)
					if !loop {
						return
					}

					continue
				}
			}

			startService(serviceConfig, logOnce)
			logOnce = false
		case <-ticker.C:

			serviceConfig, err := serviceRegistry.GetServiceConfig(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				continue
			}

			assigned, err := appAssigned(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if serviceConfig == nil || !assigned {
				log.Errorf("%s no longer exists.  Stopping worker.", app)
				serviceRuntime.StopAllMatching(app)
				delete(workerChans, app)
				return
			}

			if serviceConfig.Version() == "" {
				continue
			}

			started, container, err := serviceRuntime.StartIfNotRunning(serviceConfig)
			if err != nil {
				log.Errorf("ERROR: Could not start containers: %s\n", err)
				continue
			}

			if started {
				log.Printf("Started %s version %s as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
			}

			log.Debugf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])

			err = serviceRuntime.StopAllButLatestService(serviceConfig, stopCutoff)
			if err != nil {
				log.Errorf("ERROR: Could not stop containers: %s\n", err)
			}
		}

		if !loop {
			return
		}

	}
}

func monitorService(changedConfigs chan *registry.ConfigChange) {

	for {

		var changedConfig *registry.ConfigChange
		select {

		case changedConfig = <-changedConfigs:

			if changedConfig.Error != nil {
				log.Errorf("ERROR: Error watching changes: %s\n", changedConfig.Error)
				continue
			}

			if changedConfig.ServiceConfig == nil {
				continue
			}

			assigned, err := appAssigned(changedConfig.ServiceConfig.Name)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", changedConfig.ServiceConfig.Name, err)
				if !loop {
					return
				}
				continue
			}

			if !assigned {
				continue
			}

			ch, ok := workerChans[changedConfig.ServiceConfig.Name]
			if !ok {
				name := changedConfig.ServiceConfig.Name
				ch := make(chan string)
				workerChans[name] = ch
				wg.Add(1)
				go restartContainers(name, ch)
				ch <- "deploy"

				log.Printf("Started new worker for %s\n", name)
				continue
			}

			if changedConfig.Restart {
				log.Printf("Restarting %s", changedConfig.ServiceConfig.Name)
				ch <- "restart"
			} else {
				ch <- "deploy"
			}
		}
	}

}

func main() {
	flag.Int64Var(&stopCutoff, "cutoff", 10, "Seconds to wait before stopping old containers")
	flag.StringVar(&app, "app", "", "App to start")
	flag.StringVar(&redisHost, "redis", utils.GetEnv("GALAXY_REDIS_HOST", utils.DefaultRedisHost), "redis host")
	flag.StringVar(&env, "env", utils.GetEnv("GALAXY_ENV", ""), "Environment namespace")
	flag.StringVar(&pool, "pool", utils.GetEnv("GALAXY_POOL", ""), "Pool namespace")
	flag.BoolVar(&loop, "loop", false, "Run continously")
	flag.StringVar(&shuttleHost, "shuttleAddr", "", "IP where containers can reach shuttle proxy. Defaults to docker0 IP.")
	flag.StringVar(&statsdHost, "statsdAddr", utils.GetEnv("GALAXY_STATSD_HOST", ""), "IP where containers can reach a statsd service. Defaults to docker0 IP:8125.")
	flag.BoolVar(&debug, "debug", false, "verbose logging")
	flag.BoolVar(&version, "v", false, "display version info")

	flag.Parse()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if strings.TrimSpace(env) == "" {
		fmt.Println("Need an env")
		flag.PrintDefaults()
		os.Exit(1)
	}

	if strings.TrimSpace(pool) == "" {
		fmt.Println("Need a pool")
		flag.PrintDefaults()
		os.Exit(1)
	}

	if debug {
		log.DefaultLogger.Level = log.DEBUG
	}

	initOrDie()
	serviceRegistry.CreatePool(pool)

	for app, ch := range workerChans {
		wg.Add(1)
		go restartContainers(app, ch)
		ch <- "deploy"
	}

	if loop {
		cancelChan := make(chan struct{})
		// do we need to cancel ever?

		restartChan := serviceRegistry.Watch(cancelChan)
		monitorService(restartChan)
	}

	wg.Wait()
}
<commit_msg>Convert anonymous func to func<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/litl/galaxy/log"
	"github.com/litl/galaxy/registry"
	"github.com/litl/galaxy/runtime"
	"github.com/litl/galaxy/utils"
)

var (
	stopCutoff      int64
	app             string
	redisHost       string
	env             string
	pool            string
	loop            bool
	shuttleHost     string
	statsdHost      string
	debug           bool
	runOnce         bool
	version         bool
	buildVersion    string
	serviceConfigs  []*registry.ServiceConfig
	serviceRegistry *registry.ServiceRegistry
	serviceRuntime  *runtime.ServiceRuntime
	workerChans     map[string]chan string
	wg              sync.WaitGroup
)

func initOrDie() {

	serviceRegistry = registry.NewServiceRegistry(
		env,
		pool,
		"",
		registry.DefaultTTL,
		"",
	)

	serviceRegistry.Connect(redisHost)
	serviceRuntime = runtime.NewServiceRuntime(serviceRegistry, shuttleHost, statsdHost)

	apps, err := serviceRegistry.ListAssignments(pool)
	if err != nil {
		log.Fatalf("ERROR: Could not retrieve service configs for /%s/%s: %s\n", env, pool, err)
	}

	workerChans = make(map[string]chan string)
	for _, app := range apps {
		serviceConfig, err := serviceRegistry.GetServiceConfig(app)
		if err != nil {
			log.Fatalf("ERROR: Could not retrieve service config for /%s/%s: %s\n", env, pool, err)
		}

		workerChans[serviceConfig.Name] = make(chan string)
	}
}

func pullImageAsync(serviceConfig registry.ServiceConfig, errChan chan error) {
	// err logged via pullImage
	_, err := pullImage(&serviceConfig)
	if err != nil {
		errChan <- err
		return
	}
	errChan <- nil
}

func pullAllImages() error {
	serviceConfigs, err := serviceRegistry.ListApps()
	if err != nil {
		log.Errorf("ERROR: Could not retrieve service configs for /%s/%s: %s\n", env, pool, err)
		return err
	}

	if len(serviceConfigs) == 0 {
		log.Printf("No services configured for /%s/%s\n", env, pool)
		return err
	}

	errChan := make(chan error)
	for _, serviceConfig := range serviceConfigs {
		go pullImageAsync(serviceConfig, errChan)
	}

	for i := 0; i < len(serviceConfigs); i++ {
		err := <-errChan
		if err != nil {
			// return the first error we got to signal that one of the pulls failed
			return err
		}
	}
	return nil
}

func pullImage(serviceConfig *registry.ServiceConfig) (*docker.Image, error) {
	log.Printf("Pulling %s version %s\n", serviceConfig.Name, serviceConfig.Version())
	image, err := serviceRuntime.PullImage(serviceConfig.Version(), true)
	if image == nil || err != nil {
		log.Errorf("ERROR: Could not pull image %s: %s\n",
			serviceConfig.Version(), err)
		return nil, err
	}
	log.Printf("Pulled %s\n", serviceConfig.Version())
	return image, nil
}

func startService(serviceConfig *registry.ServiceConfig, logStatus bool) {
	started, container, err := serviceRuntime.StartIfNotRunning(serviceConfig)
	if err != nil {
		log.Errorf("ERROR: Could not start containers: %s\n", err)
		return
	}

	if started {
		log.Printf("Started %s version %s as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
	}

	if logStatus && !debug {
		log.Printf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
	}

	log.Debugf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])

	err = serviceRuntime.StopAllButLatestService(serviceConfig, stopCutoff)
	if err != nil {
		log.Errorf("ERROR: Could not stop containers: %s\n", err)
	}
}

func appAssigned(app string) (bool, error) {
	assignments, err := serviceRegistry.ListAssignments(pool)
	if err != nil {
		return false, err
	}

	if !utils.StringInSlice(app, assignments) {
		return false, nil
	}
	return true, nil
}

func restartContainers(app string, cmdChan chan string) {
	defer wg.Done()
	logOnce := true

	ticker := time.NewTicker(10 * time.Second)

	for {

		select {

		case cmd := <-cmdChan:

			assigned, err := appAssigned(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving assignments for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if !assigned {
				continue
			}

			serviceConfig, err := serviceRegistry.GetServiceConfig(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if serviceConfig.Version() == "" {
				if !loop {
					return
				}
				continue
			}

			if cmd == "deploy" {
				_, err = pullImage(serviceConfig)
				if err != nil {
					if !loop {
						return
					}

					// if we can't pull the image, leave whatever is running alone
					continue
				}

			}

			if cmd == "restart" {
				err := serviceRuntime.Stop(serviceConfig)
				if err != nil {
					log.Errorf("ERROR: Could not stop %s: %s\n",
						serviceConfig.Version(), err)
					if !loop {
						return
					}

					continue
				}
			}

			startService(serviceConfig, logOnce)
			logOnce = false
		case <-ticker.C:

			serviceConfig, err := serviceRegistry.GetServiceConfig(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				continue
			}

			assigned, err := appAssigned(app)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", app, err)
				if !loop {
					return
				}

				continue
			}

			if serviceConfig == nil || !assigned {
				log.Errorf("%s no longer exists.  Stopping worker.", app)
				serviceRuntime.StopAllMatching(app)
				delete(workerChans, app)
				return
			}

			if serviceConfig.Version() == "" {
				continue
			}

			started, container, err := serviceRuntime.StartIfNotRunning(serviceConfig)
			if err != nil {
				log.Errorf("ERROR: Could not start containers: %s\n", err)
				continue
			}

			if started {
				log.Printf("Started %s version %s as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])
			}

			log.Debugf("%s version %s running as %s\n", serviceConfig.Name, serviceConfig.Version(), container.ID[0:12])

			err = serviceRuntime.StopAllButLatestService(serviceConfig, stopCutoff)
			if err != nil {
				log.Errorf("ERROR: Could not stop containers: %s\n", err)
			}
		}

		if !loop {
			return
		}

	}
}

func monitorService(changedConfigs chan *registry.ConfigChange) {

	for {

		var changedConfig *registry.ConfigChange
		select {

		case changedConfig = <-changedConfigs:

			if changedConfig.Error != nil {
				log.Errorf("ERROR: Error watching changes: %s\n", changedConfig.Error)
				continue
			}

			if changedConfig.ServiceConfig == nil {
				continue
			}

			assigned, err := appAssigned(changedConfig.ServiceConfig.Name)
			if err != nil {
				log.Errorf("ERROR: Error retrieving service config for %s: %s\n", changedConfig.ServiceConfig.Name, err)
				if !loop {
					return
				}
				continue
			}

			if !assigned {
				continue
			}

			ch, ok := workerChans[changedConfig.ServiceConfig.Name]
			if !ok {
				name := changedConfig.ServiceConfig.Name
				ch := make(chan string)
				workerChans[name] = ch
				wg.Add(1)
				go restartContainers(name, ch)
				ch <- "deploy"

				log.Printf("Started new worker for %s\n", name)
				continue
			}

			if changedConfig.Restart {
				log.Printf("Restarting %s", changedConfig.ServiceConfig.Name)
				ch <- "restart"
			} else {
				ch <- "deploy"
			}
		}
	}

}

func main() {
	flag.Int64Var(&stopCutoff, "cutoff", 10, "Seconds to wait before stopping old containers")
	flag.StringVar(&app, "app", "", "App to start")
	flag.StringVar(&redisHost, "redis", utils.GetEnv("GALAXY_REDIS_HOST", utils.DefaultRedisHost), "redis host")
	flag.StringVar(&env, "env", utils.GetEnv("GALAXY_ENV", ""), "Environment namespace")
	flag.StringVar(&pool, "pool", utils.GetEnv("GALAXY_POOL", ""), "Pool namespace")
	flag.BoolVar(&loop, "loop", false, "Run continously")
	flag.StringVar(&shuttleHost, "shuttleAddr", "", "IP where containers can reach shuttle proxy. Defaults to docker0 IP.")
	flag.StringVar(&statsdHost, "statsdAddr", utils.GetEnv("GALAXY_STATSD_HOST", ""), "IP where containers can reach a statsd service. Defaults to docker0 IP:8125.")
	flag.BoolVar(&debug, "debug", false, "verbose logging")
	flag.BoolVar(&version, "v", false, "display version info")

	flag.Parse()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if strings.TrimSpace(env) == "" {
		fmt.Println("Need an env")
		flag.PrintDefaults()
		os.Exit(1)
	}

	if strings.TrimSpace(pool) == "" {
		fmt.Println("Need a pool")
		flag.PrintDefaults()
		os.Exit(1)
	}

	if debug {
		log.DefaultLogger.Level = log.DEBUG
	}

	initOrDie()
	serviceRegistry.CreatePool(pool)

	for app, ch := range workerChans {
		wg.Add(1)
		go restartContainers(app, ch)
		ch <- "deploy"
	}

	if loop {
		cancelChan := make(chan struct{})
		// do we need to cancel ever?

		restartChan := serviceRegistry.Watch(cancelChan)
		monitorService(restartChan)
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/cbroglie/mustache"
	"github.com/leancloud/lean-cli/api"
	"github.com/leancloud/lean-cli/apps"
	"github.com/urfave/cli"
)

var (
	defaultBashEnvTemplateString = "export {{name}}={{value}}"
	defaultDOSEnvTemplateString  = "SET {{name}}={{value}}"
)

// this function is not reliable
func detectDOS() bool {
	if runtime.GOOS != "windows" {
		return false
	}
	shell := os.Getenv("SHELL")
	if strings.Contains(shell, "bash") ||
		strings.Contains(shell, "zsh") ||
		strings.Contains(shell, "fish") ||
		strings.Contains(shell, "csh") ||
		strings.Contains(shell, "ksh") ||
		strings.Contains(shell, "ash") {
		return false
	}
	return true
}

func envAction(c *cli.Context) error {
	port := strconv.Itoa(c.Int("port"))
	tmplString := c.String("template")
	if tmplString == "" {
		if detectDOS() {
			tmplString = defaultDOSEnvTemplateString
		} else {
			tmplString = defaultBashEnvTemplateString
		}
	}

	tmpl, err := mustache.ParseString(tmplString)
	if err != nil {
		return err
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	region, err := apps.GetAppRegion(appID)
	if err != nil {
		return err
	}

	apiServer := api.GetAppAPIURL(region, appID)

	appInfo, err := api.GetAppInfo(appID)
	if err != nil {
		return err
	}

	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}
	haveStaging := "false"
	if engineInfo.Mode == "prod" {
		haveStaging = "true"
	}

	groupName, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	groupInfo, err := api.GetGroup(appID, groupName)
	if err != nil {
		return err
	}

	envs := []map[string]string{
		map[string]string{"name": "LC_APP_ID", "value": appInfo.AppID},
		map[string]string{"name": "LC_APP_KEY", "value": appInfo.AppKey},
		map[string]string{"name": "LC_APP_MASTER_KEY", "value": appInfo.MasterKey},
		map[string]string{"name": "LC_APP_PORT", "value": port},
		map[string]string{"name": "LC_API_SERVER", "value": apiServer},
		map[string]string{"name": "LEANCLOUD_APP_ID", "value": appInfo.AppID},
		map[string]string{"name": "LEANCLOUD_APP_KEY", "value": appInfo.AppKey},
		map[string]string{"name": "LEANCLOUD_APP_MASTER_KEY", "value": appInfo.MasterKey},
		map[string]string{"name": "LEANCLOUD_APP_HOOK_KEY", "value": appInfo.HookKey},
		map[string]string{"name": "LEANCLOUD_APP_PORT", "value": port},
		map[string]string{"name": "LEANCLOUD_API_SERVER", "value": apiServer},
		map[string]string{"name": "LEANCLOUD_APP_ENV", "value": "development"},
		map[string]string{"name": "LEANCLOUD_REGION", "value": region.String()},
		map[string]string{"name": "LEANCLOUD_APP_DOMAIN", "value": groupInfo.Domain},
		map[string]string{"name": "LEAN_CLI_HAVE_STAGING", "value": haveStaging},
	}

	for name, value := range groupInfo.Environments {
		envs = append(envs, map[string]string{"name": name, "value": value})
	}

	for _, env := range envs {
		result, err := tmpl.Render(env)
		if err != nil {
			return err
		}
		fmt.Println(result)
	}

	return nil
}

func envSetAction(c *cli.Context) error {
	if c.NArg() != 2 {
		cli.ShowSubcommandHelp(c)
		return cli.NewExitError("", 1)
	}
	envName := c.Args()[0]
	envValue := c.Args()[1]

	if strings.HasPrefix(strings.ToUpper(envName), "LEANCLOUD") {
		return errors.New("Do not set any environment variable starting with `LEANCLOUD`")
	}

	if strings.HasPrefix(strings.ToUpper(envName), "LEAN_CLI") {
		return errors.New("Do not set any environment variable starting with `LEAN_CLI`")
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	logp.Info("Retriving LeanEngine info ...")
	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}
	group, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}

	envs := engineInfo.Environments
	envs[envName] = envValue
	logp.Info("Updating environment variables for group: " + group)
	return api.PutEnvironments(appID, group, envs)
}

func envUnsetAction(c *cli.Context) error {
	if c.NArg() != 1 {
		cli.ShowSubcommandHelp(c)
		return cli.NewExitError("", 1)
	}
	env := c.Args()[0]

	if strings.HasPrefix(strings.ToUpper(env), "LEANCLOUD") {
		return errors.New("Please do not unset any environment variable starting with `LEANCLOUD`")
	}

	if strings.HasPrefix(strings.ToUpper(env), "LEAN_CLI") {
		return errors.New("Please do not unset any environment variable starting with `LEAN_CLI`")
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	logp.Info("Retrieving LeanEngine info ...")
	group, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}

	envs := engineInfo.Environments
	delete(envs, env)

	logp.Info("Updating environment variables for group: " + group)
	return api.PutEnvironments(appID, group, envs)
}
<commit_msg>:ok_hand: `lean env` Should use envs from groupInfo<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/cbroglie/mustache"
	"github.com/leancloud/lean-cli/api"
	"github.com/leancloud/lean-cli/apps"
	"github.com/urfave/cli"
)

var (
	defaultBashEnvTemplateString = "export {{name}}={{value}}"
	defaultDOSEnvTemplateString  = "SET {{name}}={{value}}"
)

// this function is not reliable
func detectDOS() bool {
	if runtime.GOOS != "windows" {
		return false
	}
	shell := os.Getenv("SHELL")
	if strings.Contains(shell, "bash") ||
		strings.Contains(shell, "zsh") ||
		strings.Contains(shell, "fish") ||
		strings.Contains(shell, "csh") ||
		strings.Contains(shell, "ksh") ||
		strings.Contains(shell, "ash") {
		return false
	}
	return true
}

func envAction(c *cli.Context) error {
	port := strconv.Itoa(c.Int("port"))
	tmplString := c.String("template")
	if tmplString == "" {
		if detectDOS() {
			tmplString = defaultDOSEnvTemplateString
		} else {
			tmplString = defaultBashEnvTemplateString
		}
	}

	tmpl, err := mustache.ParseString(tmplString)
	if err != nil {
		return err
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	region, err := apps.GetAppRegion(appID)
	if err != nil {
		return err
	}

	apiServer := api.GetAppAPIURL(region, appID)

	appInfo, err := api.GetAppInfo(appID)
	if err != nil {
		return err
	}

	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}
	haveStaging := "false"
	if engineInfo.Mode == "prod" {
		haveStaging = "true"
	}

	groupName, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	groupInfo, err := api.GetGroup(appID, groupName)
	if err != nil {
		return err
	}

	envs := []map[string]string{
		map[string]string{"name": "LC_APP_ID", "value": appInfo.AppID},
		map[string]string{"name": "LC_APP_KEY", "value": appInfo.AppKey},
		map[string]string{"name": "LC_APP_MASTER_KEY", "value": appInfo.MasterKey},
		map[string]string{"name": "LC_APP_PORT", "value": port},
		map[string]string{"name": "LC_API_SERVER", "value": apiServer},
		map[string]string{"name": "LEANCLOUD_APP_ID", "value": appInfo.AppID},
		map[string]string{"name": "LEANCLOUD_APP_KEY", "value": appInfo.AppKey},
		map[string]string{"name": "LEANCLOUD_APP_MASTER_KEY", "value": appInfo.MasterKey},
		map[string]string{"name": "LEANCLOUD_APP_HOOK_KEY", "value": appInfo.HookKey},
		map[string]string{"name": "LEANCLOUD_APP_PORT", "value": port},
		map[string]string{"name": "LEANCLOUD_API_SERVER", "value": apiServer},
		map[string]string{"name": "LEANCLOUD_APP_ENV", "value": "development"},
		map[string]string{"name": "LEANCLOUD_REGION", "value": region.String()},
		map[string]string{"name": "LEANCLOUD_APP_DOMAIN", "value": groupInfo.Domain},
		map[string]string{"name": "LEAN_CLI_HAVE_STAGING", "value": haveStaging},
	}

	for name, value := range groupInfo.Environments {
		envs = append(envs, map[string]string{"name": name, "value": value})
	}

	for _, env := range envs {
		result, err := tmpl.Render(env)
		if err != nil {
			return err
		}
		fmt.Println(result)
	}

	return nil
}

func envSetAction(c *cli.Context) error {
	if c.NArg() != 2 {
		cli.ShowSubcommandHelp(c)
		return cli.NewExitError("", 1)
	}
	envName := c.Args()[0]
	envValue := c.Args()[1]

	if strings.HasPrefix(strings.ToUpper(envName), "LEANCLOUD") {
		return errors.New("Do not set any environment variable starting with `LEANCLOUD`")
	}

	if strings.HasPrefix(strings.ToUpper(envName), "LEAN_CLI") {
		return errors.New("Do not set any environment variable starting with `LEAN_CLI`")
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	logp.Info("Retriving LeanEngine info ...")
	group, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}

	groupInfo, err := api.GetGroup(appID, group)
	if err != nil {
		return err
	}

	envs := groupInfo.Environments
	envs[envName] = envValue
	logp.Info("Updating environment variables for group: " + group)
	return api.PutEnvironments(appID, group, envs)
}

func envUnsetAction(c *cli.Context) error {
	if c.NArg() != 1 {
		cli.ShowSubcommandHelp(c)
		return cli.NewExitError("", 1)
	}
	env := c.Args()[0]

	if strings.HasPrefix(strings.ToUpper(env), "LEANCLOUD") {
		return errors.New("Please do not unset any environment variable starting with `LEANCLOUD`")
	}

	if strings.HasPrefix(strings.ToUpper(env), "LEAN_CLI") {
		return errors.New("Please do not unset any environment variable starting with `LEAN_CLI`")
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	logp.Info("Retrieving LeanEngine info ...")
	group, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	groupInfo, err := api.GetGroup(appID, group)
	if err != nil {
		return err
	}

	envs := groupInfo.Environments
	delete(envs, env)

	logp.Info("Updating environment variables for group: " + group)
	return api.PutEnvironments(appID, group, envs)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"strings"
	"testing"

	"github.com/cristianoliveira/ergo/commands/setup"
	"github.com/cristianoliveira/ergo/proxy"
)

func TestSetup(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	command := SetupCommand{System: "inexistent-os", Remove: false}
	_, err := command.Execute(config)

	if !strings.Contains(err.Error(), "List of supported system") {
		t.Fatalf("Expected Setup to tell us about the supported systems if we ask"+
			" it to run an unsupported system. Got %s.", err.Error())
	}
}

type TestRunner struct {
	Test            *testing.T
	ExpectToInclude string
}

func (r *TestRunner) Run(command string) error {
	if r.ExpectToInclude == "" {
		fmt.Println("No expectation")
		return nil
	}

	if !strings.Contains(command, r.ExpectToInclude) {
		r.Test.Fatalf(
			"Expected command to include '%s' but it is not present.\n Command: %s",
			r.ExpectToInclude,
			command,
		)
	}

	return nil
}

func TestSetupLinuxGnome(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title:                  "expect to set networking mode auto",
				CommandExpectToInclude: "mode 'auto'",
			},
			{
				Title:                  "expect to set networking url",
				CommandExpectToInclude: `autoconfig-url '` + config.GetProxyPacURL() + `'`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "linux-gnome", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title:                  "expect to set networking mode none",
				CommandExpectToInclude: "mode 'none'",
			},
			{
				Title:                  "expect to set networking no url",
				CommandExpectToInclude: `autoconfig-url ''`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "linux-gnome", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}

func TestSetupOSX(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title:                  "expect to set networking proxy pac url",
				CommandExpectToInclude: `-setautoproxyurl "Wi-Fi" "` + config.GetProxyPacURL() + `"`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "osx", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title:                  "expect to set networking wi-fi to none",
				CommandExpectToInclude: `-setautoproxyurl "Wi-Fi" ""`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "osx", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}

func TestSetupWindows(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to add a new register",
				CommandExpectToInclude: "reg add",
			},
			{
				Title:                  "expect to set networking proxy pac url",
				CommandExpectToInclude: `AutoConfigURL /t REG_SZ /d ` + config.GetProxyPacURL(),
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "windows", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title:                  "expect to delete the register",
				CommandExpectToInclude: "reg delete",
			},
			{
				Title:                  "expect to set networking wi-fi to none",
				CommandExpectToInclude: "AutoConfigURL /f",
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "windows", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}
<commit_msg>chore: fix lint for go old versions<commit_after>package commands

import (
	"fmt"
	"strings"
	"testing"

	"github.com/cristianoliveira/ergo/commands/setup"
	"github.com/cristianoliveira/ergo/proxy"
)

func TestSetup(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	command := SetupCommand{System: "inexistent-os", Remove: false}
	_, err := command.Execute(config)

	if !strings.Contains(err.Error(), "List of supported system") {
		t.Fatalf("Expected Setup to tell us about the supported systems if we ask"+
			" it to run an unsupported system. Got %s.", err.Error())
	}
}

type TestRunner struct {
	Test            *testing.T
	ExpectToInclude string
}

func (r *TestRunner) Run(command string) error {
	if r.ExpectToInclude == "" {
		fmt.Println("No expectation")
		return nil
	}

	if !strings.Contains(command, r.ExpectToInclude) {
		r.Test.Fatalf(
			"Expected command to include '%s' but it is not present.\n Command: %s",
			r.ExpectToInclude,
			command,
		)
	}

	return nil
}

func TestSetupLinuxGnome(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title: "expect to set networking mode auto",
				CommandExpectToInclude: "mode 'auto'",
			},
			{
				Title: "expect to set networking url",
				CommandExpectToInclude: `autoconfig-url '` + config.GetProxyPacURL() + `'`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "linux-gnome", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title: "expect to set networking mode none",
				CommandExpectToInclude: "mode 'none'",
			},
			{
				Title: "expect to set networking no url",
				CommandExpectToInclude: `autoconfig-url ''`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "linux-gnome", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}

func TestSetupOSX(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title: "expect to set networking proxy pac url",
				CommandExpectToInclude: `-setautoproxyurl "Wi-Fi" "` + config.GetProxyPacURL() + `"`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "osx", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to run with sh",
				CommandExpectToInclude: "/bin/sh -c",
			},
			{
				Title: "expect to set networking wi-fi to none",
				CommandExpectToInclude: `-setautoproxyurl "Wi-Fi" ""`,
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "osx", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}

func TestSetupWindows(t *testing.T) {
	config := buildConfig([]proxy.Service{
		{Name: "test.dev", URL: "localhost:999"},
	})

	t.Run("when setting up", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to add a new register",
				CommandExpectToInclude: "reg add",
			},
			{
				Title: "expect to set networking proxy pac url",
				CommandExpectToInclude: `AutoConfigURL /t REG_SZ /d ` + config.GetProxyPacURL(),
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "windows", Remove: false}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})

	t.Run("when setting down", func(t *testing.T) {
		var cases = []struct {
			Title                  string
			CommandExpectToInclude string
		}{
			{
				Title: "expect to delete the register",
				CommandExpectToInclude: "reg delete",
			},
			{
				Title: "expect to set networking wi-fi to none",
				CommandExpectToInclude: "AutoConfigURL /f",
			},
		}

		for _, c := range cases {
			t.Run(c.Title, func(tt *testing.T) {
				setup.RunnerDefault = &TestRunner{
					Test:            tt,
					ExpectToInclude: c.CommandExpectToInclude,
				}

				command := SetupCommand{System: "windows", Remove: true}
				_, err := command.Execute(config)
				if err != nil {
					t.Fatalf(err.Error())
				}
			})
		}
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The cert-manager 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 mutation_test

import (
	"bytes"
	"encoding/json"
	"testing"

	admissionv1 "k8s.io/api/admission/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager"
	"github.com/jetstack/cert-manager/pkg/internal/api/mutation"
	cminternal "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
	"github.com/jetstack/cert-manager/pkg/webhook"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

var (
	// use the webhook's Scheme during test fixtures as it has all internal and
	// external cert-manager kinds registered
	scheme = webhook.Scheme
)

func TestMutate(t *testing.T) {
	crGVK := &metav1.GroupVersionKind{
		Group:   certmanager.GroupName,
		Version: "v1",
		Kind:    "CertificateRequest",
	}

	testCR := gen.CertificateRequest("test-cr",
		gen.SetCertificateRequestTypeMeta(metav1.TypeMeta{
			Kind:       "CertificateRequest",
			APIVersion: "cert-manager.io/v1",
		}),
	)
	testCRBytes, err := json.Marshal(testCR)
	if err != nil {
		t.Fatal(err)
	}

	testNotRegistered := gen.CertificateRequest("test-cr",
		gen.SetCertificateRequestTypeMeta(metav1.TypeMeta{
			Kind:       "NotRegistered",
			APIVersion: "not-registered.io/v1",
		}),
	)
	testNotRegisteredBytes, err := json.Marshal(testNotRegistered)
	if err != nil {
		t.Fatal(err)
	}

	type mutationsEntry struct {
		obj runtime.Object
		fn  func(t *testing.T) mutation.MutateFunc
	}
	type mutationUpdatesEntry struct {
		obj runtime.Object
		fn  func(t *testing.T) mutation.MutateUpdateFunc
	}
	tests := map[string]struct {
		mutations       []mutationsEntry
		mutationUpdates []mutationUpdatesEntry
		req             *admissionv1.AdmissionRequest

		expErr   bool
		expPatch []byte
	}{
		"exit early if operation is not UPDATE or CREATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Delete,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: nil,
		},
		"if no functions registered, expect only default patch on CREATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if no functions registered, expect only default patch on UPDATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if kind presented for mutation hasn't been registered for CREATE, error": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
			},
			expErr:   true,
			expPatch: nil,
		},
		"if kind presented for mutation hasn't been registered for UPDATE, error": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				OldObject: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
				Object: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
			},
			expErr:   true,
			expPatch: nil,
		},
		"if update mutation function registered for different kind, ignore": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if create mutation function registered for different kind, ignore": {
			mutations: nil,
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if create mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if update mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if multiple create mutation functions registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							if cr.Annotations == nil {
								cr.Annotations = make(map[string]string)
							}
							cr.Annotations["second-mutation"] = "called"
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"add","path":"/metadata/annotations","value":{"second-mutation":"called"}},{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if multiple update mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							if cr.Annotations == nil {
								cr.Annotations = make(map[string]string)
							}
							cr.Annotations["second-mutation"] = "called"
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"add","path":"/metadata/annotations","value":{"second-mutation":"called"}},{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			reg := mutation.NewRegistry(scheme)

			// Add mutation functions to registry
			for _, m := range test.mutations {
				reg.AddMutateFunc(m.obj, m.fn(t))
			}
			for _, m := range test.mutationUpdates {
				reg.AddMutateUpdateFunc(m.obj, m.fn(t))
			}

			patch, err := reg.Mutate(test.req)
			if test.expErr != (err != nil) {
				t.Errorf("unexpected error, exp=%t got=%v",
					test.expErr, err)
			}

			if !bytes.Equal(test.expPatch, patch) {
				t.Errorf("unexpected patch, exp=%s got=%s",
					test.expPatch, patch)
			}
		})
	}
}
<commit_msg>errcheck<commit_after>/*
Copyright 2020 The cert-manager 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 mutation_test

import (
	"bytes"
	"encoding/json"
	"testing"

	admissionv1 "k8s.io/api/admission/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager"
	"github.com/jetstack/cert-manager/pkg/internal/api/mutation"
	cminternal "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
	"github.com/jetstack/cert-manager/pkg/webhook"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

var (
	// use the webhook's Scheme during test fixtures as it has all internal and
	// external cert-manager kinds registered
	scheme = webhook.Scheme
)

func TestMutate(t *testing.T) {
	crGVK := &metav1.GroupVersionKind{
		Group:   certmanager.GroupName,
		Version: "v1",
		Kind:    "CertificateRequest",
	}

	testCR := gen.CertificateRequest("test-cr",
		gen.SetCertificateRequestTypeMeta(metav1.TypeMeta{
			Kind:       "CertificateRequest",
			APIVersion: "cert-manager.io/v1",
		}),
	)
	testCRBytes, err := json.Marshal(testCR)
	if err != nil {
		t.Fatal(err)
	}

	testNotRegistered := gen.CertificateRequest("test-cr",
		gen.SetCertificateRequestTypeMeta(metav1.TypeMeta{
			Kind:       "NotRegistered",
			APIVersion: "not-registered.io/v1",
		}),
	)
	testNotRegisteredBytes, err := json.Marshal(testNotRegistered)
	if err != nil {
		t.Fatal(err)
	}

	type mutationsEntry struct {
		obj runtime.Object
		fn  func(t *testing.T) mutation.MutateFunc
	}
	type mutationUpdatesEntry struct {
		obj runtime.Object
		fn  func(t *testing.T) mutation.MutateUpdateFunc
	}
	tests := map[string]struct {
		mutations       []mutationsEntry
		mutationUpdates []mutationUpdatesEntry
		req             *admissionv1.AdmissionRequest

		expErr   bool
		expPatch []byte
	}{
		"exit early if operation is not UPDATE or CREATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Delete,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: nil,
		},
		"if no functions registered, expect only default patch on CREATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if no functions registered, expect only default patch on UPDATE": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if kind presented for mutation hasn't been registered for CREATE, error": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
			},
			expErr:   true,
			expPatch: nil,
		},
		"if kind presented for mutation hasn't been registered for UPDATE, error": {
			mutations:       nil,
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				OldObject: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
				Object: runtime.RawExtension{
					Raw: testNotRegisteredBytes,
				},
			},
			expErr:   true,
			expPatch: nil,
		},
		"if update mutation function registered for different kind, ignore": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: nil,
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if create mutation function registered for different kind, ignore": {
			mutations: nil,
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte("[]"),
		},
		"if create mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if update mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if multiple create mutation functions registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							if cr.Annotations == nil {
								cr.Annotations = make(map[string]string)
							}
							cr.Annotations["second-mutation"] = "called"
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Create,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"add","path":"/metadata/annotations","value":{"second-mutation":"called"}},{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
		"if multiple update mutation function registered for kind, run mutation": {
			mutations: []mutationsEntry{
				{
					obj: new(cminternal.Certificate),
					fn: func(t *testing.T) mutation.MutateFunc {
						return func(_ *admissionv1.AdmissionRequest, _ runtime.Object) {
							t.Error("unexpected call")
						}
					},
				},
			},
			mutationUpdates: []mutationUpdatesEntry{
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							cr.Spec.Request = []byte("mutation called")
						}
					},
				},
				{
					obj: new(cminternal.CertificateRequest),
					fn: func(t *testing.T) mutation.MutateUpdateFunc {
						return func(_ *admissionv1.AdmissionRequest, _, obj runtime.Object) {
							cr := obj.(*cminternal.CertificateRequest)
							if cr.Annotations == nil {
								cr.Annotations = make(map[string]string)
							}
							cr.Annotations["second-mutation"] = "called"
						}
					},
				},
			},
			req: &admissionv1.AdmissionRequest{
				RequestKind: crGVK.DeepCopy(),
				Operation:   admissionv1.Update,
				Object: runtime.RawExtension{
					Raw: testCRBytes,
				},
				OldObject: runtime.RawExtension{
					Raw: testCRBytes,
				},
			},
			expErr:   false,
			expPatch: []byte(`[{"op":"add","path":"/metadata/annotations","value":{"second-mutation":"called"}},{"op":"replace","path":"/spec/request","value":"bXV0YXRpb24gY2FsbGVk"}]`),
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			reg := mutation.NewRegistry(scheme)

			// Add mutation functions to registry
			for _, m := range test.mutations {
				if err := reg.AddMutateFunc(m.obj, m.fn(t)); err != nil {
					t.Errorf("reg.AddMutateFunc failed (%s)", err.Error())
				}
			}
			for _, m := range test.mutationUpdates {
				if err := reg.AddMutateUpdateFunc(m.obj, m.fn(t)); err != nil {
					t.Errorf("reg.AddMutateUpdateFunc failed (%s)", err.Error())
				}
			}

			patch, err := reg.Mutate(test.req)
			if test.expErr != (err != nil) {
				t.Errorf("unexpected error, exp=%t got=%v",
					test.expErr, err)
			}

			if !bytes.Equal(test.expPatch, patch) {
				t.Errorf("unexpected patch, exp=%s got=%s",
					test.expPatch, patch)
			}
		})
	}
}
<|endoftext|>


<commit_before>package deplactive

import (
	"fmt"
	"strings"

	"github.com/sirupsen/logrus"

	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/chkit/pkg/util/text"
	"github.com/containerum/chkit/pkg/util/validation"
)

func getContainers(conts []container.Container) []container.Container {
	containers := make([]container.Container, len(conts))
	copy(containers, conts)
	ok := true
	for exit := false; !exit; {
		containerMenuItems := make([]*activekit.MenuItem, 0, len(containers))
		for i, cont := range containers {
			containerMenuItems = append(containerMenuItems,
				&activekit.MenuItem{
					Label: fmt.Sprintf("Edit container %q", cont.Name),
					Action: func(i int, cont container.Container) func() error {
						return func() error {
							logrus.Debugf("editing container %q", containerMenuItems[i])
							edited, ok := getContainer(cont)
							if ok {
								containers[i] = edited
							}
							return nil
						}
					}(i, cont),
				})
		}
		containerMenuItems = append(containerMenuItems,
			[]*activekit.MenuItem{
				{
					Label: "Add new container",
					Action: func() error {
						logrus.Debugf("adding container")
						cont, ok := getContainer(container.Container{
							Container: model.Container{
								Name: namegen.Aster() + "-" + namegen.Color(),
								Limits: model.Resource{
									Memory: 256,
									CPU:    200,
								},
								Ports: []model.ContainerPort(nil),
							},
						})
						if ok {
							containers = append(containers, cont)
							fmt.Printf("Container %q added to list\n", cont.Name)
						}
						return nil
					},
				},
				{
					Label: "Delete container",
					Action: func() error {
						logrus.Debugf("deleting container")
						var deleteMenu []*activekit.MenuItem
						for i, name := range getContainersNamesList(containers) {
							deleteMenu = append(deleteMenu, &activekit.MenuItem{
								Label: name,
								Action: func(i int, name string) func() error {
									return func() error {
										yes, _ := activekit.Yes(fmt.Sprintf("Are you sure you want to delete the container %q?",
											name))
										if yes {
											containers = append(containers[:i], containers[i+1:]...)
										}
										return nil
									}
								}(i, name),
							})
						}
						deleteMenu = append(deleteMenu, &activekit.MenuItem{
							Label: "Return to previous menu",
						})
						(&activekit.Menu{
							Title: "Which container do you want to delete?",
							Items: deleteMenu,
						}).Run()
						return nil
					},
				},
				{
					Label: "Confirm",
					Action: func() error {
						exit = true
						ok = true
						return nil
					},
				},
				{
					Label: "Return to previous menu, drop all changes",
					Action: func() error {
						exit = true
						ok = false
						return nil
					},
				},
			}...)
		_, err := (&activekit.Menu{
			Items: containerMenuItems,
		}).Run()
		if err != nil {
			logrus.WithError(err).Errorf("")
			break
		}
	}
	if ok {
		return containers
	}
	return conts
}

func getContainer(con container.Container) (container.Container, bool) {
	ok := true
	for exit := false; !exit; {
		(&activekit.Menu{
			Items: []*activekit.MenuItem{
				{
					Label: fmt.Sprintf("Set name         : %s", con.Name),
					Action: func() error {
						con.Name = getContainerName(con.Name)
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set image        : %s",
						activekit.OrString(con.Image, "none (required)")),
					Action: func() error {
						con.Image = getContainerImage()
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set memory limit : %dMb", con.Limits.Memory),
					Action: func() error {
						con.Limits.Memory = getMemory(con.Limits.Memory)
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set CPU limit    : %d", con.Limits.CPU),
					Action: func() error {
						con.Limits.CPU = getCPU(con.Limits.CPU)
						return nil
					},
				},
				{
					Label: "Confirm",
					Action: func() error {
						if err := validateContainer(con); err != nil {
							errText := err.Error()
							attention := strings.Repeat("!", text.Width(errText))
							fmt.Printf("%s\n%v\n%s\n", attention, errText, attention)
							return nil
						}
						exit = true
						return nil
					},
				},
				{
					Label: "Return to previous menu",
					Action: func() error {
						ok = false
						exit = true
						return nil
					},
				},
			},
		}).Run()
	}
	return con, ok
}

func getContainerName(defaultName string) string {
	for {
		name, _ := activekit.AskLine(fmt.Sprintf("Type container name (press Enter to use %q) > ", defaultName))
		name = strings.TrimSpace(name)
		if name == "" {
			name = defaultName
		}
		if validation.ValidateContainerName(name) != nil {
			fmt.Printf("Invalid name :( Try again.\n")
			continue
		}
		return name
	}
}

func getContainerImage() string {
	fmt.Printf("Which image do you want to use?\n")
	for {
		image, _ := activekit.AskLine("> ")
		image = strings.TrimSpace(image)
		if image == "" {
			return ""
		}
		if validation.ValidateImageName(image) != nil {
			fmt.Printf("Invalid image name :( Try again.\n")
			continue
		}
		return image
	}
}

func getMemory(oldValue uint) uint {
	for {
		memStr, _ := activekit.AskLine("Memory (Mb, 10..8000) > ")
		memStr = strings.TrimSpace(memStr)
		var mem uint
		if memStr == "" {
			return oldValue
		}
		if _, err := fmt.Sscanln(memStr, &mem); err != nil || mem < 10 || mem > 8000 {
			fmt.Printf("Memory must be interger number 10..16000. Try again.\n")
			continue
		}
		return mem
	}
}

func getCPU(oldValue uint) uint {
	for {
		cpuStr, _ := activekit.AskLine("CPU (10..3000 mCPU) > ")
		cpuStr = strings.TrimSpace(cpuStr)
		var cpu uint
		if cpuStr == "" {
			return oldValue
		}
		if _, err := fmt.Sscanln(cpuStr, &cpu); err != nil || cpu < 10 || cpu > 3000 {
			fmt.Printf("CPU must be number 10..3000. Try again.\n")
			continue
		}
		return cpu
	}
}

func getName(defaultName string) string {
	for {
		name, _ := activekit.AskLine(fmt.Sprintf("Print deployment name (or hit Enter to use %q) > ", defaultName))
		if strings.TrimSpace(name) == "" {
			name = defaultName
		}
		if err := validation.ValidateLabel(name); err != nil {
			fmt.Printf("Invalid name %q. Try again\n", name)
			continue
		}
		return name
	}
}

func getReplicas(defaultReplicas int) int {
	for {
		replicasStr, _ := activekit.AskLine(fmt.Sprintf("Print number or replicas (1..15, hit Enter to use %d) > ", defaultReplicas))
		replicas := defaultReplicas
		if strings.TrimSpace(replicasStr) == "" {
			return defaultReplicas
		}
		if _, err := fmt.Sscan(replicasStr, &replicas); err != nil || replicas < 1 || replicas > 15 {
			fmt.Printf("Expected number 1..15! Try again.\n")
			continue
		}
		return replicas
	}
}

func getContainersNamesList(containers []container.Container) []string {
	names := make([]string, 0, len(containers))
	for _, cont := range containers {
		names = append(names, cont.Name)
	}
	return names
}
<commit_msg>add unit<commit_after>package deplactive

import (
	"fmt"
	"strings"

	"github.com/sirupsen/logrus"

	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/chkit/pkg/util/text"
	"github.com/containerum/chkit/pkg/util/validation"
)

func getContainers(conts []container.Container) []container.Container {
	containers := make([]container.Container, len(conts))
	copy(containers, conts)
	ok := true
	for exit := false; !exit; {
		containerMenuItems := make([]*activekit.MenuItem, 0, len(containers))
		for i, cont := range containers {
			containerMenuItems = append(containerMenuItems,
				&activekit.MenuItem{
					Label: fmt.Sprintf("Edit container %q", cont.Name),
					Action: func(i int, cont container.Container) func() error {
						return func() error {
							logrus.Debugf("editing container %q", containerMenuItems[i])
							edited, ok := getContainer(cont)
							if ok {
								containers[i] = edited
							}
							return nil
						}
					}(i, cont),
				})
		}
		containerMenuItems = append(containerMenuItems,
			[]*activekit.MenuItem{
				{
					Label: "Add new container",
					Action: func() error {
						logrus.Debugf("adding container")
						cont, ok := getContainer(container.Container{
							Container: model.Container{
								Name: namegen.Aster() + "-" + namegen.Color(),
								Limits: model.Resource{
									Memory: 256,
									CPU:    200,
								},
								Ports: []model.ContainerPort(nil),
							},
						})
						if ok {
							containers = append(containers, cont)
							fmt.Printf("Container %q added to list\n", cont.Name)
						}
						return nil
					},
				},
				{
					Label: "Delete container",
					Action: func() error {
						logrus.Debugf("deleting container")
						var deleteMenu []*activekit.MenuItem
						for i, name := range getContainersNamesList(containers) {
							deleteMenu = append(deleteMenu, &activekit.MenuItem{
								Label: name,
								Action: func(i int, name string) func() error {
									return func() error {
										yes, _ := activekit.Yes(fmt.Sprintf("Are you sure you want to delete the container %q?",
											name))
										if yes {
											containers = append(containers[:i], containers[i+1:]...)
										}
										return nil
									}
								}(i, name),
							})
						}
						deleteMenu = append(deleteMenu, &activekit.MenuItem{
							Label: "Return to previous menu",
						})
						(&activekit.Menu{
							Title: "Which container do you want to delete?",
							Items: deleteMenu,
						}).Run()
						return nil
					},
				},
				{
					Label: "Confirm",
					Action: func() error {
						exit = true
						ok = true
						return nil
					},
				},
				{
					Label: "Return to previous menu, drop all changes",
					Action: func() error {
						exit = true
						ok = false
						return nil
					},
				},
			}...)
		_, err := (&activekit.Menu{
			Items: containerMenuItems,
		}).Run()
		if err != nil {
			logrus.WithError(err).Errorf("")
			break
		}
	}
	if ok {
		return containers
	}
	return conts
}

func getContainer(con container.Container) (container.Container, bool) {
	ok := true
	for exit := false; !exit; {
		(&activekit.Menu{
			Items: []*activekit.MenuItem{
				{
					Label: fmt.Sprintf("Set name         : %s", con.Name),
					Action: func() error {
						con.Name = getContainerName(con.Name)
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set image        : %s",
						activekit.OrString(con.Image, "none (required)")),
					Action: func() error {
						con.Image = getContainerImage()
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set memory limit : %d Mb", con.Limits.Memory),
					Action: func() error {
						con.Limits.Memory = getMemory(con.Limits.Memory)
						return nil
					},
				},
				{
					Label: fmt.Sprintf("Set CPU limit    : %d mCPU", con.Limits.CPU),
					Action: func() error {
						con.Limits.CPU = getCPU(con.Limits.CPU)
						return nil
					},
				},
				{
					Label: "Confirm",
					Action: func() error {
						if err := validateContainer(con); err != nil {
							errText := err.Error()
							attention := strings.Repeat("!", text.Width(errText))
							fmt.Printf("%s\n%v\n%s\n", attention, errText, attention)
							return nil
						}
						exit = true
						return nil
					},
				},
				{
					Label: "Return to previous menu",
					Action: func() error {
						ok = false
						exit = true
						return nil
					},
				},
			},
		}).Run()
	}
	return con, ok
}

func getContainerName(defaultName string) string {
	for {
		name, _ := activekit.AskLine(fmt.Sprintf("Type container name (press Enter to use %q) > ", defaultName))
		name = strings.TrimSpace(name)
		if name == "" {
			name = defaultName
		}
		if validation.ValidateContainerName(name) != nil {
			fmt.Printf("Invalid name :( Try again.\n")
			continue
		}
		return name
	}
}

func getContainerImage() string {
	fmt.Printf("Which image do you want to use?\n")
	for {
		image, _ := activekit.AskLine("> ")
		image = strings.TrimSpace(image)
		if image == "" {
			return ""
		}
		if validation.ValidateImageName(image) != nil {
			fmt.Printf("Invalid image name :( Try again.\n")
			continue
		}
		return image
	}
}

func getMemory(oldValue uint) uint {
	for {
		memStr, _ := activekit.AskLine("Memory (Mb, 10..8000) > ")
		memStr = strings.TrimSpace(memStr)
		var mem uint
		if memStr == "" {
			return oldValue
		}
		if _, err := fmt.Sscanln(memStr, &mem); err != nil || mem < 10 || mem > 8000 {
			fmt.Printf("Memory must be interger number 10..16000. Try again.\n")
			continue
		}
		return mem
	}
}

func getCPU(oldValue uint) uint {
	for {
		cpuStr, _ := activekit.AskLine("CPU (10..3000 mCPU) > ")
		cpuStr = strings.TrimSpace(cpuStr)
		var cpu uint
		if cpuStr == "" {
			return oldValue
		}
		if _, err := fmt.Sscanln(cpuStr, &cpu); err != nil || cpu < 10 || cpu > 3000 {
			fmt.Printf("CPU must be number 10..3000. Try again.\n")
			continue
		}
		return cpu
	}
}

func getName(defaultName string) string {
	for {
		name, _ := activekit.AskLine(fmt.Sprintf("Print deployment name (or hit Enter to use %q) > ", defaultName))
		if strings.TrimSpace(name) == "" {
			name = defaultName
		}
		if err := validation.ValidateLabel(name); err != nil {
			fmt.Printf("Invalid name %q. Try again\n", name)
			continue
		}
		return name
	}
}

func getReplicas(defaultReplicas int) int {
	for {
		replicasStr, _ := activekit.AskLine(fmt.Sprintf("Print number or replicas (1..15, hit Enter to use %d) > ", defaultReplicas))
		replicas := defaultReplicas
		if strings.TrimSpace(replicasStr) == "" {
			return defaultReplicas
		}
		if _, err := fmt.Sscan(replicasStr, &replicas); err != nil || replicas < 1 || replicas > 15 {
			fmt.Printf("Expected number 1..15! Try again.\n")
			continue
		}
		return replicas
	}
}

func getContainersNamesList(containers []container.Container) []string {
	names := make([]string, 0, len(containers))
	for _, cont := range containers {
		names = append(names, cont.Name)
	}
	return names
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"

	"github.com/grafana/grafana/pkg/services/ngalert/models"
	"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)

func AddAlertDefinitionMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) {
	mg.AddMigration("delete alert_definition table", migrator.NewDropTableMigration("alert_definition"))

	alertDefinition := migrator.Table{
		Name: "alert_definition",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false, Default: fmt.Sprintf("%d", defaultIntervalSeconds)},
			{Name: "version", Type: migrator.DB_Int, Nullable: false, Default: "0"},
			{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"org_id", "title"}, Type: migrator.IndexType},
			{Cols: []string{"org_id", "uid"}, Type: migrator.IndexType},
		},
	}
	// create table
	mg.AddMigration("recreate alert_definition table", migrator.NewAddTableMigration(alertDefinition))

	// create indices
	mg.AddMigration("add index in alert_definition on org_id and title columns", migrator.NewAddIndexMigration(alertDefinition, alertDefinition.Indices[0]))
	mg.AddMigration("add index in alert_definition on org_id and uid columns", migrator.NewAddIndexMigration(alertDefinition, alertDefinition.Indices[1]))

	mg.AddMigration("alter alert_definition table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_definition MODIFY data MEDIUMTEXT;"))

	mg.AddMigration("drop index in alert_definition on org_id and title columns", migrator.NewDropIndexMigration(alertDefinition, alertDefinition.Indices[0]))
	mg.AddMigration("drop index in alert_definition on org_id and uid columns", migrator.NewDropIndexMigration(alertDefinition, alertDefinition.Indices[1]))

	uniqueIndices := []*migrator.Index{
		{Cols: []string{"org_id", "title"}, Type: migrator.UniqueIndex},
		{Cols: []string{"org_id", "uid"}, Type: migrator.UniqueIndex},
	}
	mg.AddMigration("add unique index in alert_definition on org_id and title columns", migrator.NewAddIndexMigration(alertDefinition, uniqueIndices[0]))
	mg.AddMigration("add unique index in alert_definition on org_id and uid columns", migrator.NewAddIndexMigration(alertDefinition, uniqueIndices[1]))

	mg.AddMigration("Add column paused in alert_definition", migrator.NewAddColumnMigration(alertDefinition, &migrator.Column{
		Name: "paused", Type: migrator.DB_Bool, Nullable: false, Default: "0",
	}))
}

func AddAlertDefinitionVersionMigrations(mg *migrator.Migrator) {
	mg.AddMigration("delete alert_definition_version table", migrator.NewDropTableMigration("alert_definition_version"))

	alertDefinitionVersion := migrator.Table{
		Name: "alert_definition_version",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "alert_definition_id", Type: migrator.DB_BigInt},
			{Name: "alert_definition_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			{Name: "parent_version", Type: migrator.DB_Int, Nullable: false},
			{Name: "restored_from", Type: migrator.DB_Int, Nullable: false},
			{Name: "version", Type: migrator.DB_Int, Nullable: false},
			{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"alert_definition_id", "version"}, Type: migrator.UniqueIndex},
			{Cols: []string{"alert_definition_uid", "version"}, Type: migrator.UniqueIndex},
		},
	}
	mg.AddMigration("recreate alert_definition_version table", migrator.NewAddTableMigration(alertDefinitionVersion))
	mg.AddMigration("add index in alert_definition_version table on alert_definition_id and version columns", migrator.NewAddIndexMigration(alertDefinitionVersion, alertDefinitionVersion.Indices[0]))
	mg.AddMigration("add index in alert_definition_version table on alert_definition_uid and version columns", migrator.NewAddIndexMigration(alertDefinitionVersion, alertDefinitionVersion.Indices[1]))

	mg.AddMigration("alter alert_definition_version table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_definition_version MODIFY data MEDIUMTEXT;"))
}

func AlertInstanceMigration(mg *migrator.Migrator) {
	alertInstance := migrator.Table{
		Name: "alert_instance",
		Columns: []*migrator.Column{
			{Name: "def_org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "def_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			{Name: "labels", Type: migrator.DB_Text, Nullable: false},
			{Name: "labels_hash", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "current_state", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "current_state_since", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "current_state_end", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "last_eval_time", Type: migrator.DB_BigInt, Nullable: false},
		},
		PrimaryKeys: []string{"def_org_id", "def_uid", "labels_hash"},
		Indices: []*migrator.Index{
			{Cols: []string{"def_org_id", "def_uid", "current_state"}, Type: migrator.IndexType},
			{Cols: []string{"def_org_id", "current_state"}, Type: migrator.IndexType},
		},
	}

	// create table
	mg.AddMigration("create alert_instance table", migrator.NewAddTableMigration(alertInstance))
	mg.AddMigration("add index in alert_instance table on def_org_id, def_uid and current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[0]))
	mg.AddMigration("add index in alert_instance table on def_org_id, current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[1]))
}

func AddAlertRuleMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) {
	alertRule := migrator.Table{
		Name: "alert_rule",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false, Default: fmt.Sprintf("%d", defaultIntervalSeconds)},
			{Name: "version", Type: migrator.DB_Int, Nullable: false, Default: "0"},
			{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			// the following fields will correspond to a dashboard (or folder) UIID
			{Name: "namespace_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
			{Name: "rule_group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "no_data_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.NoData.String())},
			{Name: "exec_err_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.AlertingErrState.String())},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"org_id", "title"}, Type: migrator.UniqueIndex},
			{Cols: []string{"org_id", "uid"}, Type: migrator.UniqueIndex},
			{Cols: []string{"org_id", "namespace_uid", "rule_group"}, Type: migrator.IndexType},
		},
	}
	// create table
	mg.AddMigration("create alert_rule table", migrator.NewAddTableMigration(alertRule))

	// create indices
	mg.AddMigration("add index in alert_rule on org_id and title columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[0]))
	mg.AddMigration("add index in alert_rule on org_id and uid columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[1]))
	mg.AddMigration("add index in alert_rule on org_id, namespace_uid, group_uid columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[2]))

	mg.AddMigration("alter alert_rule table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_rule MODIFY data MEDIUMTEXT;"))
}

func AddAlertRuleVersionMigrations(mg *migrator.Migrator) {
	alertRuleVersion := migrator.Table{
		Name: "alert_rule_version",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "rule_org_id", Type: migrator.DB_BigInt},
			{Name: "rule_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			// the following fields will correspond to a dashboard (or folder) UID
			{Name: "rule_namespace_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
			{Name: "rule_group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "parent_version", Type: migrator.DB_Int, Nullable: false},
			{Name: "restored_from", Type: migrator.DB_Int, Nullable: false},
			{Name: "version", Type: migrator.DB_Int, Nullable: false},
			{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "no_data_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.NoData.String())},
			{Name: "exec_err_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.AlertingErrState.String())},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"rule_org_id", "rule_uid", "version"}, Type: migrator.UniqueIndex},
			{Cols: []string{"rule_org_id", "rule_namespace_uid", "rule_group"}, Type: migrator.IndexType},
		},
	}
	mg.AddMigration("create alert_rule_version table", migrator.NewAddTableMigration(alertRuleVersion))
	mg.AddMigration("add index in alert_rule_version table on rule_org_id, rule_uid and version columns", migrator.NewAddIndexMigration(alertRuleVersion, alertRuleVersion.Indices[0]))
	mg.AddMigration("add index in alert_rule_version table on rule_org_id, rule_namespace_uid and rule_group columns", migrator.NewAddIndexMigration(alertRuleVersion, alertRuleVersion.Indices[1]))

	mg.AddMigration("alter alert_rule_version table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_rule_version MODIFY data MEDIUMTEXT;"))
}
<commit_msg>Alerting: Fix persistance migration (#32650)<commit_after>package store

import (
	"fmt"

	"github.com/grafana/grafana/pkg/services/ngalert/models"
	"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)

func AddAlertDefinitionMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) {
	mg.AddMigration("delete alert_definition table", migrator.NewDropTableMigration("alert_definition"))

	alertDefinition := migrator.Table{
		Name: "alert_definition",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false, Default: fmt.Sprintf("%d", defaultIntervalSeconds)},
			{Name: "version", Type: migrator.DB_Int, Nullable: false, Default: "0"},
			{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"org_id", "title"}, Type: migrator.IndexType},
			{Cols: []string{"org_id", "uid"}, Type: migrator.IndexType},
		},
	}
	// create table
	mg.AddMigration("recreate alert_definition table", migrator.NewAddTableMigration(alertDefinition))

	// create indices
	mg.AddMigration("add index in alert_definition on org_id and title columns", migrator.NewAddIndexMigration(alertDefinition, alertDefinition.Indices[0]))
	mg.AddMigration("add index in alert_definition on org_id and uid columns", migrator.NewAddIndexMigration(alertDefinition, alertDefinition.Indices[1]))

	mg.AddMigration("alter alert_definition table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_definition MODIFY data MEDIUMTEXT;"))

	mg.AddMigration("drop index in alert_definition on org_id and title columns", migrator.NewDropIndexMigration(alertDefinition, alertDefinition.Indices[0]))
	mg.AddMigration("drop index in alert_definition on org_id and uid columns", migrator.NewDropIndexMigration(alertDefinition, alertDefinition.Indices[1]))

	uniqueIndices := []*migrator.Index{
		{Cols: []string{"org_id", "title"}, Type: migrator.UniqueIndex},
		{Cols: []string{"org_id", "uid"}, Type: migrator.UniqueIndex},
	}
	mg.AddMigration("add unique index in alert_definition on org_id and title columns", migrator.NewAddIndexMigration(alertDefinition, uniqueIndices[0]))
	mg.AddMigration("add unique index in alert_definition on org_id and uid columns", migrator.NewAddIndexMigration(alertDefinition, uniqueIndices[1]))

	mg.AddMigration("Add column paused in alert_definition", migrator.NewAddColumnMigration(alertDefinition, &migrator.Column{
		Name: "paused", Type: migrator.DB_Bool, Nullable: false, Default: "0",
	}))
}

func AddAlertDefinitionVersionMigrations(mg *migrator.Migrator) {
	mg.AddMigration("delete alert_definition_version table", migrator.NewDropTableMigration("alert_definition_version"))

	alertDefinitionVersion := migrator.Table{
		Name: "alert_definition_version",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "alert_definition_id", Type: migrator.DB_BigInt},
			{Name: "alert_definition_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			{Name: "parent_version", Type: migrator.DB_Int, Nullable: false},
			{Name: "restored_from", Type: migrator.DB_Int, Nullable: false},
			{Name: "version", Type: migrator.DB_Int, Nullable: false},
			{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"alert_definition_id", "version"}, Type: migrator.UniqueIndex},
			{Cols: []string{"alert_definition_uid", "version"}, Type: migrator.UniqueIndex},
		},
	}
	mg.AddMigration("recreate alert_definition_version table", migrator.NewAddTableMigration(alertDefinitionVersion))
	mg.AddMigration("add index in alert_definition_version table on alert_definition_id and version columns", migrator.NewAddIndexMigration(alertDefinitionVersion, alertDefinitionVersion.Indices[0]))
	mg.AddMigration("add index in alert_definition_version table on alert_definition_uid and version columns", migrator.NewAddIndexMigration(alertDefinitionVersion, alertDefinitionVersion.Indices[1]))

	mg.AddMigration("alter alert_definition_version table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_definition_version MODIFY data MEDIUMTEXT;"))
}

func AlertInstanceMigration(mg *migrator.Migrator) {
	alertInstance := migrator.Table{
		Name: "alert_instance",
		Columns: []*migrator.Column{
			{Name: "def_org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "def_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			{Name: "labels", Type: migrator.DB_Text, Nullable: false},
			{Name: "labels_hash", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "current_state", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "current_state_since", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "last_eval_time", Type: migrator.DB_BigInt, Nullable: false},
		},
		PrimaryKeys: []string{"def_org_id", "def_uid", "labels_hash"},
		Indices: []*migrator.Index{
			{Cols: []string{"def_org_id", "def_uid", "current_state"}, Type: migrator.IndexType},
			{Cols: []string{"def_org_id", "current_state"}, Type: migrator.IndexType},
		},
	}

	// create table
	mg.AddMigration("create alert_instance table", migrator.NewAddTableMigration(alertInstance))
	mg.AddMigration("add index in alert_instance table on def_org_id, def_uid and current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[0]))
	mg.AddMigration("add index in alert_instance table on def_org_id, current_state columns", migrator.NewAddIndexMigration(alertInstance, alertInstance.Indices[1]))
	mg.AddMigration("add column current_state_end to alert_instance", migrator.NewAddColumnMigration(alertInstance, &migrator.Column{
		Name: "current_state_end", Type: migrator.DB_BigInt, Nullable: false, Default: "0",
	}))
}

func AddAlertRuleMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64) {
	alertRule := migrator.Table{
		Name: "alert_rule",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false, Default: fmt.Sprintf("%d", defaultIntervalSeconds)},
			{Name: "version", Type: migrator.DB_Int, Nullable: false, Default: "0"},
			{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			// the following fields will correspond to a dashboard (or folder) UIID
			{Name: "namespace_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
			{Name: "rule_group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "no_data_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.NoData.String())},
			{Name: "exec_err_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.AlertingErrState.String())},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"org_id", "title"}, Type: migrator.UniqueIndex},
			{Cols: []string{"org_id", "uid"}, Type: migrator.UniqueIndex},
			{Cols: []string{"org_id", "namespace_uid", "rule_group"}, Type: migrator.IndexType},
		},
	}
	// create table
	mg.AddMigration("create alert_rule table", migrator.NewAddTableMigration(alertRule))

	// create indices
	mg.AddMigration("add index in alert_rule on org_id and title columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[0]))
	mg.AddMigration("add index in alert_rule on org_id and uid columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[1]))
	mg.AddMigration("add index in alert_rule on org_id, namespace_uid, group_uid columns", migrator.NewAddIndexMigration(alertRule, alertRule.Indices[2]))

	mg.AddMigration("alter alert_rule table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_rule MODIFY data MEDIUMTEXT;"))
}

func AddAlertRuleVersionMigrations(mg *migrator.Migrator) {
	alertRuleVersion := migrator.Table{
		Name: "alert_rule_version",
		Columns: []*migrator.Column{
			{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
			{Name: "rule_org_id", Type: migrator.DB_BigInt},
			{Name: "rule_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false, Default: "0"},
			// the following fields will correspond to a dashboard (or folder) UID
			{Name: "rule_namespace_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
			{Name: "rule_group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "parent_version", Type: migrator.DB_Int, Nullable: false},
			{Name: "restored_from", Type: migrator.DB_Int, Nullable: false},
			{Name: "version", Type: migrator.DB_Int, Nullable: false},
			{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
			{Name: "title", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "condition", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
			{Name: "data", Type: migrator.DB_Text, Nullable: false},
			{Name: "interval_seconds", Type: migrator.DB_BigInt, Nullable: false},
			{Name: "no_data_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.NoData.String())},
			{Name: "exec_err_state", Type: migrator.DB_NVarchar, Length: 15, Nullable: false, Default: fmt.Sprintf("'%s'", models.AlertingErrState.String())},
		},
		Indices: []*migrator.Index{
			{Cols: []string{"rule_org_id", "rule_uid", "version"}, Type: migrator.UniqueIndex},
			{Cols: []string{"rule_org_id", "rule_namespace_uid", "rule_group"}, Type: migrator.IndexType},
		},
	}
	mg.AddMigration("create alert_rule_version table", migrator.NewAddTableMigration(alertRuleVersion))
	mg.AddMigration("add index in alert_rule_version table on rule_org_id, rule_uid and version columns", migrator.NewAddIndexMigration(alertRuleVersion, alertRuleVersion.Indices[0]))
	mg.AddMigration("add index in alert_rule_version table on rule_org_id, rule_namespace_uid and rule_group columns", migrator.NewAddIndexMigration(alertRuleVersion, alertRuleVersion.Indices[1]))

	mg.AddMigration("alter alert_rule_version table data column to mediumtext in mysql", migrator.NewRawSQLMigration("").
		Mysql("ALTER TABLE alert_rule_version MODIFY data MEDIUMTEXT;"))
}
<|endoftext|>