github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/k8s/e2e/common/test_common.go (about)

     1  package common
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/go-resty/resty/v2"
    12  	"github.com/onsi/gomega"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/chaos"
    17  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/client"
    18  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/config"
    19  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/environment"
    20  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/pkg/helm/chainlink"
    21  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/pkg/helm/ethereum"
    22  	"github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/presets"
    23  	"github.com/smartcontractkit/chainlink-testing-framework/libs/logging"
    24  	"github.com/smartcontractkit/chainlink-testing-framework/libs/utils/ptr"
    25  )
    26  
    27  const (
    28  	TestEnvType = "chainlink-testing-framework-k8s-test"
    29  )
    30  
    31  var (
    32  	testSelector = fmt.Sprintf("envType=%s", TestEnvType)
    33  )
    34  
    35  func GetTestEnvConfig(t *testing.T) *environment.Config {
    36  	return &environment.Config{
    37  		NamespacePrefix: TestEnvType,
    38  		Labels:          []string{testSelector},
    39  		Test:            t,
    40  	}
    41  }
    42  
    43  func TestMultiStageMultiManifestConnection(t *testing.T) {
    44  	t.Parallel()
    45  	l := logging.GetTestLogger(t)
    46  	testEnvConfig := GetTestEnvConfig(t)
    47  
    48  	ethChart := ethereum.New(nil)
    49  	ethNetworkName := ethChart.GetProps().(*ethereum.Props).NetworkName
    50  
    51  	// we adding the same chart with different index and executing multi-stage deployment
    52  	// connections should be renewed
    53  	e := environment.New(testEnvConfig)
    54  	err := e.AddHelm(ethChart).
    55  		AddHelm(chainlink.New(0, map[string]any{
    56  			"replicas": 1,
    57  			"toml":     presets.BaseToml,
    58  		})).
    59  		Run()
    60  	require.NoError(t, err)
    61  	if e.WillUseRemoteRunner() {
    62  		return
    63  	}
    64  	t.Cleanup(func() {
    65  		assert.NoError(t, e.Shutdown())
    66  	})
    67  	require.Len(t, e.URLs[chainlink.NodesLocalURLsKey], 1)
    68  	require.Len(t, e.URLs[chainlink.NodesInternalURLsKey], 1)
    69  	require.Len(t, e.URLs[chainlink.DBsLocalURLsKey], 1)
    70  	require.Len(t, e.URLs, 7)
    71  
    72  	err = e.AddHelm(chainlink.New(1, map[string]any{
    73  		"replicas": 1,
    74  		"toml":     presets.BaseToml,
    75  	})).
    76  		Run()
    77  	require.NoError(t, err)
    78  	require.Len(t, e.URLs[chainlink.NodesLocalURLsKey], 2)
    79  	require.Len(t, e.URLs[chainlink.NodesInternalURLsKey], 2)
    80  	require.Len(t, e.URLs[chainlink.DBsLocalURLsKey], 2)
    81  	require.Len(t, e.URLs, 7)
    82  
    83  	urls := make([]string, 0)
    84  	if e.Cfg.InsideK8s {
    85  		urls = append(urls, e.URLs[chainlink.NodesInternalURLsKey]...)
    86  		urls = append(urls, e.URLs[ethNetworkName+"_internal_http"]...)
    87  	} else {
    88  		urls = append(urls, e.URLs[chainlink.NodesLocalURLsKey]...)
    89  		urls = append(urls, e.URLs[ethNetworkName+"_http"]...)
    90  	}
    91  
    92  	r := resty.New()
    93  	for _, u := range urls {
    94  		l.Info().Str("URL", u).Send()
    95  		res, err := r.R().Get(u)
    96  		require.NoError(t, err)
    97  		require.Equal(t, "200 OK", res.Status())
    98  	}
    99  }
   100  
   101  func TestConnectWithoutManifest(t *testing.T) {
   102  	l := logging.GetTestLogger(t)
   103  	existingEnvConfig := GetTestEnvConfig(t)
   104  	testEnvConfig := GetTestEnvConfig(t)
   105  	existingEnvAlreadySetupVar := "ENV_ALREADY_EXISTS"
   106  	var existingEnv *environment.Environment
   107  
   108  	// only run this section if we don't already have an existing environment
   109  	// needed for remote runner based tests to prevent duplicate envs from being created
   110  	if os.Getenv(existingEnvAlreadySetupVar) == "" {
   111  		existingEnv = environment.New(existingEnvConfig)
   112  		l.Info().Str("Namespace", existingEnvConfig.Namespace).Msg("Existing Env Namespace")
   113  		// deploy environment to use as an existing one for the test
   114  		existingEnv.Cfg.JobImage = ""
   115  		existingEnv.AddHelm(ethereum.New(nil)).
   116  			AddHelm(chainlink.New(0, map[string]any{
   117  				"replicas": 1,
   118  				"toml":     presets.BaseToml,
   119  			}))
   120  		err := existingEnv.Run()
   121  		require.NoError(t, err)
   122  		// propagate the existing environment to the remote runner
   123  		t.Setenv(fmt.Sprintf("TEST_%s", existingEnvAlreadySetupVar), "abc")
   124  		// set the namespace to the existing one for local runs
   125  		testEnvConfig.Namespace = existingEnv.Cfg.Namespace
   126  	} else {
   127  		l.Info().Msg("Environment already exists, verfying it is correct")
   128  		require.NotEmpty(t, os.Getenv(config.EnvVarNamespace))
   129  		noManifestUpdate, err := strconv.ParseBool(os.Getenv(config.EnvVarNoManifestUpdate))
   130  		require.NoError(t, err, "Failed to parse the no manifest update env var")
   131  		require.True(t, noManifestUpdate)
   132  	}
   133  
   134  	// Now run an environment without a manifest like a normal test
   135  	testEnvConfig.NoManifestUpdate = true
   136  	testEnv := environment.New(testEnvConfig)
   137  	l.Info().Msgf("Testing Env Namespace %s", testEnv.Cfg.Namespace)
   138  	err := testEnv.AddHelm(ethereum.New(nil)).
   139  		AddHelm(chainlink.New(0, map[string]any{
   140  			"replicas": 1,
   141  			"toml":     presets.BaseToml,
   142  		})).
   143  		Run()
   144  	require.NoError(t, err)
   145  	if testEnv.WillUseRemoteRunner() {
   146  		return
   147  	}
   148  	t.Cleanup(func() {
   149  		assert.NoError(t, testEnv.Shutdown())
   150  	})
   151  
   152  	connection := client.LocalConnection
   153  	if testEnv.Cfg.InsideK8s {
   154  		connection = client.RemoteConnection
   155  	}
   156  	url, err := testEnv.Fwd.FindPort("chainlink-0:node-1", "node", "access").As(connection, client.HTTP)
   157  	require.NoError(t, err)
   158  	urlGeth, err := testEnv.Fwd.FindPort("geth:0", "geth-network", "http-rpc").As(connection, client.HTTP)
   159  	require.NoError(t, err)
   160  	r := resty.New()
   161  	l.Info().Msgf("getting url: %s", url)
   162  	res, err := r.R().Get(url)
   163  	require.NoError(t, err)
   164  	require.Equal(t, "200 OK", res.Status())
   165  	l.Info().Msgf("getting url: %s", url)
   166  	res, err = r.R().Get(urlGeth)
   167  	require.NoError(t, err)
   168  	require.Equal(t, "200 OK", res.Status())
   169  	l.Info().Msgf("done getting url: %s", url)
   170  }
   171  
   172  func Test5NodesSoakEnvironmentWithPVCs(t *testing.T) {
   173  	t.Parallel()
   174  	testEnvConfig := GetTestEnvConfig(t)
   175  	e := presets.EVMSoak(testEnvConfig)
   176  	err := e.Run()
   177  	require.NoError(t, err)
   178  	t.Cleanup(func() {
   179  		assert.NoError(t, e.Shutdown())
   180  	})
   181  }
   182  
   183  func TestWithSingleNodeEnvParallel(t *testing.T) {
   184  	t.Parallel()
   185  	TestWithSingleNodeEnv(t)
   186  }
   187  
   188  func TestWithSingleNodeEnv(t *testing.T) {
   189  	testEnvConfig := GetTestEnvConfig(t)
   190  	e, err := presets.EVMOneNode(testEnvConfig)
   191  	require.NoError(t, err)
   192  	err = e.Run()
   193  	require.NoError(t, err)
   194  	if e.WillUseRemoteRunner() {
   195  		return
   196  	}
   197  	t.Cleanup(func() {
   198  		assert.NoError(t, e.Shutdown())
   199  	})
   200  }
   201  
   202  func TestMultipleNodeWithDiffDBVersionEnv(t *testing.T) {
   203  	t.Parallel()
   204  	testEnvConfig := GetTestEnvConfig(t)
   205  	e := presets.EVMMultipleNodesWithDiffDBVersion(testEnvConfig)
   206  	err := e.Run()
   207  	require.NoError(t, err)
   208  	if e.WillUseRemoteRunner() {
   209  		return
   210  	}
   211  	t.Cleanup(func() {
   212  		assert.NoError(t, e.Shutdown())
   213  	})
   214  }
   215  
   216  func TestMinResources5NodesEnv(t *testing.T) {
   217  	t.Parallel()
   218  	testEnvConfig := GetTestEnvConfig(t)
   219  	e := presets.EVMMinimalLocal(testEnvConfig)
   220  	err := e.Run()
   221  	require.NoError(t, err)
   222  	if e.WillUseRemoteRunner() {
   223  		return
   224  	}
   225  	t.Cleanup(func() {
   226  		assert.NoError(t, e.Shutdown())
   227  	})
   228  }
   229  
   230  func TestMinResources5NodesEnvWithBlockscout(t *testing.T) {
   231  	t.Parallel()
   232  	testEnvConfig := GetTestEnvConfig(t)
   233  	e, err := presets.EVMMinimalLocalBS(testEnvConfig)
   234  	require.NoError(t, err)
   235  	err = e.Run()
   236  	require.NoError(t, err)
   237  	if e.WillUseRemoteRunner() {
   238  		return
   239  	}
   240  	t.Cleanup(func() {
   241  		assert.NoError(t, e.Shutdown())
   242  	})
   243  }
   244  
   245  func Test5NodesPlus2MiningGethsReorgEnv(t *testing.T) {
   246  	t.Parallel()
   247  	testEnvConfig := GetTestEnvConfig(t)
   248  	e, err := presets.EVMReorg(testEnvConfig)
   249  	require.NoError(t, err)
   250  	err = e.Run()
   251  	require.NoError(t, err)
   252  	if e.WillUseRemoteRunner() {
   253  		return
   254  	}
   255  	t.Cleanup(func() {
   256  		assert.NoError(t, e.Shutdown())
   257  	})
   258  }
   259  
   260  func TestMultipleInstancesOfTheSameType(t *testing.T) {
   261  	t.Parallel()
   262  	testEnvConfig := GetTestEnvConfig(t)
   263  	e := environment.New(testEnvConfig).
   264  		AddHelm(ethereum.New(nil)).
   265  		AddHelm(chainlink.New(0, map[string]any{
   266  			"replicas": 1,
   267  			"toml":     presets.BaseToml,
   268  		})).
   269  		AddHelm(chainlink.New(1, map[string]any{
   270  			"replicas": 1,
   271  			"toml":     presets.BaseToml,
   272  		}))
   273  	err := e.Run()
   274  	require.NoError(t, err)
   275  	if e.WillUseRemoteRunner() {
   276  		return
   277  	}
   278  	t.Cleanup(func() {
   279  		assert.NoError(t, e.Shutdown())
   280  	})
   281  }
   282  
   283  // TestWithChaos runs a test with chaos injected into the environment.
   284  func TestWithChaos(t *testing.T) {
   285  	t.Parallel()
   286  	l := logging.GetTestLogger(t)
   287  	appLabel := "chainlink-0"
   288  	testCase := struct {
   289  		chaosFunc  chaos.ManifestFunc
   290  		chaosProps *chaos.Props
   291  	}{
   292  		chaos.NewFailPods,
   293  		&chaos.Props{
   294  			LabelsSelector: &map[string]*string{client.AppLabel: ptr.Ptr(appLabel)},
   295  			DurationStr:    "30s",
   296  		},
   297  	}
   298  	testEnvConfig := GetTestEnvConfig(t)
   299  	cd := chainlink.New(0, map[string]any{
   300  		"replicas": 1,
   301  		"toml":     presets.BaseToml,
   302  	})
   303  
   304  	e := environment.New(testEnvConfig).
   305  		AddHelm(ethereum.New(nil)).
   306  		AddHelm(cd)
   307  	err := e.Run()
   308  	require.NoError(t, err)
   309  	if e.WillUseRemoteRunner() {
   310  		return
   311  	}
   312  	t.Cleanup(func() {
   313  		assert.NoError(t, e.Shutdown())
   314  	})
   315  
   316  	url := e.URLs["chainlink_local"][0]
   317  	r := resty.New()
   318  	res, err := r.R().Get(url)
   319  	require.NoError(t, err)
   320  	require.Equal(t, "200 OK", res.Status())
   321  
   322  	// start chaos
   323  	_, err = e.Chaos.Run(testCase.chaosFunc(e.Cfg.Namespace, testCase.chaosProps))
   324  	require.NoError(t, err)
   325  	gom := gomega.NewGomegaWithT(t)
   326  	gom.Eventually(func(g gomega.Gomega) {
   327  		res, err = r.R().Get(url)
   328  		g.Expect(err).Should(gomega.HaveOccurred())
   329  		l.Info().Msg("Expected error was found")
   330  	}, "1m", "3s").Should(gomega.Succeed())
   331  
   332  	l.Info().Msg("Waiting for Pod to start back up")
   333  	err = e.Run()
   334  	require.NoError(t, err)
   335  
   336  	// verify that the node can receive requests again
   337  	url = e.URLs["chainlink_local"][0]
   338  	res, err = r.R().Get(url)
   339  	require.NoError(t, err)
   340  	require.Equal(t, "200 OK", res.Status())
   341  }
   342  
   343  func TestEmptyEnvironmentStartup(t *testing.T) {
   344  	t.Parallel()
   345  	testEnvConfig := GetTestEnvConfig(t)
   346  	e := environment.New(testEnvConfig)
   347  	err := e.Run()
   348  	require.NoError(t, err)
   349  	if e.WillUseRemoteRunner() {
   350  		return
   351  	}
   352  	t.Cleanup(func() {
   353  		assert.NoError(t, e.Shutdown())
   354  	})
   355  }
   356  
   357  func TestRolloutRestart(t *testing.T, statefulSet bool) {
   358  	t.Parallel()
   359  	testEnvConfig := GetTestEnvConfig(t)
   360  	cd := chainlink.New(0, map[string]any{
   361  		"replicas": 5,
   362  		"toml":     presets.BaseToml,
   363  		"db": map[string]any{
   364  			"stateful": true,
   365  			"capacity": "1Gi",
   366  		},
   367  	})
   368  
   369  	e := environment.New(testEnvConfig).
   370  		AddHelm(ethereum.New(nil)).
   371  		AddHelm(cd)
   372  	err := e.Run()
   373  	require.NoError(t, err)
   374  	if e.WillUseRemoteRunner() {
   375  		return
   376  	}
   377  	t.Cleanup(func() {
   378  		assert.NoError(t, e.Shutdown())
   379  	})
   380  
   381  	if statefulSet {
   382  		err = e.RolloutStatefulSets()
   383  		require.NoError(t, err, "failed to rollout statefulsets")
   384  	} else {
   385  		err = e.RolloutRestartBySelector("deployment", testSelector)
   386  		require.NoError(t, err, "failed to rollout restart deployment")
   387  	}
   388  
   389  	err = e.Run()
   390  	require.NoError(t, err, "failed to run environment")
   391  }
   392  
   393  func TestReplaceHelm(t *testing.T) {
   394  	t.Parallel()
   395  	testEnvConfig := GetTestEnvConfig(t)
   396  	cd := chainlink.New(0, map[string]any{
   397  		"toml": presets.BaseToml,
   398  		"chainlink": map[string]any{
   399  			"resources": map[string]any{
   400  				"requests": map[string]any{
   401  					"cpu": "350m",
   402  				},
   403  				"limits": map[string]any{
   404  					"cpu": "350m",
   405  				},
   406  			},
   407  		},
   408  	})
   409  
   410  	e := environment.New(testEnvConfig).AddHelm(cd)
   411  	err := e.Run()
   412  	require.NoError(t, err)
   413  	if e.WillUseRemoteRunner() {
   414  		return
   415  	}
   416  	t.Cleanup(func() {
   417  		assert.NoError(t, e.Shutdown())
   418  	})
   419  	require.NoError(t, err)
   420  	cd = chainlink.New(1, map[string]any{
   421  		"toml": presets.BaseToml,
   422  		"chainlink": map[string]any{
   423  			"resources": map[string]any{
   424  				"requests": map[string]any{
   425  					"cpu": "345m",
   426  				},
   427  				"limits": map[string]any{
   428  					"cpu": "345m",
   429  				},
   430  			},
   431  		},
   432  	})
   433  	require.NoError(t, err)
   434  	e, err = e.
   435  		ReplaceHelm("chainlink-0", cd)
   436  	require.NoError(t, err)
   437  	err = e.Run()
   438  	require.NoError(t, err)
   439  }
   440  
   441  func TestRunTimeout(t *testing.T) {
   442  	t.Parallel()
   443  	testEnvConfig := GetTestEnvConfig(t)
   444  	e, err := presets.EVMOneNode(testEnvConfig)
   445  	require.NoError(t, err)
   446  	e.Cfg.ReadyCheckData.Timeout = 5 * time.Second
   447  	err = e.Run()
   448  	require.Error(t, err)
   449  }
   450  
   451  func TestReallyLongLogs(t *testing.T) {
   452  	t.Parallel()
   453  	l := logging.GetTestLogger(t)
   454  	testEnvConfig := GetTestEnvConfig(t)
   455  	val, _ := os.LookupEnv(config.EnvVarJobImage)
   456  	if val != "" {
   457  		env := environment.New(testEnvConfig)
   458  		err := env.Run()
   459  		require.NoError(t, err)
   460  	}
   461  	s := strings.Repeat("a", 500000)
   462  	// this shouldn't hang
   463  	l.Info().Int("len", len(s)).Str("string", s).Msg("string")
   464  }