package main

import (
	"bytes"
	"context"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

	pb "tiflash-auto-scaling/supervisor_proto"
	"tiflash-auto-scaling/tiflashrpc"

	"github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/kvproto/pkg/mpp"
	"github.com/pingcap/kvproto/pkg/tikvpb"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/mitchellh/go-ps"
	"github.com/prometheus/common/config"
	"github.com/prometheus/common/expfmt"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8stypes "k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	AssignTenantID   atomic.Value
	AssignTiflashVer atomic.Value
	StartTime        atomic.Int64
	IsUnassigning    atomic.Bool // if IsUnassigning is true, it should be false in a few seconds
	IsStopping       atomic.Bool
	MuOfTenantInfo   sync.Mutex // protect startTime, IsUnassigning and assignTenantID

	Pid                       atomic.Int32
	ReqId                     atomic.Int32
	MuOfSupervisor            sync.Mutex
	AssignCh                  = make(chan *pb.AssignRequest, 10000)
	LabelPatchCh              = make(chan string, 10000)
	PdAddr                    string
	TimeoutArrOfK8sLabelPatch = []int{1, 2, 4, 8, 10}

	// TiFlashBinPath   = "./bin/tiflash"
	IsTestEnv = false

	PathOfTiflashData      = "/tiflash/data"
	PathOfTiflashCache     = "/tiflash/cache"
	CapicityOfTiflashCache = "10737418240"
	RunMode                = ""
)

const (
	RunModeServeless = iota
	RunModeLocal
	RunModeDedicated
	RunModeCustom
	RunModeTest
)

var OptionRunMode = RunModeLocal

const NeedPd = false

var S3BucketForTiFLashLog = ""
var S3Mutex sync.Mutex
var LocalPodIp string
var LocalPodName string
var CheckTiFlashIdleInitSleepSec = 10
var CheckTiFlashIdleInterval = 1
var CheckTiFlashIdleTimeout = 60
var K8sCli *kubernetes.Clientset
var CAPath string
var CertPath string
var KeyPath string

var tlsConfig *tls.Config

const CheckTiflashIdleTimeoutEnv = "CHECK_TIFLASH_IDLE_TIMEOUT"
const CheckTiflashIdleIntervalEnv = "CHECK_TIFLASH_IDLE_INTERVAL"
const TiFlashMetricTaskPrefix = "tiflash_coprocessor_handling_request_count"
const HTTPTimeout = 5 * time.Second

const (
	allinterfaces              = "0.0.0.0"
	localhost                  = "127.0.0.1"
	tiflashLocalGRPCAddr       = "127.0.0.1:3930"
	tiflashLocalMetricsBaseURL = "127.0.0.1:8234/metrics"
)

func setTenantInfo(tenantID string, isUnassigning bool, ver string) int64 {
	MuOfTenantInfo.Lock()
	defer MuOfTenantInfo.Unlock()
	AssignTenantID.Store(tenantID)
	AssignTiflashVer.Store(ver)
	stime := time.Now().Unix()
	StartTime.Store(stime)
	IsUnassigning.Store(isUnassigning)
	return stime
}

func updateTenantInfoIsUnassigning(isUnassigning bool) int64 {
	MuOfTenantInfo.Lock()
	defer MuOfTenantInfo.Unlock()
	stime := time.Now().Unix() // TODO remove?
	StartTime.Store(stime)     // TODO remove?
	IsUnassigning.Store(isUnassigning)
	return stime
}

func getTenantInfo() (string, int64, bool, string) {
	MuOfTenantInfo.Lock()
	defer MuOfTenantInfo.Unlock()
	return AssignTenantID.Load().(string), StartTime.Load(), IsUnassigning.Load(), AssignTiflashVer.Load().(string)
}

func GetTiFlashTaskNum() (int, error) {
	client := http.Client{
		Timeout: HTTPTimeout,
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	}
	scheme := "http"
	if tlsConfig != nil {
		scheme = "https"
	}
	resp, err := client.Get(fmt.Sprintf("%s://%s", scheme, tiflashLocalMetricsBaseURL))
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return 0, errors.New("http status code is not 200")
	}
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	res, err := GetTiFlashTaskNumByMetricsByte(data)
	if err != nil {
		return 0, err
	}
	return res, nil
}

