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 }