<commit_before>package kube

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

	"github.com/buger/goterm"
	"github.com/fatih/color"
)

var (
	red     = color.New(color.FgRed).SprintfFunc()
	blue    = color.New(color.FgBlue).SprintfFunc()
	green   = color.New(color.FgGreen).SprintfFunc()
	yellow  = color.New(color.FgYellow).SprintfFunc()
	magenta = color.New(color.FgMagenta).SprintfFunc()
	cyan    = color.New(color.FgCyan).SprintfFunc()
	gray    = color.New(color.FgHiBlack).SprintfFunc()
	white   = color.New(color.FgWhite).SprintfFunc()
	bold    = color.New(color.Bold).SprintfFunc()

	out io.Writer
)

// Tool is to execute batch tasks using kubectl command.
type Tool struct {
	kubectl   Kubectl
	yes       bool
	force     bool
	interval  int
	minStable float64
}

func init() {
	out = os.Stdout
}

// SetNamespace to define target namespace.
func (t *Tool) SetNamespace(namespace string) {
	t.kubectl.Namespace = namespace
}

// SetYes to skip confirmation.
func (t *Tool) SetYes(yes bool) {
	t.yes = yes
}

// SetForce to reload without status checking.
func (t *Tool) SetForce(force bool) {
	t.force = force
}

// SetInterval seconds between each pod restarts.
func (t *Tool) SetInterval(interval int) {
	t.interval = interval
}

// SetMinimumStable available pods rate to detect RC availability.
func (t *Tool) SetMinimumStable(minStable float64) {
	t.minStable = minStable
}

// PrintInfo writes version of target cluster.
func (t *Tool) PrintInfo() (err error) {
	c, s, err := t.kubectl.Version()
	if err != nil {
		return
	}
	t.PrintContext()
	log("client version :", green(c))
	log("server version :", blue(s))
	return
}

// PrintContext writes current active context.
func (t *Tool) PrintContext() (err error) {
	cc, err := t.kubectl.CurrentContext()
	if err != nil {
		return
	}
	log("context ->", yellow(cc))
	return
}

// PrintPodList print images of running pods in specific RC.
func (t *Tool) PrintPodList(rcname string) (err error) {
	var selector Selector
	if rcname != "" {
		selector = Selector{"name": rcname}
	}

	pods, err := t.kubectl.PodList(selector)
	if err != nil {
		return
	}
	w := goterm.NewTable(0, 4, 1, ' ', 0)
	fmt.Fprintf(w, "NAME\tSTATUS\tR\tPOD IP\tNODE IP\tIMAGE\tVERSION\n")
	for _, pod := range pods {
		for i, container := range pod.Spec.Containers {
			cstate := pod.Status.ContainerStatuses[i].State
			status := "Running"
			if cstate.Terminated != nil {
				status = "Terminated"
			} else if cstate.Waiting != nil {
				status = "Waiting"
			}
			img, ver := parseImage(container.Image)
			fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\n",
				pod.Name,
				status,
				pod.Status.ContainerStatuses[i].RestartCount,
				pod.Status.PodIP,
				pod.Status.HostIP,
				img, ver,
			)
		}
	}
	fmt.Println(w.String())
	return
}

// PrintRCList print images of running RCs.
func (t *Tool) PrintRCList() (err error) {
	rcs, err := t.kubectl.RCList()
	if err != nil {
		return
	}
	w := goterm.NewTable(0, 4, 1, ' ', 0)
	fmt.Fprintf(w, "NAME\tREPLICAS\tIMAGE\tVERSION\n")
	for _, rc := range rcs {
		rcSpec := rc.Spec
		if rcSpec.Template == nil {
			continue
		}
		podSpec := rcSpec.Template.Spec
		for _, container := range podSpec.Containers {
			img, ver := parseImage(container.Image)
			fmt.Fprintf(w, "%s\t%d/%d\t%s\t%s\n",
				rc.Name, rc.Status.Replicas, *rcSpec.Replicas, img, ver,
			)
		}
	}
	fmt.Println(w.String())
	//goterm.Println(w)
	return
}

// Reload all or one pod(s) in single rc.
func (t *Tool) Reload(name string, one bool) (err error) {
	if one {
		log("reloading " + magenta("1") + " pod in replication controller.")
	} else {
		log("reloading " + red("all") + " pods in replication controller.")
	}
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	pods, err := t.kubectl.PodList(rc.Spec.Selector)
	if err != nil {
		return
	}
	if len(pods) == 0 {
		err = errors.New("no pod found")
		return
	}
	t.PrintContext()
	log("rc      :", blue(name))

	// first pod only
	if one {
		pods = pods[:1]
	}

	for i := range pods {
		if t.podAvailable(pods[i]) {
			logf("pod[%03d]: %s", i, green(pods[i].Name))
		} else {
			logf("pod[%03d]: %s", i, red(pods[i].Name))
		}
	}
	t.confirm("continue?")
	// do reload
	err = t.reloadPods(rc, pods)
	return
}

