<commit_before>package engine

import (
	"fmt"
	"time"

	"github.com/laincloud/deployd/cluster"
	"github.com/laincloud/deployd/storage"
	"github.com/mijia/adoc"
	"github.com/mijia/go-generics"
	"github.com/mijia/sweb/log"
)

type pgOperation interface {
	Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool
}

type pgOperSaveStore struct {
	force bool
}

type pgOperCacheLastSpec struct {
	spec PodGroupSpec
}

func (op pgOperCacheLastSpec) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s save last pod spec info, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if err := store.SetWithTTL(pgCtrl.lastPodSpecKey, op.spec, DefaultLastSpecCacheTTL, true); err != nil {
		log.Warnf("[Store] Failed to save last pod spec %s, %s", pgCtrl.lastPodSpecKey, err)
		_err = err
	}
	return false
}

func (op pgOperSaveStore) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s save, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	pg := pgCtrl.Inspect()
	if pgCtrl.IsHealthy() {
		if err := store.Set(pgCtrl.storedKey, pg, op.force); err != nil {
			log.Warnf("[Store] Failed to save pod group %s, %s", pgCtrl.storedKey, err)
			_err = err
		}
	} else {
		log.Warnf("Some pods lost IP, will not save pod group %s", pgCtrl.storedKey)
	}

	return false
}

type pgOperRemoveStore struct{}

func (op pgOperRemoveStore) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s remove, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if err := store.Remove(pgCtrl.storedKey); err != nil {
		_err = err
	} else {
		store.TryRemoveDir(pgCtrl.storedKeyDir)
	}
	return false
}

type pgOperSnapshotEagleView struct {
	pgName string
}

func (op pgOperSnapshotEagleView) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s snapshot the swarm containers, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if pods, err := ev.RefreshPodGroup(c, op.pgName); err != nil {
		_err = err
	} else {
		snapshot := make(map[string]RuntimeEaglePod)
		for _, p := range pods {
			snapshot[p.Container.Id] = p
		}
		pgCtrl.evSnapshot = snapshot
		pgCtrl.cleanCorruptedContainers()
	}
	return false
}

type pgOperUpgradeInstance struct {
	instanceNo int
	version    int
	oldPodSpec PodSpec
	newPodSpec PodSpec
}

func (op pgOperUpgradeInstance) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s upgrade instance, iNo=%d, version=%d, duration=%s", pgCtrl, op.instanceNo, op.version, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()

	podCtrl := pgCtrl.podCtrls[op.instanceNo-1]
	newPodSpec := op.newPodSpec.Clone()
	newPodSpec.PrevState = podCtrl.spec.PrevState.Clone() // upgrade action, state should not changed
	prevNodeName := newPodSpec.PrevState.NodeName

	isLastPodHealthy := pgCtrl.waitLastPodHealth(op.instanceNo)
	if !isLastPodHealthy && op.instanceNo == 2 && pgCtrl.rollBack() {
		return false
	}
	log.Infof("upgrade instance : %d !", op.instanceNo)
	var lowOp pgOperation
	lowOp = pgOperRemoveInstance{op.instanceNo, op.oldPodSpec}
	lowOp.Do(pgCtrl, c, store, ev)
	time.Sleep(10 * time.Second)

	// FIXME: do we need to consider hard state flag on upgrade
	if op.oldPodSpec.IsStateful() && newPodSpec.IsStateful() && prevNodeName != "" {
		newPodSpec.Filters = append(newPodSpec.Filters, fmt.Sprintf("constraint:node==%s", prevNodeName))
	}
	podCtrl.spec = newPodSpec
	podCtrl.pod.State = RunStatePending
	podCtrl.pod.RestartCount = 0
	lowOp = pgOperDeployInstance{op.instanceNo, op.version}
	lowOp.Do(pgCtrl, c, store, ev)
	return false
}

type pgOperRefreshInstance struct {
	instanceNo int
	spec       PodGroupSpec
}

func (op pgOperRefreshInstance) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var runtime ImRuntime
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s refresh instance, iNo=%d, runtime=%+v, duration=%s", pgCtrl, op.instanceNo, runtime, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()

	podCtrl := pgCtrl.podCtrls[op.instanceNo-1]

	podCtrl.Refresh(c)
	runtime = podCtrl.pod.ImRuntime<commit_msg>Fixed bug for refresh when pod is shrinked<commit_after>package engine

import (
	"fmt"
	"time"

	"github.com/laincloud/deployd/cluster"
	"github.com/laincloud/deployd/storage"
	"github.com/mijia/adoc"
	"github.com/mijia/go-generics"
	"github.com/mijia/sweb/log"
)

type pgOperation interface {
	Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool
}

type pgOperSaveStore struct {
	force bool
}