func GetTiFlashTaskNumByMetricsByte(data []byte) (int, error) {
	res := 0
	reader := bytes.NewReader(data)
	var parser expfmt.TextParser
	metricFamilies, err := parser.TextToMetricFamilies(reader)
	if err != nil {
		return 0, err
	}
	for _, v := range metricFamilies {
		if strings.HasPrefix(*v.Name, TiFlashMetricTaskPrefix) {
			for _, m := range v.Metric {
				res += int(*m.Gauge.Value)
			}
			break
		}

	}
	return res, nil
}

func CheckTiFlashAlive() (bool, error) {
	//var callOptions []grpc.CallOption
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	var trsec grpc.DialOption
	if tlsConfig != nil {
		trsec = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
	} else {
		trsec = grpc.WithInsecure()
	}

	conn, err := grpc.DialContext(
		ctx,
		tiflashLocalGRPCAddr,
		trsec, grpc.WithConnectParams(grpc.ConnectParams{
			MinConnectTimeout: 100 * time.Millisecond,
		}))
	if err != nil || conn == nil {
		return false, err
	}
	defer func(conn *grpc.ClientConn) {
		err := conn.Close()
		if err != nil {
			log.Printf("[error]close grpc connection failed: %v\n", err.Error())
		}
	}(conn)
	resp := &tiflashrpc.Response{}
	client := tikvpb.NewTikvClient(conn)
	req := &tiflashrpc.Request{
		Type:    tiflashrpc.CmdMPPAlive,
		StoreTp: tiflashrpc.TiFlash,
		Req:     &mpp.IsAliveRequest{},
		Context: kvrpcpb.Context{},
	}
	resp.Resp, err = client.IsAlive(context.Background(), req.IsMPPAlive())
	if resp.Resp == nil || err != nil {
		return false, err
	}
	return resp.Resp.(*mpp.IsAliveResponse).Available, err
}

// return true if tiflash process exists.
func checkTiFlashProcessExists(pid string) (bool, error) {
	intPid, err := strconv.Atoi(pid)
	if err != nil {
		return false, err
	}
	p, err := ps.FindProcess(intPid)
	if err != nil {
		return false, err
	}
	if p == nil {
		return false, nil
	}
	// Same pid, but it's not tiflash process.
	if !strings.Contains(strings.ToLower(p.Executable()), "tiflash") {
		return false, nil
	}
	return true, nil
}

func SetupSigHandler() {
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	go func() {
		sig := <-signalCh
		log.Printf("supervisor got signal: %v, killing everything\n", sig)
		IsStopping.Store(true)
		AssignCh <- nil
	}()
	log.Printf("supervisor signal handler setup")
}

