github.com/kotalco/kotal@v0.3.0/controllers/bitcoin/node_controller_test.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "time" 8 9 bitcoinv1alpha1 "github.com/kotalco/kotal/apis/bitcoin/v1alpha1" 10 bitcoinClients "github.com/kotalco/kotal/clients/bitcoin" 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("Bitcoin node controller", func() { 24 ns := &corev1.Namespace{ 25 ObjectMeta: metav1.ObjectMeta{ 26 Name: "bitcoin", 27 }, 28 } 29 30 key := types.NamespacedName{ 31 Name: "bitcoin-node", 32 Namespace: ns.Name, 33 } 34 35 testImage := "kotalco/bitcoin-core:controller-test" 36 37 spec := bitcoinv1alpha1.NodeSpec{ 38 Image: testImage, 39 Network: bitcoinv1alpha1.Mainnet, 40 RPC: true, 41 } 42 43 toCreate := &bitcoinv1alpha1.Node{ 44 ObjectMeta: metav1.ObjectMeta{ 45 Name: key.Name, 46 Namespace: key.Namespace, 47 }, 48 Spec: spec, 49 } 50 51 client := bitcoinClients.NewClient(toCreate, nil) 52 53 t := true 54 55 nodeOwnerReference := metav1.OwnerReference{ 56 APIVersion: "bitcoin.kotal.io/v1alpha1", 57 Kind: "Node", 58 Name: toCreate.Name, 59 Controller: &t, 60 BlockOwnerDeletion: &t, 61 } 62 63 It(fmt.Sprintf("Should create %s namespace", ns.Name), func() { 64 Expect(k8sClient.Create(context.TODO(), ns)).To(Succeed()) 65 }) 66 67 It("should create Bitcoin node", func() { 68 if os.Getenv(shared.EnvUseExistingCluster) != "true" { 69 toCreate.Default() 70 } 71 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 72 }) 73 74 It("Should get Bitcoin node", func() { 75 fetched := &bitcoinv1alpha1.Node{} 76 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 77 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 78 nodeOwnerReference.UID = fetched.UID 79 time.Sleep(5 * time.Second) 80 }) 81 82 It("Should create node statefulset", func() { 83 fetched := &appsv1.StatefulSet{} 84 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 85 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 86 Expect(*fetched.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 87 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 88 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 89 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 90 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 91 })) 92 Expect(fetched.Spec.Template.Spec.Containers[0].Name).To(Equal("node")) 93 Expect(fetched.Spec.Template.Spec.Containers[0].Image).To(Equal(testImage)) 94 Expect(fetched.Spec.Template.Spec.Containers[0].Env).To(Equal(client.Env())) 95 Expect(fetched.Spec.Template.Spec.Containers[0].Command).To(Equal(client.Command())) 96 Expect(fetched.Spec.Template.Spec.Containers[0].Args).To(Equal(client.Args())) 97 Expect(fetched.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElements( 98 corev1.VolumeMount{ 99 Name: "data", 100 MountPath: shared.PathData(client.HomeDir()), 101 }, 102 )) 103 // volumes 104 Expect(fetched.Spec.Template.Spec.Volumes).To(ContainElements( 105 []corev1.Volume{ 106 { 107 Name: "data", 108 VolumeSource: corev1.VolumeSource{ 109 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 110 ClaimName: toCreate.Name, 111 }, 112 }, 113 }, 114 }, 115 )) 116 }) 117 118 It("Should create allocate correct resources to node statefulset", func() { 119 fetched := &appsv1.StatefulSet{} 120 expectedResources := corev1.ResourceRequirements{ 121 Requests: corev1.ResourceList{ 122 corev1.ResourceCPU: resource.MustParse(bitcoinv1alpha1.DefaultNodeCPURequest), 123 corev1.ResourceMemory: resource.MustParse(bitcoinv1alpha1.DefaultNodeMemoryRequest), 124 }, 125 Limits: corev1.ResourceList{ 126 corev1.ResourceCPU: resource.MustParse(bitcoinv1alpha1.DefaultNodeCPULimit), 127 corev1.ResourceMemory: resource.MustParse(bitcoinv1alpha1.DefaultNodeMemoryLimit), 128 }, 129 } 130 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 131 Expect(fetched.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 132 }) 133 134 It("Should create node data persistent volume with correct resources", func() { 135 fetched := &corev1.PersistentVolumeClaim{} 136 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 137 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 138 expectedResources := corev1.VolumeResourceRequirements{ 139 Requests: corev1.ResourceList{ 140 corev1.ResourceStorage: resource.MustParse(bitcoinv1alpha1.DefaultNodeStorageRequest), 141 }, 142 } 143 Expect(fetched.Spec.Resources).To(Equal(expectedResources)) 144 }) 145 146 It("Should create node service", func() { 147 fetched := &corev1.Service{} 148 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 149 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 150 Expect(fetched.Spec.Ports).To(ContainElements( 151 []corev1.ServicePort{ 152 { 153 Name: "p2p", 154 Port: int32(bitcoinv1alpha1.DefaultMainnetP2PPort), 155 TargetPort: intstr.FromString("p2p"), 156 Protocol: corev1.ProtocolTCP, 157 }, 158 { 159 Name: "rpc", 160 Port: int32(bitcoinv1alpha1.DefaultMainnetRPCPort), 161 TargetPort: intstr.FromString("rpc"), 162 Protocol: corev1.ProtocolTCP, 163 }, 164 }, 165 )) 166 }) 167 168 It(fmt.Sprintf("Should delete %s namespace", ns.Name), func() { 169 Expect(k8sClient.Delete(context.Background(), ns)).To(Succeed()) 170 }) 171 172 })