// Update RC image version to specific value.
func (t *Tool) Update(name string, container string, version string) (err error) {
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	c, err := pickContainer(rc, container)
	if err != nil {
		return
	}
	img, ver := parseImage(c.Image)
	t.PrintContext()
	log("RC       :", green(name))
	log("Container:", green(name))

	if version == "" {
		version, err = t.selectVersion(rc, container)
		if err != nil {
			return
		}
	}

	log("Image    :", magenta(img)+":"+yellow(ver))
	log("       ->:", magenta(img)+":"+bold(yellow(version)))
	t.confirm("continue?")

	newImage := img + ":" + version

	//t.kubectl.Patch
	patch := fmt.Sprintf(`{"spec":{"template":{"spec":{"containers":[{"name":"%s","image":"%s"}]}}}}`, c.Name, newImage)
	if err = t.kubectl.PatchRC(rc.Name, patch); err != nil {
		return
	}
	log(green("Successfully patched"))
	return
}

func (t *Tool) selectVersion(rc ReplicationController, container string) (version string, err error) {
	pods, err := t.kubectl.PodList(Selector{"name": rc.Name})
	if err != nil {
		return
	}
	vermap := map[string]int{}
	for i := range pods {
		c, err := pickPodContainer(pods[i], container)
		if err != nil {
			return version, err
		}
		_, ver := parseImage(c.Image)
		vermap[ver]++
	}
	vers := make([]string, 0, len(vermap))
	for k := range vermap {
		vers = append(vers, k)
	}
	sort.Strings(vers)
	log("Current versions")
	for i, ver := range vers {
		logf("[%s] - %s", blue("%d", i), yellow(ver))
	}

	fmt.Print("choose or input version: ")
	res := ""
	fmt.Scanf("%s", &res)
	// if number set, use version from list
	if regexp.MustCompile("^[0-9]+$").MatchString(res) {
		idx, err := strconv.Atoi(res)
		if err != nil {
			return "", err
		}
		if idx < 0 || idx >= len(vers) {
			err = errors.New("version index out of range")
			return "", err
		}
		return vers[idx], nil
	}
	return strings.Trim(res, " \n\t"), nil
}

// FixVersion of pods running on RC with destroying all pods that has
// different version of RC ones.
func (t *Tool) FixVersion(name string) (err error) {
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	allPods, err := t.kubectl.PodList(rc.Spec.Selector)
	if err != nil {
		return
	}
	pods := []Pod{}
	rspec := rc.Spec.Template.Spec
	for _, pod := range allPods {
		// when containers has different size, move on
		if len(pod.Spec.Containers) != len(rspec.Containers) {
			pods = append(pods, pod)
			continue
		}
		for i, cs := range pod.Spec.Containers {
			if cs.Image != rspec.Containers[i].Image {
				pods = append(pods, pod)
				break
			}
		}
	}

	if len(pods) == 0 {
		log(green("all pods are up to date."))
		return
	}

	t.PrintContext()
	log("rc      :", blue(name))

	for i := range pods {
		if t.podAvailable(pods[i]) {
			logf("pod[%03d]: %s", i, green(pods[i].Name))
		} else {
			logf("pod[%03d]: %s %s", i, red(pods[i].Name), gray("(unavailable)"))
		}
	}
	t.confirm("continue?")
	// do reload
	err = t.reloadPods(rc, pods)
	return
}

// reloadPods deletes pods one by one with waiting created pod become available.
func (t *Tool) reloadPods(rc ReplicationController, pods []Pod) (err error) {

	livePods := make([]Pod, 0, len(pods))
	deadPods := make([]Pod, 0, len(pods))

	deletedPods := make([]string, 0, len(pods))

	// separate dead/live pods<commit_msg>Show RC images at the time of execution reload, fix-version commands (#2)<commit_after>package kube

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

	"github.com/buger/goterm"
	"github.com/fatih/color"
)