func AssignTenantService(req *pb.AssignRequest) (*pb.Result, error) {
	if IsStopping.Load() {
		realTID, stimeOfAssign, isUnassigning, ver := getTenantInfo()
		log.Printf("ignore assign rpc because supervisor is stopping")
		return &pb.Result{HasErr: true, NeedUpdateStateIfErr: true, ErrInfo: "Supervisor is stopping", TenantID: realTID, StartTime: stimeOfAssign, IsUnassigning: isUnassigning, TiflashVer: ver}, nil
	}
	curReqId := ReqId.Add(1)
	log.Printf("received assign request by: %v reqid: %v tiflash_ver: %v\n", req.GetTenantID(), curReqId, req.TiflashVer)
	var errInfo string
	defer log.Printf("finished assign request by: %v reqid: %v\n", req.GetTenantID(), curReqId)
	if AssignTenantID.Load() == nil || AssignTenantID.Load().(string) == "" {
		if MuOfSupervisor.TryLock() {
			defer MuOfSupervisor.Unlock()
			if AssignTenantID.Load() == nil || AssignTenantID.Load().(string) == "" {
				stime := setTenantInfo(req.GetTenantID(), false, req.GetTiflashVer())
				LabelPatchCh <- req.GetTenantID()
				configFile := fmt.Sprintf("conf/tiflash-tenant-%s.toml", req.GetTenantID())
				PdAddr = req.GetPdAddr()
				if req.GetTiflashVer() != "" {
					InitTiFlashConf(allinterfaces, req.GetTiflashVer(), CAPath, CertPath, KeyPath)
				}
				err := RenderTiFlashConf(
					configFile,
					req.GetTidbStatusAddr(),
					req.GetPdAddr(),
					req.GetTenantID(),
					req.GetTiflashVer(),
					CAPath,
					CertPath,
					KeyPath,
				)
				if err != nil {
					//rollback
					setTenantInfo("", false, "")
					LabelPatchCh <- "null"
					return &pb.Result{HasErr: true, NeedUpdateStateIfErr: true, ErrInfo: "could not render config", TenantID: "", StartTime: stime, IsUnassigning: false}, err
				}
				AssignCh <- req
				for Pid.Load() == 0 {
					time.Sleep(10 * time.Microsecond)
				}
				localSt := time.Now()
				MaxWaitPortOpenTimeSec := 40 * time.Second
				isTimeout := false
				isTiFlashAlive, err := CheckTiFlashAlive()
				for !isTiFlashAlive && !IsTestEnv {
					time.Sleep(100 * time.Microsecond)
					if time.Since(localSt) >= MaxWaitPortOpenTimeSec {
						isTimeout = true
						break
					}
					isTiFlashAlive, err = CheckTiFlashAlive()
				}
				if isTimeout {
					errMsg := fmt.Sprintf("tiflash not ready for %v", MaxWaitPortOpenTimeSec)
					log.Printf("%s. err: %v", errMsg, err)
					return &pb.Result{HasErr: true, ErrInfo: errMsg, TenantID: AssignTenantID.Load().(string), StartTime: stime, IsUnassigning: false}, nil
				}
				return &pb.Result{HasErr: false, ErrInfo: "", TenantID: AssignTenantID.Load().(string), StartTime: stime, IsUnassigning: false}, nil
			}
		} else {
			errInfo = "TryLock failed"
		}
	} else if AssignTenantID.Load().(string) == req.GetTenantID() {
		realTID, stimeOfAssign, isUnassigning, ver := getTenantInfo()
		return &pb.Result{HasErr: false, ErrInfo: "", TenantID: realTID, StartTime: stimeOfAssign, IsUnassigning: isUnassigning, TiflashVer: ver}, nil
	} else {
		errInfo = "TiFlash has been occupied by a tenant"
	}
	realTID, stimeOfAssign, isUnassigning, ver := getTenantInfo()
	log.Printf("[error][assign]%v realTID: %v wantTID: %v\n", errInfo, realTID, req.TenantID)
	return &pb.Result{HasErr: true, NeedUpdateStateIfErr: true, ErrInfo: "TiFlash has been occupied by a tenant", TenantID: realTID, StartTime: stimeOfAssign, IsUnassigning: isUnassigning, TiflashVer: ver}, nil
}

func UnassignTenantService(req *pb.UnassignRequest) (*pb.Result, error) {
	if IsStopping.Load() {
		realTID, stimeOfAssign, isUnassigning, ver := getTenantInfo()
		log.Printf("ignore unassign rpc because supervisor is stopping")
		return &pb.Result{HasErr: true, NeedUpdateStateIfErr: false, ErrInfo: "Supervisor is stopping", TenantID: realTID, StartTime: stimeOfAssign, IsUnassigning: isUnassigning, TiflashVer: ver}, nil
	}
	curReqId := ReqId.Add(1)
	log.Printf("received unassign request by: %v reqid: %v\n", req.GetAssertTenantID(), curReqId)
	defer log.Printf("finished unassign request by: %v reqid: %v\n", req.GetAssertTenantID(), curReqId)
	var errInfo string
	if req.AssertTenantID == AssignTenantID.Load().(string) {
		if MuOfSupervisor.TryLock() {
			defer MuOfSupervisor.Unlock()
			if req.AssertTenantID == AssignTenantID.Load().(string) && Pid.Load() != 0 {
				TryToUploadTiFlashLogIntoS3(false)
				if NeedPd {
					err := PdCtlNotifyPDForExit()
					if err != nil {
						return &pb.Result{HasErr: true, ErrInfo: err.Error(), TenantID: AssignTenantID.Load().(string)}, err
					}

					go func() {
						log.Printf("[UnassignTenantService]RemoveStoreIDsOfUnhealthRNs \n")
						err = PdCtlRemoveStoreIDsOfUnhealthRNs()
						if err != nil {
							log.Printf("[error]Remove StoreIDs Of Unhealth RNs fail: %v\n", err.Error())
						}
					}()
				}
				if !req.ForceShutdown {
					updateTenantInfoIsUnassigning(true)
					log.Printf("[unassigning]wait tiflash to shutdown gracefully\n")
					startTime := time.Now()
					time.Sleep(time.Duration(CheckTiFlashIdleInitSleepSec) * time.Second) // sleep a few seconds to prevent new mpp tasks arrive shortly after the begining of unassigning,  but  the tiflash is idle at the begining of unassigning
					for time.Now().Sub(startTime).Seconds() < float64(CheckTiFlashIdleTimeout) {
						if IsTestEnv {
							log.Printf("[test][unassigning]tiflash has no task, shutdown\n")
							break
						}
						taskNum, err := GetTiFlashTaskNum()
						if err != nil {
							log.Printf("[error]GetTiFlashTaskNum fail: %v\n", err.Error())
							break
						}
						if taskNum == 0 {
							log.Printf("[unassigning]tiflash has no task, shutdown\n")
							break
						}
						log.Printf("[unassigning]tiflash has %v task, wait for %v seconds\n", taskNum, CheckTiFlashIdleTimeout)
						time.Sleep(time.Duration(CheckTiFlashIdleInterval) * time.Second)
					}
				}

				setTenantInfo("", false, "")
				AssignCh <- nil
				// cmd := exec.Command("killall", "-9", TiFlashBinPath)
				// err := cmd.Run()
				// if err != nil {
				// 	log.Printf("[error] killall tiflash failed! tenant:%v err;%v", req.AssertTenantID, err.Error())
				// }
				Pid.Store(0)
				LabelPatchCh <- "null"
				TryToUploadTiFlashLogIntoS3(true)
				// if err != nil {
				// 	return &pb.Result{HasErr: true, ErrInfo: err.Error(), TenantID: AssignTenantID.Load().(string)}, err
				// }
				return &pb.Result{HasErr: false, ErrInfo: "", TenantID: AssignTenantID.Load().(string)}, nil
			}
		} else {
			errInfo = "TryLock failed"
		}
	} else {
		errInfo = "TiFlash is not assigned to this tenant"
	}
	realTID, stimeOfAssign, isUnassigning, ver := getTenantInfo()
	log.Printf("[error][unassign]%v realTID:%v wantTID:%v\n", errInfo, realTID, req.AssertTenantID)
	return &pb.Result{HasErr: true, NeedUpdateStateIfErr: false, ErrInfo: errInfo, TenantID: realTID, StartTime: stimeOfAssign, IsUnassigning: isUnassigning, TiflashVer: ver}, nil

}