type pgOperCacheLastSpec struct {
	spec PodGroupSpec
}

func (op pgOperCacheLastSpec) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s save last pod spec info, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if err := store.SetWithTTL(pgCtrl.lastPodSpecKey, op.spec, DefaultLastSpecCacheTTL, true); err != nil {
		log.Warnf("[Store] Failed to save last pod spec %s, %s", pgCtrl.lastPodSpecKey, err)
		_err = err
	}
	return false
}

func (op pgOperSaveStore) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s save, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	pg := pgCtrl.Inspect()
	if pgCtrl.IsHealthy() {
		if err := store.Set(pgCtrl.storedKey, pg, op.force); err != nil {
			log.Warnf("[Store] Failed to save pod group %s, %s", pgCtrl.storedKey, err)
			_err = err
		}
	} else {
		log.Warnf("Some pods lost IP, will not save pod group %s", pgCtrl.storedKey)
	}

	return false
}

type pgOperRemoveStore struct{}

func (op pgOperRemoveStore) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s remove, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if err := store.Remove(pgCtrl.storedKey); err != nil {
		_err = err
	} else {
		store.TryRemoveDir(pgCtrl.storedKeyDir)
	}
	return false
}

type pgOperSnapshotEagleView struct {
	pgName string
}

func (op pgOperSnapshotEagleView) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var _err error
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s snapshot the swarm containers, op=%+v, err=%v, duration=%s", pgCtrl, op, _err, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()
	if pods, err := ev.RefreshPodGroup(c, op.pgName); err != nil {
		_err = err
	} else {
		snapshot := make(map[string]RuntimeEaglePod)
		for _, p := range pods {
			snapshot[p.Container.Id] = p
		}
		pgCtrl.evSnapshot = snapshot
		pgCtrl.cleanCorruptedContainers()
	}
	return false
}

type pgOperUpgradeInstance struct {
	instanceNo int
	version    int
	oldPodSpec PodSpec
	newPodSpec PodSpec
}

func (op pgOperUpgradeInstance) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s upgrade instance, iNo=%d, version=%d, duration=%s", pgCtrl, op.instanceNo, op.version, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()

	podCtrl := pgCtrl.podCtrls[op.instanceNo-1]
	newPodSpec := op.newPodSpec.Clone()
	newPodSpec.PrevState = podCtrl.spec.PrevState.Clone() // upgrade action, state should not changed
	prevNodeName := newPodSpec.PrevState.NodeName

	isLastPodHealthy := pgCtrl.waitLastPodHealth(op.instanceNo)
	if !isLastPodHealthy && op.instanceNo == 2 && pgCtrl.rollBack() {
		return false
	}
	log.Infof("upgrade instance : %d !", op.instanceNo)
	var lowOp pgOperation
	lowOp = pgOperRemoveInstance{op.instanceNo, op.oldPodSpec}
	lowOp.Do(pgCtrl, c, store, ev)
	time.Sleep(10 * time.Second)

	// FIXME: do we need to consider hard state flag on upgrade
	if op.oldPodSpec.IsStateful() && newPodSpec.IsStateful() && prevNodeName != "" {
		newPodSpec.Filters = append(newPodSpec.Filters, fmt.Sprintf("constraint:node==%s", prevNodeName))
	}
	podCtrl.spec = newPodSpec
	podCtrl.pod.State = RunStatePending
	podCtrl.pod.RestartCount = 0
	lowOp = pgOperDeployInstance{op.instanceNo, op.version}
	lowOp.Do(pgCtrl, c, store, ev)
	return false
}

type pgOperRefreshInstance struct {
	instanceNo int
	spec       PodGroupSpec
}

func (op pgOperRefreshInstance) Do(pgCtrl *podGroupController, c cluster.Cluster, store storage.Store, ev *RuntimeEagleView) bool {
	var runtime ImRuntime
	start := time.Now()
	defer func() {
		pgCtrl.RLock()
		log.Infof("%s refresh instance, iNo=%d, runtime=%+v, duration=%s", pgCtrl, op.instanceNo, runtime, time.Now().Sub(start))
		pgCtrl.RUnlock()
	}()

	if(op.instanceNo > len(pgCtrl.podCtrls)){
		log.Warnf("Pod is not exists")
		return false
	}
	podCtrl := pgCtrl.podCtrls[op.instanceNo-1]

	podCtrl.Refresh(c)
	runtime = podCtrl.pod.ImRuntime
{'fraction_non_alphanumeric': 0.138506976270496, 'fraction_numerical': 0.0012221203788573174, 'mean_word_length': 4.686160972785177, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, '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': '5486925', 'n_tokens_mistral': 3652, 'n_tokens_neox': 3553, 'n_words': 846}