var (
	red     = color.New(color.FgRed).SprintfFunc()
	blue    = color.New(color.FgBlue).SprintfFunc()
	green   = color.New(color.FgGreen).SprintfFunc()
	yellow  = color.New(color.FgYellow).SprintfFunc()
	magenta = color.New(color.FgMagenta).SprintfFunc()
	cyan    = color.New(color.FgCyan).SprintfFunc()
	gray    = color.New(color.FgHiBlack).SprintfFunc()
	white   = color.New(color.FgWhite).SprintfFunc()
	bold    = color.New(color.Bold).SprintfFunc()

	out io.Writer
)

// Tool is to execute batch tasks using kubectl command.
type Tool struct {
	kubectl   Kubectl
	yes       bool
	force     bool
	interval  int
	minStable float64
}

func init() {
	out = os.Stdout
}

// SetNamespace to define target namespace.
func (t *Tool) SetNamespace(namespace string) {
	t.kubectl.Namespace = namespace
}

// SetYes to skip confirmation.
func (t *Tool) SetYes(yes bool) {
	t.yes = yes
}

// SetForce to reload without status checking.
func (t *Tool) SetForce(force bool) {
	t.force = force
}

// SetInterval seconds between each pod restarts.
func (t *Tool) SetInterval(interval int) {
	t.interval = interval
}

// SetMinimumStable available pods rate to detect RC availability.
func (t *Tool) SetMinimumStable(minStable float64) {
	t.minStable = minStable
}

// PrintInfo writes version of target cluster.
func (t *Tool) PrintInfo() (err error) {
	c, s, err := t.kubectl.Version()
	if err != nil {
		return
	}
	t.PrintContext()
	log("client version :", green(c))
	log("server version :", blue(s))
	return
}

// PrintContext writes current active context.
func (t *Tool) PrintContext() (err error) {
	cc, err := t.kubectl.CurrentContext()
	if err != nil {
		return
	}
	log("context ->", yellow(cc))
	return
}

// PrintPodList print images of running pods in specific RC.
func (t *Tool) PrintPodList(rcname string) (err error) {
	var selector Selector
	if rcname != "" {
		selector = Selector{"name": rcname}
	}

	pods, err := t.kubectl.PodList(selector)
	if err != nil {
		return
	}
	w := goterm.NewTable(0, 4, 1, ' ', 0)
	fmt.Fprintf(w, "NAME\tSTATUS\tR\tPOD IP\tNODE IP\tIMAGE\tVERSION\n")
	for _, pod := range pods {
		for i, container := range pod.Spec.Containers {
			cstate := pod.Status.ContainerStatuses[i].State
			status := "Running"
			if cstate.Terminated != nil {
				status = "Terminated"
			} else if cstate.Waiting != nil {
				status = "Waiting"
			}
			img, ver := parseImage(container.Image)
			fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\n",
				pod.Name,
				status,
				pod.Status.ContainerStatuses[i].RestartCount,
				pod.Status.PodIP,
				pod.Status.HostIP,
				img, ver,
			)
		}
	}
	fmt.Println(w.String())
	return
}

// PrintRCList print images of running RCs.
func (t *Tool) PrintRCList() (err error) {
	rcs, err := t.kubectl.RCList()
	if err != nil {
		return
	}
	w := goterm.NewTable(0, 4, 1, ' ', 0)
	fmt.Fprintf(w, "NAME\tREPLICAS\tIMAGE\tVERSION\n")
	for _, rc := range rcs {
		rcSpec := rc.Spec
		if rcSpec.Template == nil {
			continue
		}
		podSpec := rcSpec.Template.Spec
		for _, container := range podSpec.Containers {
			img, ver := parseImage(container.Image)
			fmt.Fprintf(w, "%s\t%d/%d\t%s\t%s\n",
				rc.Name, rc.Status.Replicas, *rcSpec.Replicas, img, ver,
			)
		}
	}
	fmt.Println(w.String())
	//goterm.Println(w)
	return
}

// Reload all or one pod(s) in single rc.
func (t *Tool) Reload(name string, one bool) (err error) {
	if one {
		log("reloading " + magenta("1") + " pod in replication controller.")
	} else {
		log("reloading " + red("all") + " pods in replication controller.")
	}
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	pods, err := t.kubectl.PodList(rc.Spec.Selector)
	if err != nil {
		return
	}
	if len(pods) == 0 {
		err = errors.New("no pod found")
		return
	}
	t.PrintContext()
	log("rc      :", blue(name))
	rspec := rc.Spec.Template.Spec
	for i := range rspec.Containers {
		if i == 0 {
			log("image(s):", blue(rspec.Containers[i].Image))
			continue
		}
		log("         ", blue(rspec.Containers[i].Image))
	}

	// first pod only
	if one {
		pods = pods[:1]
	}

	for i := range pods {
		if t.podAvailable(pods[i]) {
			logf("pod[%03d]: %s", i, green(pods[i].Name))
		} else {
			logf("pod[%03d]: %s", i, red(pods[i].Name))
		}
	}
	t.confirm("continue?")
	// do reload
	err = t.reloadPods(rc, pods)
	return
}