func GetCurrentTenantService() (*pb.GetTenantResponse, error) {
	tenantID, startTime, isUnassigning, ver := getTenantInfo()
	return &pb.GetTenantResponse{TenantID: tenantID, StartTime: startTime, IsUnassigning: isUnassigning, TiflashVer: ver}, nil
}

func K8sPodLabelPatchMaintainer() {
	for true {
		if len(LabelPatchCh) > 1 {
			log.Printf("[warning]size of patch channel > 1, size: %v\n", len(LabelPatchCh))
			for len(LabelPatchCh) > 1 {
				<-LabelPatchCh
			}
		}
		in := <-LabelPatchCh
		err := patchLabel(in)
		if err != nil {
			index := 0
			for len(LabelPatchCh) == 0 {
				time.Sleep(time.Duration(TimeoutArrOfK8sLabelPatch[index]) * time.Second)
				err = patchLabel(in)
				if err == nil {
					break
				}
				if index < len(TimeoutArrOfK8sLabelPatch)-1 {
					index++
				}
			}
		}
	}
}

func GenBinPath(ver string) string {
	if IsTestEnv {
		return "./test_data/infinite_loop.sh"
	}
	if ver == "" || ver == "s3" {
		return "./bin/tiflash"
	} else {
		return "./bin/tiflash"
		// TODO
		// return "./bin/" + ver + "/tiflash"
	}
}

