github.com/kotalco/kotal@v0.3.0/controllers/ethereum/node_controller_test.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "time" 8 9 ethereumv1alpha1 "github.com/kotalco/kotal/apis/ethereum/v1alpha1" 10 sharedAPI "github.com/kotalco/kotal/apis/shared" 11 "github.com/kotalco/kotal/controllers/shared" 12 . "github.com/onsi/ginkgo/v2" 13 . "github.com/onsi/gomega" 14 "github.com/onsi/gomega/gstruct" 15 appsv1 "k8s.io/api/apps/v1" 16 corev1 "k8s.io/api/core/v1" 17 "k8s.io/apimachinery/pkg/api/resource" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/types" 20 "k8s.io/apimachinery/pkg/util/intstr" 21 ) 22 23 var _ = Describe("Ethereum network controller", func() { 24 25 const ( 26 sleepTime = 5 * time.Second 27 interval = 2 * time.Second 28 timeout = 2 * time.Minute 29 networkID = 7777 30 // node private key 31 privatekey = "608e9b6f67c65e47531e08e8e501386dfae63a540fa3c48802c8aad854510b4e" 32 // imported account 33 accountKey = "5df5eff7ef9e4e82739b68a34c6b23608d79ee8daf3b598a01ffb0dd7aa3a2fd" 34 accountAddress = sharedAPI.EthereumAddress("0x2b3430337f12Ce89EaBC7b0d865F4253c7744c0d") 35 accountPassword = "secret" 36 ) 37 38 var ( 39 useExistingCluster = os.Getenv(shared.EnvUseExistingCluster) == "true" 40 ) 41 42 Context("Joining Mainnet", func() { 43 ns := &corev1.Namespace{ 44 ObjectMeta: metav1.ObjectMeta{ 45 Name: "mainnet", 46 }, 47 } 48 key := types.NamespacedName{ 49 Name: "my-node", 50 Namespace: ns.Name, 51 } 52 53 spec := ethereumv1alpha1.NodeSpec{ 54 Client: ethereumv1alpha1.BesuClient, 55 Network: "mainnet", 56 NodePrivateKeySecretName: "nodekey", 57 SyncMode: ethereumv1alpha1.FullSynchronization, 58 Logging: sharedAPI.NoLogs, 59 } 60 61 toCreate := ðereumv1alpha1.Node{ 62 ObjectMeta: metav1.ObjectMeta{ 63 Name: key.Name, 64 Namespace: key.Namespace, 65 }, 66 Spec: spec, 67 } 68 t := true 69 70 nodeOwnerReference := metav1.OwnerReference{ 71 APIVersion: "ethereum.kotal.io/v1alpha1", 72 Kind: "Node", 73 Name: toCreate.Name, 74 Controller: &t, 75 BlockOwnerDeletion: &t, 76 } 77 78 It(fmt.Sprintf("should create %s namespace", ns.Name), func() { 79 Expect(k8sClient.Create(context.Background(), ns)).Should(Succeed()) 80 }) 81 82 It("Should create nodekey secret", func() { 83 secret := corev1.Secret{ 84 ObjectMeta: metav1.ObjectMeta{ 85 Name: "nodekey", 86 Namespace: ns.Name, 87 }, 88 StringData: map[string]string{ 89 "key": privatekey, 90 }, 91 } 92 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 93 }) 94 95 It("Should create the node", func() { 96 if !useExistingCluster { 97 toCreate.Default() 98 } 99 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 100 time.Sleep(sleepTime) 101 }) 102 103 It("Should get the node", func() { 104 fetched := ðereumv1alpha1.Node{} 105 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 106 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 107 // TODO: test status 108 nodeOwnerReference.UID = fetched.GetUID() 109 }) 110 111 It("Should create node configmap", func() { 112 config := &corev1.ConfigMap{} 113 Expect(k8sClient.Get(context.Background(), key, config)).Should(Succeed()) 114 Expect(config.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 115 }) 116 117 It("Should create node service", func() { 118 svc := &corev1.Service{} 119 Expect(k8sClient.Get(context.Background(), key, svc)).To(Succeed()) 120 Expect(svc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 121 Expect(svc.Spec.Ports).To(ContainElements([]corev1.ServicePort{ 122 { 123 Name: "discovery", 124 Port: int32(ethereumv1alpha1.DefaultP2PPort), 125 TargetPort: intstr.FromString("discovery"), 126 Protocol: corev1.ProtocolUDP, 127 }, 128 { 129 Name: "p2p", 130 Port: int32(ethereumv1alpha1.DefaultP2PPort), 131 TargetPort: intstr.FromString("p2p"), 132 Protocol: corev1.ProtocolTCP, 133 }, 134 })) 135 }) 136 137 It("Should create node statefulset with correct arguments", func() { 138 sts := &appsv1.StatefulSet{} 139 Expect(k8sClient.Get(context.Background(), key, sts)).To(Succeed()) 140 Expect(sts.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 141 Expect(*sts.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 142 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 143 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 144 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 145 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 146 })) 147 Expect(sts.Spec.Template.Spec.Containers[0].Image).To(Equal(toCreate.Spec.Image)) 148 }) 149 150 It("Should allocate correct resources to node statefulset", func() { 151 sts := &appsv1.StatefulSet{} 152 expectedResources := corev1.ResourceRequirements{ 153 Requests: corev1.ResourceList{ 154 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeCPURequest), 155 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeMemoryRequest), 156 }, 157 Limits: corev1.ResourceList{ 158 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeCPULimit), 159 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeMemoryLimit), 160 }, 161 } 162 Expect(k8sClient.Get(context.Background(), key, sts)).To(Succeed()) 163 Expect(sts.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 164 }) 165 166 It("Should create node data persistent volume with correct resources", func() { 167 pvc := &corev1.PersistentVolumeClaim{} 168 expectedResources := corev1.VolumeResourceRequirements{ 169 Requests: corev1.ResourceList{ 170 corev1.ResourceStorage: resource.MustParse(ethereumv1alpha1.DefaultMainNetworkFullNodeStorageRequest), 171 }, 172 } 173 Expect(k8sClient.Get(context.Background(), key, pvc)).To(Succeed()) 174 Expect(pvc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 175 Expect(pvc.Spec.Resources).To(Equal(expectedResources)) 176 }) 177 178 It("Should delete the node", func() { 179 toDelete := ðereumv1alpha1.Node{} 180 Expect(k8sClient.Get(context.Background(), key, toDelete)).To(Succeed()) 181 Expect(k8sClient.Delete(context.Background(), toDelete)).To(Succeed()) 182 time.Sleep(sleepTime) 183 }) 184 185 It("Should not get the node after deletion", func() { 186 fetched := ðereumv1alpha1.Node{} 187 Expect(k8sClient.Get(context.Background(), key, fetched)).ToNot(Succeed()) 188 }) 189 190 if useExistingCluster { 191 It("Should delete node statefulset", func() { 192 nodeSts := &appsv1.StatefulSet{} 193 Expect(k8sClient.Get(context.Background(), key, nodeSts)).ToNot(Succeed()) 194 }) 195 196 It("Should delete node service", func() { 197 nodeSvc := &corev1.Service{} 198 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).ToNot(Succeed()) 199 }) 200 } 201 202 It(fmt.Sprintf("should delete %s namespace", ns.Name), func() { 203 Expect(k8sClient.Delete(context.Background(), ns)).Should(Succeed()) 204 }) 205 }) 206 207 Context("Joining Goerli", func() { 208 ns := &corev1.Namespace{ 209 ObjectMeta: metav1.ObjectMeta{ 210 Name: ethereumv1alpha1.GoerliNetwork, 211 }, 212 } 213 key := types.NamespacedName{ 214 Name: "my-node", 215 Namespace: ns.Name, 216 } 217 218 spec := ethereumv1alpha1.NodeSpec{ 219 Client: ethereumv1alpha1.BesuClient, 220 Network: ethereumv1alpha1.GoerliNetwork, 221 NodePrivateKeySecretName: "nodekey", 222 Logging: sharedAPI.FatalLogs, 223 } 224 225 toCreate := ðereumv1alpha1.Node{ 226 ObjectMeta: metav1.ObjectMeta{ 227 Name: key.Name, 228 Namespace: key.Namespace, 229 }, 230 Spec: spec, 231 } 232 t := true 233 nodeOwnerReference := metav1.OwnerReference{ 234 APIVersion: "ethereum.kotal.io/v1alpha1", 235 Kind: "Node", 236 Controller: &t, 237 BlockOwnerDeletion: &t, 238 } 239 240 It(fmt.Sprintf("should create %s namespace", ns.Name), func() { 241 Expect(k8sClient.Create(context.Background(), ns)).Should(Succeed()) 242 }) 243 244 It("Should create nodekey secret", func() { 245 secret := corev1.Secret{ 246 ObjectMeta: metav1.ObjectMeta{ 247 Name: "nodekey", 248 Namespace: ns.Name, 249 }, 250 StringData: map[string]string{ 251 "key": privatekey, 252 }, 253 } 254 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 255 }) 256 257 It("Should create account private key and password secrets", func() { 258 accountPrivateKeySecret := corev1.Secret{ 259 ObjectMeta: metav1.ObjectMeta{ 260 Name: "my-account-privatekey", 261 Namespace: ns.Name, 262 }, 263 StringData: map[string]string{ 264 "key": accountKey, 265 }, 266 } 267 Expect(k8sClient.Create(context.Background(), &accountPrivateKeySecret)).To(Succeed()) 268 269 accountPasswordSecret := corev1.Secret{ 270 ObjectMeta: metav1.ObjectMeta{ 271 Name: "my-account-password", 272 Namespace: ns.Name, 273 }, 274 StringData: map[string]string{ 275 "password": accountPassword, 276 }, 277 } 278 Expect(k8sClient.Create(context.Background(), &accountPasswordSecret)).To(Succeed()) 279 }) 280 281 It("Should create the node", func() { 282 if !useExistingCluster { 283 toCreate.Default() 284 } 285 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 286 time.Sleep(sleepTime) 287 }) 288 289 It("Should get the node", func() { 290 fetched := ðereumv1alpha1.Node{} 291 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 292 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 293 nodeOwnerReference.UID = fetched.GetUID() 294 nodeOwnerReference.Name = key.Name 295 }) 296 297 It("Should create node config", func() { 298 config := &corev1.ConfigMap{} 299 Expect(k8sClient.Get(context.Background(), key, config)).Should(Succeed()) 300 }) 301 302 It("Should create node service", func() { 303 svc := &corev1.Service{} 304 Expect(k8sClient.Get(context.Background(), key, svc)).To(Succeed()) 305 Expect(svc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 306 Expect(svc.Spec.Ports).To(ContainElements([]corev1.ServicePort{ 307 { 308 Name: "discovery", 309 Port: int32(ethereumv1alpha1.DefaultP2PPort), 310 TargetPort: intstr.FromString("discovery"), 311 Protocol: corev1.ProtocolUDP, 312 }, 313 { 314 Name: "p2p", 315 Port: int32(ethereumv1alpha1.DefaultP2PPort), 316 TargetPort: intstr.FromString("p2p"), 317 Protocol: corev1.ProtocolTCP, 318 }, 319 })) 320 }) 321 322 It("Should create node statefulset with correct arguments", func() { 323 sts := &appsv1.StatefulSet{} 324 Expect(k8sClient.Get(context.Background(), key, sts)).To(Succeed()) 325 Expect(sts.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 326 Expect(*sts.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 327 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 328 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 329 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 330 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 331 })) 332 Expect(sts.Spec.Template.Spec.Containers[0].Image).To(Equal(toCreate.Spec.Image)) 333 }) 334 335 It("Should allocate correct resources to node statefulset", func() { 336 nodeSts := &appsv1.StatefulSet{} 337 expectedResources := corev1.ResourceRequirements{ 338 Requests: corev1.ResourceList{ 339 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeCPURequest), 340 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeMemoryRequest), 341 }, 342 Limits: corev1.ResourceList{ 343 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeCPULimit), 344 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPublicNetworkNodeMemoryLimit), 345 }, 346 } 347 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 348 Expect(nodeSts.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 349 350 }) 351 352 It("Should create bootnode data persistent volume with correct resources", func() { 353 nodePVC := &corev1.PersistentVolumeClaim{} 354 expectedResources := corev1.VolumeResourceRequirements{ 355 Requests: corev1.ResourceList{ 356 corev1.ResourceStorage: resource.MustParse(ethereumv1alpha1.DefaultTestNetworkStorageRequest), 357 }, 358 } 359 Expect(k8sClient.Get(context.Background(), key, nodePVC)).To(Succeed()) 360 Expect(nodePVC.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 361 Expect(nodePVC.Spec.Resources).To(Equal(expectedResources)) 362 }) 363 364 It("Should delete node", func() { 365 toDelete := ðereumv1alpha1.Node{} 366 Expect(k8sClient.Get(context.Background(), key, toDelete)).To(Succeed()) 367 Expect(k8sClient.Delete(context.Background(), toDelete)).To(Succeed()) 368 time.Sleep(sleepTime) 369 }) 370 371 It("Should not get node after deletion", func() { 372 fetched := ðereumv1alpha1.Node{} 373 Expect(k8sClient.Get(context.Background(), key, fetched)).ToNot(Succeed()) 374 }) 375 376 if useExistingCluster { 377 It("Should delete node statefulset", func() { 378 nodeSts := &appsv1.StatefulSet{} 379 Expect(k8sClient.Get(context.Background(), key, nodeSts)).ToNot(Succeed()) 380 }) 381 382 // TODO: remove this test 383 It("Should delete node privatekey secret", func() { 384 nodeSecret := &corev1.Secret{} 385 Expect(k8sClient.Get(context.Background(), key, nodeSecret)).ToNot(Succeed()) 386 }) 387 388 It("Should delete node service", func() { 389 nodeSvc := &corev1.Service{} 390 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).ToNot(Succeed()) 391 }) 392 } 393 394 It(fmt.Sprintf("should delete %s namespace", ns.Name), func() { 395 Expect(k8sClient.Delete(context.Background(), ns)).Should(Succeed()) 396 }) 397 }) 398 399 Context("private PoA network", func() { 400 ns := &corev1.Namespace{ 401 ObjectMeta: metav1.ObjectMeta{ 402 Name: "poa", 403 }, 404 } 405 key := types.NamespacedName{ 406 Name: "my-poa-node", 407 Namespace: ns.Name, 408 } 409 410 spec := ethereumv1alpha1.NodeSpec{ 411 Genesis: ðereumv1alpha1.Genesis{ 412 ChainID: 55555, 413 NetworkID: networkID, 414 Clique: ðereumv1alpha1.Clique{ 415 Signers: []sharedAPI.EthereumAddress{ 416 sharedAPI.EthereumAddress("0xd2c21213027cbf4d46c16b55fa98e5252b048706"), 417 }, 418 }, 419 }, 420 Client: ethereumv1alpha1.BesuClient, 421 NodePrivateKeySecretName: "nodekey", 422 } 423 424 toCreate := ðereumv1alpha1.Node{ 425 ObjectMeta: metav1.ObjectMeta{ 426 Name: key.Name, 427 Namespace: key.Namespace, 428 }, 429 Spec: spec, 430 } 431 t := true 432 nodeOwnerReference := metav1.OwnerReference{ 433 // TODO: update version 434 APIVersion: "ethereum.kotal.io/v1alpha1", 435 Kind: "Node", 436 Controller: &t, 437 BlockOwnerDeletion: &t, 438 } 439 440 It(fmt.Sprintf("should create %s namespace", ns.Name), func() { 441 Expect(k8sClient.Create(context.Background(), ns)).Should(Succeed()) 442 }) 443 444 It("Should create nodekey secret", func() { 445 secret := corev1.Secret{ 446 ObjectMeta: metav1.ObjectMeta{ 447 Name: "nodekey", 448 Namespace: ns.Name, 449 }, 450 StringData: map[string]string{ 451 "key": privatekey, 452 }, 453 } 454 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 455 }) 456 457 It("Should create account private key and password secrets", func() { 458 accountPrivateKeySecret := corev1.Secret{ 459 ObjectMeta: metav1.ObjectMeta{ 460 Name: "my-account-privatekey", 461 Namespace: ns.Name, 462 }, 463 StringData: map[string]string{ 464 "key": accountKey, 465 }, 466 } 467 Expect(k8sClient.Create(context.Background(), &accountPrivateKeySecret)).To(Succeed()) 468 469 accountPasswordSecret := corev1.Secret{ 470 ObjectMeta: metav1.ObjectMeta{ 471 Name: "my-account-password", 472 Namespace: ns.Name, 473 }, 474 StringData: map[string]string{ 475 "password": accountPassword, 476 }, 477 } 478 Expect(k8sClient.Create(context.Background(), &accountPasswordSecret)).To(Succeed()) 479 }) 480 481 It("Should create the node", func() { 482 if !useExistingCluster { 483 toCreate.Default() 484 } 485 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 486 time.Sleep(sleepTime) 487 }) 488 489 It("Should get the node", func() { 490 fetched := ðereumv1alpha1.Node{} 491 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 492 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 493 nodeOwnerReference.UID = fetched.GetUID() 494 nodeOwnerReference.Name = key.Name 495 }) 496 497 It("Should create node service", func() { 498 svc := &corev1.Service{} 499 Expect(k8sClient.Get(context.Background(), key, svc)).To(Succeed()) 500 Expect(svc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 501 Expect(svc.Spec.Ports).To(ContainElements([]corev1.ServicePort{ 502 { 503 Name: "discovery", 504 Port: int32(ethereumv1alpha1.DefaultP2PPort), 505 TargetPort: intstr.FromString("discovery"), 506 Protocol: corev1.ProtocolUDP, 507 }, 508 { 509 Name: "p2p", 510 Port: int32(ethereumv1alpha1.DefaultP2PPort), 511 TargetPort: intstr.FromString("p2p"), 512 Protocol: corev1.ProtocolTCP, 513 }, 514 })) 515 }) 516 517 It("Should create node statefulset with correct arguments", func() { 518 nodeSts := &appsv1.StatefulSet{} 519 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 520 Expect(nodeSts.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 521 Expect(*nodeSts.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 522 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 523 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 524 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 525 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 526 })) 527 Expect(nodeSts.Spec.Template.Spec.Containers[0].Image).To(Equal(toCreate.Spec.Image)) 528 }) 529 530 It("Should create node genesis block config", func() { 531 genesisConfig := &corev1.ConfigMap{} 532 expectedExtraData := "0x0000000000000000000000000000000000000000000000000000000000000000d2c21213027cbf4d46c16b55fa98e5252b0487060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" 533 Expect(k8sClient.Get(context.Background(), key, genesisConfig)).To(Succeed()) 534 Expect(genesisConfig.Data["genesis.json"]).To(ContainSubstring(expectedExtraData)) 535 }) 536 537 It("Should allocate correct resources to node statefulset", func() { 538 nodeSts := &appsv1.StatefulSet{} 539 expectedResources := corev1.ResourceRequirements{ 540 Requests: corev1.ResourceList{ 541 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPURequest), 542 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryRequest), 543 }, 544 Limits: corev1.ResourceList{ 545 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPULimit), 546 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryLimit), 547 }, 548 } 549 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 550 Expect(nodeSts.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 551 }) 552 553 It("Should create bootnode data persistent volume with correct resources", func() { 554 nodePVC := &corev1.PersistentVolumeClaim{} 555 expectedResources := corev1.VolumeResourceRequirements{ 556 Requests: corev1.ResourceList{ 557 corev1.ResourceStorage: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeStorageRequest), 558 }, 559 } 560 Expect(k8sClient.Get(context.Background(), key, nodePVC)).To(Succeed()) 561 Expect(nodePVC.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 562 Expect(nodePVC.Spec.Resources).To(Equal(expectedResources)) 563 }) 564 565 It("Should delete node", func() { 566 toDelete := ðereumv1alpha1.Node{} 567 Expect(k8sClient.Get(context.Background(), key, toDelete)).To(Succeed()) 568 Expect(k8sClient.Delete(context.Background(), toDelete)).To(Succeed()) 569 time.Sleep(sleepTime) 570 }) 571 572 It("Should not get node after deletion", func() { 573 fetched := ðereumv1alpha1.Node{} 574 Expect(k8sClient.Get(context.Background(), key, fetched)).ToNot(Succeed()) 575 }) 576 577 if useExistingCluster { 578 It("Should delete node statefulset", func() { 579 nodeSts := &appsv1.StatefulSet{} 580 Expect(k8sClient.Get(context.Background(), key, nodeSts)).ToNot(Succeed()) 581 }) 582 583 It("Should delete node service", func() { 584 nodeSvc := &corev1.Service{} 585 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).ToNot(Succeed()) 586 }) 587 588 It("Should delete besu genesis block configmap", func() { 589 genesisConfig := &corev1.ConfigMap{} 590 genesisKey := types.NamespacedName{ 591 Name: fmt.Sprintf("%s-besu", key.Name), 592 Namespace: key.Namespace, 593 } 594 Expect(k8sClient.Get(context.Background(), genesisKey, genesisConfig)).ToNot(Succeed()) 595 }) 596 } 597 598 It(fmt.Sprintf("should delete %s namespace", ns.Name), func() { 599 Expect(k8sClient.Delete(context.Background(), ns)).Should(Succeed()) 600 }) 601 }) 602 603 Context("private PoW network", func() { 604 ns := &corev1.Namespace{ 605 ObjectMeta: metav1.ObjectMeta{ 606 Name: "pow", 607 }, 608 } 609 key := types.NamespacedName{ 610 Name: "my-pow-node", 611 Namespace: ns.Name, 612 } 613 614 spec := ethereumv1alpha1.NodeSpec{ 615 Genesis: ðereumv1alpha1.Genesis{ 616 ChainID: 55555, 617 NetworkID: networkID, 618 Ethash: ðereumv1alpha1.Ethash{}, 619 }, 620 Client: ethereumv1alpha1.BesuClient, 621 NodePrivateKeySecretName: "nodekey", 622 Logging: sharedAPI.TraceLogs, 623 } 624 625 toCreate := ðereumv1alpha1.Node{ 626 ObjectMeta: metav1.ObjectMeta{ 627 Name: key.Name, 628 Namespace: key.Namespace, 629 }, 630 Spec: spec, 631 } 632 t := true 633 nodeOwnerReference := metav1.OwnerReference{ 634 // TODO: update version 635 APIVersion: "ethereum.kotal.io/v1alpha1", 636 Kind: "Node", 637 Name: toCreate.Name, 638 Controller: &t, 639 BlockOwnerDeletion: &t, 640 } 641 642 It(fmt.Sprintf("should create %s namespace", ns.Name), func() { 643 Expect(k8sClient.Create(context.Background(), ns)).Should(Succeed()) 644 }) 645 646 It("Should create nodekey secret", func() { 647 secret := corev1.Secret{ 648 ObjectMeta: metav1.ObjectMeta{ 649 Name: "nodekey", 650 Namespace: ns.Name, 651 }, 652 StringData: map[string]string{ 653 "key": privatekey, 654 }, 655 } 656 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 657 }) 658 659 It("Should create account private key and password secrets", func() { 660 accountPrivateKeySecret := corev1.Secret{ 661 ObjectMeta: metav1.ObjectMeta{ 662 Name: "my-account-privatekey", 663 Namespace: ns.Name, 664 }, 665 StringData: map[string]string{ 666 "key": accountKey, 667 }, 668 } 669 Expect(k8sClient.Create(context.Background(), &accountPrivateKeySecret)).To(Succeed()) 670 671 accountPasswordSecret := corev1.Secret{ 672 ObjectMeta: metav1.ObjectMeta{ 673 Name: "my-account-password", 674 Namespace: ns.Name, 675 }, 676 StringData: map[string]string{ 677 "password": accountPassword, 678 }, 679 } 680 Expect(k8sClient.Create(context.Background(), &accountPasswordSecret)).To(Succeed()) 681 }) 682 683 It("Should create the network", func() { 684 if !useExistingCluster { 685 toCreate.Default() 686 } 687 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 688 time.Sleep(sleepTime) 689 }) 690 691 It("Should create the node", func() { 692 node := ðereumv1alpha1.Node{} 693 Expect(k8sClient.Get(context.Background(), key, node)).To(Succeed()) 694 Expect(node.Spec).To(Equal(toCreate.Spec)) 695 nodeOwnerReference.UID = node.GetUID() 696 nodeOwnerReference.Name = key.Name 697 }) 698 699 It("Should create node genesis block configmap", func() { 700 config := &corev1.ConfigMap{} 701 Expect(k8sClient.Get(context.Background(), key, config)).To(Succeed()) 702 }) 703 704 It("Should create node service", func() { 705 svc := &corev1.Service{} 706 Expect(k8sClient.Get(context.Background(), key, svc)).To(Succeed()) 707 Expect(svc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 708 Expect(svc.Spec.Ports).To(ContainElements([]corev1.ServicePort{ 709 { 710 Name: "discovery", 711 Port: int32(ethereumv1alpha1.DefaultP2PPort), 712 TargetPort: intstr.FromString("discovery"), 713 Protocol: corev1.ProtocolUDP, 714 }, 715 { 716 Name: "p2p", 717 Port: int32(ethereumv1alpha1.DefaultP2PPort), 718 TargetPort: intstr.FromString("p2p"), 719 Protocol: corev1.ProtocolTCP, 720 }, 721 })) 722 }) 723 724 It("Should create node statefulset with correct arguments", func() { 725 sts := &appsv1.StatefulSet{} 726 Expect(k8sClient.Get(context.Background(), key, sts)).To(Succeed()) 727 Expect(sts.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 728 Expect(*sts.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 729 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 730 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 731 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 732 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 733 })) 734 Expect(sts.Spec.Template.Spec.Containers[0].Image).To(Equal(toCreate.Spec.Image)) 735 }) 736 737 It("Should allocate correct resources to node statefulset", func() { 738 nodeSts := &appsv1.StatefulSet{} 739 expectedResources := corev1.ResourceRequirements{ 740 Requests: corev1.ResourceList{ 741 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPURequest), 742 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryRequest), 743 }, 744 Limits: corev1.ResourceList{ 745 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPULimit), 746 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryLimit), 747 }, 748 } 749 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 750 Expect(nodeSts.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 751 }) 752 753 It("Should create node data persistent volume with correct resources", func() { 754 pvc := &corev1.PersistentVolumeClaim{} 755 expectedResources := corev1.VolumeResourceRequirements{ 756 Requests: corev1.ResourceList{ 757 corev1.ResourceStorage: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeStorageRequest), 758 }, 759 } 760 Expect(k8sClient.Get(context.Background(), key, pvc)).To(Succeed()) 761 Expect(pvc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 762 Expect(pvc.Spec.Resources).To(Equal(expectedResources)) 763 }) 764 765 It("Should delete node", func() { 766 toDelete := ðereumv1alpha1.Node{} 767 Expect(k8sClient.Get(context.Background(), key, toDelete)).To(Succeed()) 768 Expect(k8sClient.Delete(context.Background(), toDelete)).To(Succeed()) 769 time.Sleep(sleepTime) 770 }) 771 772 It("Should not get node after deletion", func() { 773 fetched := ðereumv1alpha1.Node{} 774 Expect(k8sClient.Get(context.Background(), key, fetched)).ToNot(Succeed()) 775 }) 776 777 if useExistingCluster { 778 It("Should delete node statefulset", func() { 779 nodeSts := &appsv1.StatefulSet{} 780 Expect(k8sClient.Get(context.Background(), key, nodeSts)).ToNot(Succeed()) 781 }) 782 783 It("Should delete node service", func() { 784 nodeSvc := &corev1.Service{} 785 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).ToNot(Succeed()) 786 }) 787 788 It("Should delete besu genesis block configmap", func() { 789 genesisConfig := &corev1.ConfigMap{} 790 genesisKey := types.NamespacedName{ 791 Name: fmt.Sprintf("%s-besu", key.Name), 792 Namespace: key.Namespace, 793 } 794 Expect(k8sClient.Get(context.Background(), genesisKey, genesisConfig)).ToNot(Succeed()) 795 }) 796 } 797 798 It(fmt.Sprintf("should delete %s namespace", ns.Name), func() { 799 Expect(k8sClient.Delete(context.Background(), ns)).Should(Succeed()) 800 }) 801 }) 802 803 Context("private ibft2 network", func() { 804 ns := &corev1.Namespace{ 805 ObjectMeta: metav1.ObjectMeta{ 806 Name: "ibft2", 807 }, 808 } 809 key := types.NamespacedName{ 810 Name: "my-ibft2-node", 811 Namespace: ns.Name, 812 } 813 814 spec := ethereumv1alpha1.NodeSpec{ 815 Genesis: ðereumv1alpha1.Genesis{ 816 ChainID: 55555, 817 NetworkID: networkID, 818 IBFT2: ðereumv1alpha1.IBFT2{ 819 Validators: []sharedAPI.EthereumAddress{ 820 "0x427e2c7cecd72bc4cdd4f7ebb8bb6e49789c8044", 821 "0xd2c21213027cbf4d46c16b55fa98e5252b048706", 822 "0x8e1f6c7c76a1d7f74eda342d330ca9749f31cc2b", 823 }, 824 }, 825 }, 826 Client: ethereumv1alpha1.BesuClient, 827 NodePrivateKeySecretName: "nodekey", 828 Logging: sharedAPI.WarnLogs, 829 } 830 831 toCreate := ðereumv1alpha1.Node{ 832 ObjectMeta: metav1.ObjectMeta{ 833 Name: key.Name, 834 Namespace: key.Namespace, 835 }, 836 Spec: spec, 837 } 838 t := true 839 840 nodeOwnerReference := metav1.OwnerReference{ 841 // TODO: update version 842 APIVersion: "ethereum.kotal.io/v1alpha1", 843 Kind: "Node", 844 Controller: &t, 845 BlockOwnerDeletion: &t, 846 } 847 848 It(fmt.Sprintf("should create %s namespace", ns.Name), func() { 849 Expect(k8sClient.Create(context.Background(), ns)).Should(Succeed()) 850 }) 851 852 It("Should create nodekey secret", func() { 853 secret := corev1.Secret{ 854 ObjectMeta: metav1.ObjectMeta{ 855 Name: "nodekey", 856 Namespace: ns.Name, 857 }, 858 StringData: map[string]string{ 859 "key": privatekey, 860 }, 861 } 862 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 863 }) 864 865 It("Should create the node", func() { 866 if !useExistingCluster { 867 toCreate.Default() 868 } 869 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 870 time.Sleep(sleepTime) 871 }) 872 873 It("Should get the node", func() { 874 fetched := ðereumv1alpha1.Node{} 875 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 876 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 877 nodeOwnerReference.UID = fetched.GetUID() 878 nodeOwnerReference.Name = key.Name 879 }) 880 881 It("Should create node genesis block configmap", func() { 882 genesisConfig := &corev1.ConfigMap{} 883 expectedExtraData := "0xf869a00000000000000000000000000000000000000000000000000000000000000000f83f94427e2c7cecd72bc4cdd4f7ebb8bb6e49789c804494d2c21213027cbf4d46c16b55fa98e5252b048706948e1f6c7c76a1d7f74eda342d330ca9749f31cc2b808400000000c0" 884 Expect(k8sClient.Get(context.Background(), key, genesisConfig)).To(Succeed()) 885 Expect(genesisConfig.Data["genesis.json"]).To(ContainSubstring(expectedExtraData)) 886 }) 887 888 It("Should create node service", func() { 889 nodeSvc := &corev1.Service{} 890 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).To(Succeed()) 891 Expect(nodeSvc.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 892 Expect(nodeSvc.Spec.Ports).To(ContainElements([]corev1.ServicePort{ 893 { 894 Name: "discovery", 895 Port: int32(ethereumv1alpha1.DefaultP2PPort), 896 TargetPort: intstr.FromString("discovery"), 897 Protocol: corev1.ProtocolUDP, 898 }, 899 { 900 Name: "p2p", 901 Port: int32(ethereumv1alpha1.DefaultP2PPort), 902 TargetPort: intstr.FromString("p2p"), 903 Protocol: corev1.ProtocolTCP, 904 }, 905 })) 906 }) 907 908 It("Should create node statefulset with correct arguments", func() { 909 nodeSts := &appsv1.StatefulSet{} 910 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 911 Expect(nodeSts.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 912 Expect(*nodeSts.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 913 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 914 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 915 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 916 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 917 })) 918 Expect(nodeSts.Spec.Template.Spec.Containers[0].Image).To(Equal(toCreate.Spec.Image)) 919 }) 920 921 It("Should allocate correct resources to bootnode statefulset", func() { 922 nodeSts := &appsv1.StatefulSet{} 923 expectedResources := corev1.ResourceRequirements{ 924 Requests: corev1.ResourceList{ 925 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPURequest), 926 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryRequest), 927 }, 928 Limits: corev1.ResourceList{ 929 corev1.ResourceCPU: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeCPULimit), 930 corev1.ResourceMemory: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeMemoryLimit), 931 }, 932 } 933 Expect(k8sClient.Get(context.Background(), key, nodeSts)).To(Succeed()) 934 Expect(nodeSts.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 935 }) 936 937 It("Should create bootnode data persistent volume with correct resouces", func() { 938 nodePVC := &corev1.PersistentVolumeClaim{} 939 expectedResources := corev1.VolumeResourceRequirements{ 940 Requests: corev1.ResourceList{ 941 corev1.ResourceStorage: resource.MustParse(ethereumv1alpha1.DefaultPrivateNetworkNodeStorageRequest), 942 }, 943 } 944 Expect(k8sClient.Get(context.Background(), key, nodePVC)).To(Succeed()) 945 Expect(nodePVC.GetOwnerReferences()).To(ContainElement(nodeOwnerReference)) 946 Expect(nodePVC.Spec.Resources).To(Equal(expectedResources)) 947 }) 948 949 It("Should delete node", func() { 950 toDelete := ðereumv1alpha1.Node{} 951 Expect(k8sClient.Get(context.Background(), key, toDelete)).To(Succeed()) 952 Expect(k8sClient.Delete(context.Background(), toDelete)).To(Succeed()) 953 time.Sleep(sleepTime) 954 }) 955 956 It("Should not get network after deletion", func() { 957 fetched := ðereumv1alpha1.Node{} 958 Expect(k8sClient.Get(context.Background(), key, fetched)).ToNot(Succeed()) 959 }) 960 961 if useExistingCluster { 962 It("Should delete node statefulset", func() { 963 nodeSts := &appsv1.StatefulSet{} 964 Expect(k8sClient.Get(context.Background(), key, nodeSts)).ToNot(Succeed()) 965 }) 966 967 It("Should delete node service", func() { 968 nodeSvc := &corev1.Service{} 969 Expect(k8sClient.Get(context.Background(), key, nodeSvc)).ToNot(Succeed()) 970 }) 971 972 It("Should delete genesis block configmap", func() { 973 genesisConfig := &corev1.ConfigMap{} 974 genesisKey := types.NamespacedName{ 975 Name: fmt.Sprintf("%s-besu", key.Name), 976 Namespace: key.Namespace, 977 } 978 Expect(k8sClient.Get(context.Background(), genesisKey, genesisConfig)).ToNot(Succeed()) 979 }) 980 } 981 982 It(fmt.Sprintf("should delete %s namespace", ns.Name), func() { 983 Expect(k8sClient.Delete(context.Background(), ns)).Should(Succeed()) 984 }) 985 }) 986 987 })