// Update RC image version to specific value.
func (t *Tool) Update(name string, container string, version string) (err error) {
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	c, err := pickContainer(rc, container)
	if err != nil {
		return
	}
	img, ver := parseImage(c.Image)
	t.PrintContext()
	log("RC       :", green(name))
	log("Container:", green(name))

	if version == "" {
		version, err = t.selectVersion(rc, container)
		if err != nil {
			return
		}
	}

	log("Image    :", magenta(img)+":"+yellow(ver))
	log("       ->:", magenta(img)+":"+bold(yellow(version)))
	t.confirm("continue?")

	newImage := img + ":" + version

	//t.kubectl.Patch
	patch := fmt.Sprintf(`{"spec":{"template":{"spec":{"containers":[{"name":"%s","image":"%s"}]}}}}`, c.Name, newImage)
	if err = t.kubectl.PatchRC(rc.Name, patch); err != nil {
		return
	}
	log(green("Successfully patched"))
	return
}

func (t *Tool) selectVersion(rc ReplicationController, container string) (version string, err error) {
	pods, err := t.kubectl.PodList(Selector{"name": rc.Name})
	if err != nil {
		return
	}
	vermap := map[string]int{}
	for i := range pods {
		c, err := pickPodContainer(pods[i], container)
		if err != nil {
			return version, err
		}
		_, ver := parseImage(c.Image)
		vermap[ver]++
	}
	vers := make([]string, 0, len(vermap))
	for k := range vermap {
		vers = append(vers, k)
	}
	sort.Strings(vers)
	log("Current versions")
	for i, ver := range vers {
		logf("[%s] - %s", blue("%d", i), yellow(ver))
	}

	fmt.Print("choose or input version: ")
	res := ""
	fmt.Scanf("%s", &res)
	// if number set, use version from list
	if regexp.MustCompile("^[0-9]+$").MatchString(res) {
		idx, err := strconv.Atoi(res)
		if err != nil {
			return "", err
		}
		if idx < 0 || idx >= len(vers) {
			err = errors.New("version index out of range")
			return "", err
		}
		return vers[idx], nil
	}
	return strings.Trim(res, " \n\t"), nil
}

// FixVersion of pods running on RC with destroying all pods that has
// different version of RC ones.
func (t *Tool) FixVersion(name string) (err error) {
	rc, err := t.kubectl.RC(name)
	if err != nil {
		return
	}
	allPods, err := t.kubectl.PodList(rc.Spec.Selector)
	if err != nil {
		return
	}
	pods := []Pod{}
	rspec := rc.Spec.Template.Spec
	for _, pod := range allPods {
		// when containers has different size, move on
		if len(pod.Spec.Containers) != len(rspec.Containers) {
			pods = append(pods, pod)
			continue
		}
		for i, cs := range pod.Spec.Containers {
			if cs.Image != rspec.Containers[i].Image {
				pods = append(pods, pod)
				break
			}
		}
	}

	if len(pods) == 0 {
		log(green("all pods are up to date."))
		return
	}

	t.PrintContext()
	log("rc      :", blue(name))
	for i := range rspec.Containers {
		if i == 0 {
			log("image(s):", blue(rspec.Containers[i].Image))
			continue
		}
		log("         ", blue(rspec.Containers[i].Image))
	}

	for i := range pods {
		if t.podAvailable(pods[i]) {
			logf("pod[%03d]: %s", i, green(pods[i].Name))
		} else {
			logf("pod[%03d]: %s %s", i, red(pods[i].Name), gray("(unavailable)"))
		}
	}
	t.confirm("continue?")
	// do reload
	err = t.reloadPods(rc, pods)
	return
}

// reloadPods deletes pods one by one with waiting created pod become available.
func (t *Tool) reloadPods(rc ReplicationController, pods []Pod) (err error) {

	livePods := make([]Pod, 0, len(pods))
	deadPods := make([]Pod, 0, len(pods))

	deletedPods := make([]string, 0, len(pods))

	// separate dead/live pods
{'fraction_non_alphanumeric': 0.16977012970335173, 'fraction_numerical': 0.004173622704507512, 'mean_word_length': 3.397233201581028, 'pattern_counts': {'":': 22, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6463310', 'n_tokens_mistral': 6397, 'n_tokens_neox': 5955, 'n_words': 1472}