func TiFlashMaintainer() {
	var in *pb.AssignRequest
	var buf *pb.AssignRequest
	lastTID := ""
	MaxTiFlashRunRetryCnt := 60
	for true {
		errCnt := 0
		if buf != nil { // if but not nil, consume msg from buf
			in = buf
			buf = nil
			if len(AssignCh) > 0 { // there is new assign msg, skip current msg.
				continue
			}
		} else {
			if len(AssignCh) > 1 {
				log.Printf("[warning]size of assign channel > 1, size: %v\n", len(AssignCh))
				for len(AssignCh) > 1 { // pop msgs which are out of date
					<-AssignCh
				}
			}
			in = <-AssignCh
		}
		if in == nil { // term signal, skip
			continue
		}
		configFile := fmt.Sprintf("conf/tiflash-tenant-%s.toml", in.GetTenantID())
		if lastTID != in.GetTenantID() && OptionRunMode != RunModeServeless {
			err := os.RemoveAll(PathOfTiflashCache)
			if err != nil {
				log.Printf("[error]remove cache fail: %v\n", err.Error())
			}
			err = os.RemoveAll(PathOfTiflashData)
			if err != nil {
				log.Printf("[error]remove data fail: %v\n", err.Error())
			}
		}
		var err error
		for in.GetTenantID() == AssignTenantID.Load().(string) {
			if NeedPd {
				err := PdCtlNotifyPDForExit()
				if err != nil {
					log.Printf("[error]notify pd fail: %v\n", err.Error())
				}
			}
			if NeedPd {
				log.Printf("[TiFlashMaintainer]RemoveStoreIDsOfUnhealthRNs \n")
				err = PdCtlRemoveStoreIDsOfUnhealthRNs()
				if err != nil {
					log.Printf("[error]Remove StoreIDs Of Unhealth RNs fail: %v\n", err.Error())
				}
			}
			if len(AssignCh) > 0 {
				log.Printf("size of assign channel > 0, consume!\n")
				break
			}
			cmd := exec.Command(GenBinPath(in.GetTiflashVer()), "server", "--config-file", configFile)
			err = cmd.Start()
			lastTID = in.GetTenantID()
			if err != nil {
				log.Printf("[error]Start TiFlash failed: %v", err)
				if len(AssignCh) != 0 {
					//there is new assign, skip current round
					break
				} else {
					if errCnt < MaxTiFlashRunRetryCnt {
						errCnt += 1
						time.Sleep(1 * time.Second)
						continue
					} else {
						log.Printf("[error] fail to run tiflash in %v times! Begin to call UnassignTenant()", MaxTiFlashRunRetryCnt)
						UnassignTenantService(&pb.UnassignRequest{
							AssertTenantID: AssignTenantID.Load().(string),
							ForceShutdown:  true})
						break
					}

				}
			}
			pid := strconv.Itoa(cmd.Process.Pid)
			Pid.Store(int32(cmd.Process.Pid))
			errCh := make(chan error, 1)
			newAssign := false
			go func() {
				errCh <- cmd.Wait()
			}()
			select {
			case buf = <-AssignCh:
				err = killTiFlash(pid, time.Duration(100*time.Millisecond), time.Duration(10*time.Second))
				newAssign = true
			case <-errCh:
				if len(AssignCh) != 0 {
					newAssign = true
				}
			}
			if err != nil {
				log.Printf("[error]TiFlash exited with error: %v , newAssign: %v", err, newAssign)
			} else {
				log.Printf("TiFlash exited successfully, newAssign: %v", newAssign)
			}
			if newAssign {
				break
			}
		}
	}
}

// First kill with SIGTERM, so TiFlash can do some cleanup.
// Then kill with SIGKILL if timeout, which cannot be captured.
func killTiFlash(pid string, interval, timeout time.Duration) (err error) {
	needForceKill := true
	start := time.Now()

	defer func() {
		if needForceKill {
			_, err1 := exec.Command("kill", "-SIGKILL", pid).CombinedOutput()
			log.Printf("kill with SIGTERM timeout(%v; %v), kill with SIGKILL again(err: %v, err1: %v)", interval, timeout, err, err1)
			if err == nil && err1 != nil {
				err = err1
			}
			return
		}
	}()

	_, err = exec.Command("kill", "-SIGTERM", pid).CombinedOutput()
	log.Printf("kill with SIGTERM timeout start, pid: %v, err: %v", pid, err)
	if err != nil {
		return err
	}
	for time.Since(start) < timeout {
		// Use checkTiFlashProcessExists instead of checkTiFlashAlive()
		// because tiflash process may still not exit even when 3930 port is down.
		alive, err := checkTiFlashProcessExists(pid)
		if err != nil {
			return err
		}
		if !alive {
			needForceKill = false
			// TODO: maybe metric
			log.Printf("kill with SIGTERM timeout succeed, it takes: %v", time.Since(start))
			return nil
		}
		log.Printf("kill with SIGTERM timeout failed, sleep %v and try again", interval)
		time.Sleep(interval)
	}

	return nil
}

func patchLabel(tenantId string) error {
	playLoadBytes := fmt.Sprintf(`
  {
   "metadata": {
    "labels": {
     "pod" : "%s",
     "metrics_topic" : "tiflash",
	 "pod_ip" : "%s",
     "tidb_cluster" : "%s"
    }
   }
  }
  `, LocalPodName, LocalPodIp, tenantId)

	_, err := K8sCli.CoreV1().Pods("tiflash-autoscale").Patch(context.TODO(), LocalPodName, k8stypes.StrategicMergePatchType, []byte(playLoadBytes), metav1.PatchOptions{})
	return err
}

