package scale_target_test

import (
	"fmt"
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"

	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/testcase/conprof"

	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"
)

var (
	suiteTestCtx core.TestContext
)

func init() {
	suiteTestCtx = AutoConf()
}

func TestConprof(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Conprof Scale Target Suite")
}

var _ = Describe("Conprof Scale Target Test", func() {
	var deployErr error
	var b *conprof.ClusterBundle

	BeforeSuite(func() {
		tiupNode := suiteTestCtx.Resource("tiup").(resource.WorkloadNode)
		if deployErr = conprof.CheckTiUPAvailable(tiupNode); deployErr != nil {
			return
		}

		sysbenchNode := suiteTestCtx.Resource("sysbench").(resource.WorkloadNode)
		b, deployErr = conprof.NewClusterBundle(tiupNode, sysbenchNode, conprof.ClusterConfig{
			Version:        suiteTestCtx.Param("target_version"),
			Name:           "conprof-scale-target",
			PDClientPort:   2379,
			PDPeerPort:     2380,
			TiDBPort:       4000,
			TiDBStatusPort: 10080,
			TiKVPort:       20160,
			TiKVStatusPort: 20180,
			PrometheusPort: 9090,
			NgmPort:        12020,
		})
	})

	AfterSuite(func() {
		if b != nil {
			b.Close()
			b = nil
		}
	})

	TestCase("Conprof Scale Target Test", "TEST_SCALE_TARGET", func() {
		if deployErr != nil {
			Skip(fmt.Sprintf("tiup is unavailable: %s", deployErr.Error()))
		}
		Expect(b.TurnOffTopSQL()).Should(Succeed())

		By(`
1. Turn on Conprof, start ngm, run sysbench
	* Targets begin to profile
	* Data are available`)
		// =======================================
		// Turn on Conprof, start ngm, run sysbench
		// =======================================
		Expect(b.TurnOnConprof(b.NgmURL(12020))).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
2. Scale-out TiDB:4001
	* TiDB:4001 begins to profile
	* Data are available for TiDB:4001`)
		// ===================
		// Scale-out TiDB:4001
		// ===================
		Expect(b.ScaleOut("tidb", 4001, 10081)).Should(Succeed())
		Expect(b.StartSysbench(4001)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4001, StatusPort: 10081},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
3. Scale-out TiKV:20161
	* TiKV:20161 begins to profile
	* Data are available for TiKV:20161`)
		// ====================
		// Scale-out TiKV:20161
		// ====================
		Expect(b.ScaleOut("tikv", 20161, 20181)).Should(Succeed())
		b.StopAllSysbench()
		Expect(b.DestroySysbench()).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4001)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4001, StatusPort: 10081},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20161, StatusPort: 20181},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
4. Scale-out TiDB:4002
	* TiDB:4002 begins to profile
	* Data are available for TiDB:4002`)
		// ===================
		// Scale-out TiDB:4002
		// ===================
		Expect(b.ScaleOut("tidb", 4002, 10082)).Should(Succeed())
		Expect(b.StartSysbench(4002)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4001, StatusPort: 10081},
				{Name: "tidb", IP: "tidb-peer", Port: 4002, StatusPort: 10082},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20161, StatusPort: 20181},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
5. Scale-out TiKV:20162
	* TiKV:20162 begins to profile
	* Data are available for TiKV:20162`)
		// ====================
		// Scale-out TiKV:20162
		// ====================
		Expect(b.ScaleOut("tikv", 20162, 20182)).Should(Succeed())
		b.StopAllSysbench()
		Expect(b.DestroySysbench()).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4001)).Should(Succeed())
		Expect(b.StartSysbench(4002)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4001, StatusPort: 10081},
				{Name: "tidb", IP: "tidb-peer", Port: 4002, StatusPort: 10082},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20161, StatusPort: 20181},
				{Name: "tikv", IP: "tikv-peer", Port: 20162, StatusPort: 20182},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
6. Scale-out PD:2479
	* PD:2479 begins to profile
	* Data are available for PD:2479`)
		// ====================
		// Scale-out PD:2479
		// ====================
		Expect(b.ScaleOut("pd", 2479, 2480)).Should(Succeed())
		b.StopAllSysbench()
		Expect(b.DestroySysbench()).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4001)).Should(Succeed())
		Expect(b.StartSysbench(4002)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4001, StatusPort: 10081},
				{Name: "tidb", IP: "tidb-peer", Port: 4002, StatusPort: 10082},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20161, StatusPort: 20181},
				{Name: "tikv", IP: "tikv-peer", Port: 20162, StatusPort: 20182},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
				{Name: "pd", IP: "pd-peer", Port: 2479, StatusPort: 2480},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
7. Scale-in TiDB:4001 and TiDB:4002
	* TiDB:4001 and TiDB:4002 stop profiling
	* Ngm stops scraping TiDB:4001 and TiDB:4002
	* Data are unavailable for TiDB:4001 and TiDB:4002`)
		// ================================
		// Scale-in TiDB:4001 and TiDB:4002
		// ================================
		b.StopSysbench(4001)
		b.StopSysbench(4002)
		Expect(b.ScaleIn("tidb", 4001)).Should(Succeed())
		Expect(b.ScaleIn("tidb", 4002)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20161, StatusPort: 20181},
				{Name: "tikv", IP: "tikv-peer", Port: 20162, StatusPort: 20182},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
				{Name: "pd", IP: "pd-peer", Port: 2479, StatusPort: 2480},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
8. Scale-in TiKV:20161 and TiKV:20162
	* TiKV:20161 and TiKV:20162 stop profiling
	* Ngm stops scraping TiKV:20161 and TiKV:20162
	* Data are unavailable for TiKV:20161 and TiKV:20162`)
		// ==================================
		// Scale-in TiKV:20161 and TiKV:20162
		// ==================================
		Expect(b.ScaleIn("tikv", 20161)).Should(Succeed())
		Expect(b.ScaleIn("tikv", 20162)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
				{Name: "pd", IP: "pd-peer", Port: 2479, StatusPort: 2480},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
9. Scale-in PD:2379
	* PD:2379 stop profiling
	* Ngm stops scraping PD:2379
	* Data are unavailable for PD:2379`)
		// ==================================
		// Scale-in PD:2379
		// ==================================
		Expect(b.ScaleIn("pd", 2379)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "pd", IP: "pd-peer", Port: 2479, StatusPort: 2480},
			})
		}, "5m", "10s").Should(Succeed())
	})
})
