github.com/kotalco/kotal@v0.3.0/controllers/near/node_controller_test.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "time" 8 9 nearv1alpha1 "github.com/kotalco/kotal/apis/near/v1alpha1" 10 nearClients "github.com/kotalco/kotal/clients/near" 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("NEAR node controller", func() { 24 ns := &corev1.Namespace{ 25 ObjectMeta: metav1.ObjectMeta{ 26 Name: "near", 27 }, 28 } 29 30 key := types.NamespacedName{ 31 Name: "near-node", 32 Namespace: ns.Name, 33 } 34 35 testImage := "kotalco/nearcore:controller-test" 36 37 spec := nearv1alpha1.NodeSpec{ 38 Image: testImage, 39 Network: "mainnet", 40 RPC: true, 41 Archive: true, // test volume storage size 42 NodePrivateKeySecretName: "my-node-key", 43 ValidatorSecretName: "validator-key", 44 } 45 46 toCreate := &nearv1alpha1.Node{ 47 ObjectMeta: metav1.ObjectMeta{ 48 Name: key.Name, 49 Namespace: key.Namespace, 50 }, 51 Spec: spec, 52 } 53 54 client := nearClients.NewClient(toCreate) 55 56 t := true 57 58 nodeOwnerReference := metav1.OwnerReference{ 59 APIVersion: "near.kotal.io/v1alpha1", 60 Kind: "Node", 61 Name: toCreate.Name, 62 Controller: &t, 63 BlockOwnerDeletion: &t, 64 } 65 66 It(fmt.Sprintf("Should create %s namespace", ns.Name), func() { 67 Expect(k8sClient.Create(context.TODO(), ns)).To(Succeed()) 68 }) 69 70 It("should create NEAR node", func() { 71 if os.Getenv(shared.EnvUseExistingCluster) != "true" { 72 toCreate.Default() 73 } 74 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 75 }) 76 77 It("Should get NEAR node", func() { 78 fetched := &nearv1alpha1.Node{} 79 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 80 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 81 nodeOwnerReference.UID = fetched.UID 82 time.Sleep(5 * time.Second) 83 }) 84 85 It("Should create node statefulset", func() { 86 fetched := &appsv1.StatefulSet{} 87 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 88 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 89 Expect(*fetched.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 90 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 91 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 92 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 93 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 94 })) 95 // init near node 96 Expect(fetched.Spec.Template.Spec.InitContainers[0].Name).To(Equal("init-near-node")) 97 Expect(fetched.Spec.Template.Spec.InitContainers[0].Image).To(Equal(testImage)) 98 Expect(fetched.Spec.Template.Spec.InitContainers[0].Command).To(Equal([]string{"/bin/sh"})) 99 Expect(fetched.Spec.Template.Spec.InitContainers[0].Args).To(ContainElements( 100 fmt.Sprintf("%s/init_near_node.sh", shared.PathConfig(client.HomeDir())), 101 )) 102 Expect(fetched.Spec.Template.Spec.InitContainers[0].Env).To(ContainElements( 103 corev1.EnvVar{ 104 Name: shared.EnvDataPath, 105 Value: shared.PathData(client.HomeDir()), 106 }, 107 corev1.EnvVar{ 108 Name: envNetwork, 109 Value: toCreate.Spec.Network, 110 }, 111 )) 112 Expect(fetched.Spec.Template.Spec.InitContainers[0].VolumeMounts).To(ContainElements( 113 corev1.VolumeMount{ 114 Name: "data", 115 MountPath: shared.PathData(client.HomeDir()), 116 }, 117 corev1.VolumeMount{ 118 Name: "config", 119 MountPath: shared.PathConfig(client.HomeDir()), 120 }, 121 corev1.VolumeMount{ 122 Name: "secrets", 123 MountPath: shared.PathSecrets(client.HomeDir()), 124 }, 125 )) 126 // copy node key 127 Expect(fetched.Spec.Template.Spec.InitContainers[1].Name).To(Equal("copy-node-key")) 128 Expect(fetched.Spec.Template.Spec.InitContainers[1].Image).To(Equal(shared.BusyboxImage)) 129 Expect(fetched.Spec.Template.Spec.InitContainers[1].Command).To(Equal([]string{"/bin/sh"})) 130 Expect(fetched.Spec.Template.Spec.InitContainers[1].Args).To(ContainElements( 131 fmt.Sprintf("%s/copy_node_key.sh", shared.PathConfig(client.HomeDir())), 132 )) 133 Expect(fetched.Spec.Template.Spec.InitContainers[1].Env).To(ContainElements( 134 corev1.EnvVar{ 135 Name: shared.EnvDataPath, 136 Value: shared.PathData(client.HomeDir()), 137 }, 138 corev1.EnvVar{ 139 Name: shared.EnvSecretsPath, 140 Value: shared.PathSecrets(client.HomeDir()), 141 }, 142 )) 143 Expect(fetched.Spec.Template.Spec.InitContainers[1].VolumeMounts).To(ContainElements( 144 corev1.VolumeMount{ 145 Name: "data", 146 MountPath: shared.PathData(client.HomeDir()), 147 }, 148 corev1.VolumeMount{ 149 Name: "config", 150 MountPath: shared.PathConfig(client.HomeDir()), 151 }, 152 corev1.VolumeMount{ 153 Name: "secrets", 154 MountPath: shared.PathSecrets(client.HomeDir()), 155 }, 156 )) 157 // copy validator key 158 Expect(fetched.Spec.Template.Spec.InitContainers[2].Name).To(Equal("copy-validator-key")) 159 Expect(fetched.Spec.Template.Spec.InitContainers[2].Image).To(Equal(shared.BusyboxImage)) 160 Expect(fetched.Spec.Template.Spec.InitContainers[2].Command).To(Equal([]string{"/bin/sh"})) 161 Expect(fetched.Spec.Template.Spec.InitContainers[2].Args).To(ContainElements( 162 fmt.Sprintf("%s/copy_validator_key.sh", shared.PathConfig(client.HomeDir())), 163 )) 164 Expect(fetched.Spec.Template.Spec.InitContainers[2].Env).To(ContainElements( 165 corev1.EnvVar{ 166 Name: shared.EnvDataPath, 167 Value: shared.PathData(client.HomeDir()), 168 }, 169 corev1.EnvVar{ 170 Name: shared.EnvSecretsPath, 171 Value: shared.PathSecrets(client.HomeDir()), 172 }, 173 )) 174 Expect(fetched.Spec.Template.Spec.InitContainers[2].VolumeMounts).To(ContainElements( 175 corev1.VolumeMount{ 176 Name: "data", 177 MountPath: shared.PathData(client.HomeDir()), 178 }, 179 corev1.VolumeMount{ 180 Name: "config", 181 MountPath: shared.PathConfig(client.HomeDir()), 182 }, 183 corev1.VolumeMount{ 184 Name: "secrets", 185 MountPath: shared.PathSecrets(client.HomeDir()), 186 }, 187 )) 188 // node 189 Expect(fetched.Spec.Template.Spec.Containers[0].Name).To(Equal("node")) 190 Expect(fetched.Spec.Template.Spec.Containers[0].Image).To(Equal(testImage)) 191 Expect(fetched.Spec.Template.Spec.Containers[0].Command).To(Equal(client.Command())) 192 Expect(fetched.Spec.Template.Spec.Containers[0].Args).To(Equal(client.Args())) 193 Expect(fetched.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElements( 194 corev1.VolumeMount{ 195 Name: "data", 196 MountPath: shared.PathData(client.HomeDir()), 197 }, 198 corev1.VolumeMount{ 199 Name: "config", 200 MountPath: shared.PathConfig(client.HomeDir()), 201 }, 202 corev1.VolumeMount{ 203 Name: "secrets", 204 MountPath: shared.PathSecrets(client.HomeDir()), 205 }, 206 )) 207 // volumes 208 mode := corev1.ConfigMapVolumeSourceDefaultMode 209 Expect(fetched.Spec.Template.Spec.Volumes).To(ContainElements( 210 []corev1.Volume{ 211 { 212 Name: "data", 213 VolumeSource: corev1.VolumeSource{ 214 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 215 ClaimName: toCreate.Name, 216 }, 217 }, 218 }, 219 { 220 Name: "config", 221 VolumeSource: corev1.VolumeSource{ 222 ConfigMap: &corev1.ConfigMapVolumeSource{ 223 LocalObjectReference: corev1.LocalObjectReference{ 224 Name: toCreate.Name, 225 }, 226 DefaultMode: &mode, 227 }, 228 }, 229 }, 230 { 231 Name: "secrets", 232 VolumeSource: corev1.VolumeSource{ 233 Projected: &corev1.ProjectedVolumeSource{ 234 Sources: []corev1.VolumeProjection{ 235 { 236 Secret: &corev1.SecretProjection{ 237 LocalObjectReference: corev1.LocalObjectReference{ 238 Name: toCreate.Spec.NodePrivateKeySecretName, 239 }, 240 Items: []corev1.KeyToPath{ 241 { 242 Key: "key", 243 Path: "node_key.json", 244 }, 245 }, 246 }, 247 }, 248 { 249 Secret: &corev1.SecretProjection{ 250 LocalObjectReference: corev1.LocalObjectReference{ 251 Name: toCreate.Spec.ValidatorSecretName, 252 }, 253 Items: []corev1.KeyToPath{ 254 { 255 Key: "key", 256 Path: "validator_key.json", 257 }, 258 }, 259 }, 260 }, 261 }, 262 DefaultMode: &mode, 263 }, 264 }, 265 }, 266 }, 267 )) 268 }) 269 270 It("Should create allocate correct resources to peer statefulset", func() { 271 fetched := &appsv1.StatefulSet{} 272 expectedResources := corev1.ResourceRequirements{ 273 Requests: corev1.ResourceList{ 274 corev1.ResourceCPU: resource.MustParse(nearv1alpha1.DefaultNodeCPURequest), 275 corev1.ResourceMemory: resource.MustParse(nearv1alpha1.DefaultNodeMemoryRequest), 276 }, 277 Limits: corev1.ResourceList{ 278 corev1.ResourceCPU: resource.MustParse(nearv1alpha1.DefaultNodeCPULimit), 279 corev1.ResourceMemory: resource.MustParse(nearv1alpha1.DefaultNodeMemoryLimit), 280 }, 281 } 282 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 283 Expect(fetched.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 284 }) 285 286 It("Should create node configmap", func() { 287 fetched := &corev1.ConfigMap{} 288 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 289 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 290 Expect(fetched.Data).To(HaveKey("init_near_node.sh")) 291 Expect(fetched.Data).To(HaveKey("copy_node_key.sh")) 292 293 }) 294 295 It("Should create node data persistent volume with correct resources", func() { 296 fetched := &corev1.PersistentVolumeClaim{} 297 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 298 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 299 expectedResources := corev1.VolumeResourceRequirements{ 300 Requests: corev1.ResourceList{ 301 corev1.ResourceStorage: resource.MustParse(nearv1alpha1.DefaultArchivalNodeStorageRequest), 302 }, 303 } 304 Expect(fetched.Spec.Resources).To(Equal(expectedResources)) 305 }) 306 307 It("Should create node service", func() { 308 fetched := &corev1.Service{} 309 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 310 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 311 Expect(fetched.Spec.Ports).To(ContainElements( 312 []corev1.ServicePort{ 313 { 314 Name: "p2p", 315 Port: int32(nearv1alpha1.DefaultP2PPort), 316 TargetPort: intstr.FromString("p2p"), 317 Protocol: corev1.ProtocolTCP, 318 }, 319 { 320 Name: "discovery", 321 Port: int32(nearv1alpha1.DefaultP2PPort), 322 TargetPort: intstr.FromString("discovery"), 323 Protocol: corev1.ProtocolUDP, 324 }, 325 { 326 Name: "prometheus", 327 Port: int32(nearv1alpha1.DefaultPrometheusPort), 328 TargetPort: intstr.FromString("prometheus"), 329 Protocol: corev1.ProtocolTCP, 330 }, 331 { 332 Name: "rpc", 333 Port: int32(nearv1alpha1.DefaultRPCPort), 334 TargetPort: intstr.FromString("rpc"), 335 Protocol: corev1.ProtocolTCP, 336 }, 337 }, 338 )) 339 }) 340 341 It(fmt.Sprintf("Should delete %s namespace", ns.Name), func() { 342 Expect(k8sClient.Delete(context.Background(), ns)).To(Succeed()) 343 }) 344 345 })