func outsideConfig() (*rest.Config, error) {
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	// use the current context in kubeconfig
	return clientcmd.BuildConfigFromFlags("", *kubeconfig)
}

func getK8sConfig() (*rest.Config, error) {
	config, err := rest.InClusterConfig()
	if err != nil {
		return outsideConfig()
	} else {
		return config, err
	}
}

func TryToUploadTiFlashLogIntoS3(isSync bool) {
	if S3BucketForTiFLashLog != "" {
		fn := func() {
			if S3Mutex.TryLock() {
				defer S3Mutex.Unlock()
				_, err := exec.Command("aws", "s3", "cp", "--recursive", "/tiflash/log/", fmt.Sprintf("s3://%v/%v/", S3BucketForTiFLashLog, LocalPodName)).Output()
				if err != nil {
					log.Printf("[error][s3] err:%v\n", err.Error())
				}
			}
		}
		if isSync {
			fn()
		} else {
			go fn()
		}
	}
}

func InitService() {
	LocalPodIp = os.Getenv("POD_IP")
	LocalPodName = os.Getenv("POD_NAME")
	S3BucketForTiFLashLog = os.Getenv("S3_FOR_TIFLASH_LOG")
	CheckTiFlashIdleTimeoutString := os.Getenv(CheckTiflashIdleTimeoutEnv)
	CAPath = os.Getenv("CA_PATH")
	CertPath = os.Getenv("CERT_PATH")
	KeyPath = os.Getenv("KEY_PATH")
	if CAPath != "" {
		var err error
		tlsConfig, err = config.NewTLSConfig(&config.TLSConfig{
			CAFile:   CAPath,
			CertFile: CertPath,
			KeyFile:  KeyPath,
		})
		if err != nil {
			panic(fmt.Sprintf("failed to create tls config: %v", err))
		}
	}
	envtiflashCachePath := os.Getenv("TIFLASH_CACHE_PATH")
	envtiflashCacheCap := os.Getenv("TIFLASH_CACHE_CAP")
	envRunMode := os.Getenv("AS_RUN_MODE_ENV")
	envArnRole := os.Getenv("AWS_ROLE_ARN")
	log.Printf("arnrole: %v", envArnRole)
	if envtiflashCachePath != "" {
		PathOfTiflashCache = envtiflashCachePath
	}
	if envtiflashCacheCap != "" {
		CapicityOfTiflashCache = envtiflashCacheCap
	}
	if envRunMode != "" {
		if envRunMode == "local" {
			OptionRunMode = RunModeLocal
		} else if envRunMode == "dedicated" {
			OptionRunMode = RunModeDedicated
		} else if envRunMode == "serverless" {
			OptionRunMode = RunModeServeless
		} else {
			panic(fmt.Sprintf("unknown value of env AS_RUN_MODE_ENV: %v, valid options:{local, dedicated, serverless}", envRunMode))
		}
	}
	var err error
	if CheckTiFlashIdleTimeoutString != "" {
		CheckTiFlashIdleTimeout, err = strconv.Atoi(CheckTiFlashIdleTimeoutString)
		if err != nil {
			panic(err.Error())
		}
	}
	CheckTiFlashIdleIntervalString := os.Getenv(CheckTiflashIdleIntervalEnv)
	if CheckTiFlashIdleIntervalString != "" {
		CheckTiFlashIdleInterval, err = strconv.Atoi(CheckTiFlashIdleIntervalString)
		if err != nil {
			panic(err.Error())
		}
	}

	config, err := getK8sConfig()
	if err != nil {
		panic(err.Error())
	}
	K8sCli, err = kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}
	setTenantInfo("", false, "")
	LabelPatchCh <- "null"
	// NOTE(from:iosmanthus): please do not bind the service only to the POD_IP!
	// While enabling the TLS, the certificate requires the hostname to be a valid FQDN
	// or a localhost ip address (127.0.0.1 or 127.0.0.6)
	err = InitTiFlashConf(
		allinterfaces,
		DefaultVersion,
		CAPath,
		CertPath,
		KeyPath,
	)
	if err != nil {
		log.Fatalf("failed to init config: %v", err)
	}
	go TiFlashMaintainer()
	go K8sPodLabelPatchMaintainer()
}
