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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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: &ethereumv1alpha1.Genesis{
   412  				ChainID:   55555,
   413  				NetworkID: networkID,
   414  				Clique: &ethereumv1alpha1.Clique{
   415  					Signers: []sharedAPI.EthereumAddress{
   416  						sharedAPI.EthereumAddress("0xd2c21213027cbf4d46c16b55fa98e5252b048706"),
   417  					},
   418  				},
   419  			},
   420  			Client:                   ethereumv1alpha1.BesuClient,
   421  			NodePrivateKeySecretName: "nodekey",
   422  		}
   423  
   424  		toCreate := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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: &ethereumv1alpha1.Genesis{
   616  				ChainID:   55555,
   617  				NetworkID: networkID,
   618  				Ethash:    &ethereumv1alpha1.Ethash{},
   619  			},
   620  			Client:                   ethereumv1alpha1.BesuClient,
   621  			NodePrivateKeySecretName: "nodekey",
   622  			Logging:                  sharedAPI.TraceLogs,
   623  		}
   624  
   625  		toCreate := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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: &ethereumv1alpha1.Genesis{
   816  				ChainID:   55555,
   817  				NetworkID: networkID,
   818  				IBFT2: &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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 := &ethereumv1alpha1.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  })