github.com/kotalco/kotal@v0.3.0/controllers/stacks/node_controller_test.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "time" 8 9 stacksv1alpha1 "github.com/kotalco/kotal/apis/stacks/v1alpha1" 10 stacksClients "github.com/kotalco/kotal/clients/stacks" 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("Stacks node controller", func() { 24 ns := &corev1.Namespace{ 25 ObjectMeta: metav1.ObjectMeta{ 26 Name: "stacks", 27 }, 28 } 29 30 key := types.NamespacedName{ 31 Name: "stacks-node", 32 Namespace: ns.Name, 33 } 34 35 testImage := "kotalco/stacks:controller-test" 36 37 spec := stacksv1alpha1.NodeSpec{ 38 Image: testImage, 39 Network: stacksv1alpha1.Mainnet, 40 BitcoinNode: stacksv1alpha1.BitcoinNode{ 41 Endpoint: "bitcoin.blockstack.com", 42 P2pPort: 8332, 43 RpcPort: 8333, 44 RpcUsername: "blockstack", 45 RpcPasswordSecretName: "bitcoin-node-rpc-password", 46 }, 47 } 48 49 toCreate := &stacksv1alpha1.Node{ 50 ObjectMeta: metav1.ObjectMeta{ 51 Name: key.Name, 52 Namespace: key.Namespace, 53 }, 54 Spec: spec, 55 } 56 57 client := stacksClients.NewClient(toCreate) 58 59 t := true 60 61 nodeOwnerReference := metav1.OwnerReference{ 62 APIVersion: "stacks.kotal.io/v1alpha1", 63 Kind: "Node", 64 Name: toCreate.Name, 65 Controller: &t, 66 BlockOwnerDeletion: &t, 67 } 68 69 It(fmt.Sprintf("Should create %s namespace", ns.Name), func() { 70 Expect(k8sClient.Create(context.TODO(), ns)).To(Succeed()) 71 }) 72 73 It("Should create Bitcoin node rpc password secret", func() { 74 secret := corev1.Secret{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: "bitcoin-node-rpc-password", 77 Namespace: ns.Name, 78 }, 79 StringData: map[string]string{ 80 "password": "blockstacksystem", 81 }, 82 } 83 Expect(k8sClient.Create(context.Background(), &secret)).To(Succeed()) 84 }) 85 86 It("should create Stacks node", func() { 87 if os.Getenv(shared.EnvUseExistingCluster) != "true" { 88 toCreate.Default() 89 } 90 Expect(k8sClient.Create(context.Background(), toCreate)).Should(Succeed()) 91 }) 92 93 It("Should get Stacks node", func() { 94 fetched := &stacksv1alpha1.Node{} 95 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 96 Expect(fetched.Spec).To(Equal(toCreate.Spec)) 97 nodeOwnerReference.UID = fetched.UID 98 time.Sleep(5 * time.Second) 99 }) 100 101 It("Should create peer configmap", func() { 102 fetched := &corev1.ConfigMap{} 103 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 104 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 105 Expect(fetched.Data).To(HaveKey("config.toml")) 106 }) 107 108 It("Should create node statefulset", func() { 109 fetched := &appsv1.StatefulSet{} 110 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 111 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 112 Expect(*fetched.Spec.Template.Spec.SecurityContext).To(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ 113 "RunAsUser": gstruct.PointTo(Equal(int64(1000))), 114 "RunAsGroup": gstruct.PointTo(Equal(int64(3000))), 115 "FSGroup": gstruct.PointTo(Equal(int64(2000))), 116 "RunAsNonRoot": gstruct.PointTo(Equal(true)), 117 })) 118 Expect(fetched.Spec.Template.Spec.Containers[0].Name).To(Equal("node")) 119 Expect(fetched.Spec.Template.Spec.Containers[0].Image).To(Equal(testImage)) 120 Expect(fetched.Spec.Template.Spec.Containers[0].Env).To(Equal(client.Env())) 121 Expect(fetched.Spec.Template.Spec.Containers[0].Command).To(Equal(client.Command())) 122 Expect(fetched.Spec.Template.Spec.Containers[0].Args).To(Equal(client.Args())) 123 Expect(fetched.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElements( 124 corev1.VolumeMount{ 125 Name: "data", 126 MountPath: shared.PathData(client.HomeDir()), 127 }, 128 corev1.VolumeMount{ 129 Name: "config", 130 ReadOnly: true, 131 MountPath: shared.PathConfig(client.HomeDir()), 132 }, 133 )) 134 // volumes 135 mode := corev1.ConfigMapVolumeSourceDefaultMode 136 Expect(fetched.Spec.Template.Spec.Volumes).To(ContainElements( 137 []corev1.Volume{ 138 { 139 Name: "data", 140 VolumeSource: corev1.VolumeSource{ 141 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 142 ClaimName: toCreate.Name, 143 }, 144 }, 145 }, 146 { 147 Name: "config", 148 VolumeSource: corev1.VolumeSource{ 149 ConfigMap: &corev1.ConfigMapVolumeSource{ 150 LocalObjectReference: corev1.LocalObjectReference{ 151 Name: toCreate.Name, 152 }, 153 DefaultMode: &mode, 154 }, 155 }, 156 }, 157 }, 158 )) 159 }) 160 161 It("Should create allocate correct resources to node statefulset", func() { 162 fetched := &appsv1.StatefulSet{} 163 expectedResources := corev1.ResourceRequirements{ 164 Requests: corev1.ResourceList{ 165 corev1.ResourceCPU: resource.MustParse(stacksv1alpha1.DefaultNodeCPURequest), 166 corev1.ResourceMemory: resource.MustParse(stacksv1alpha1.DefaultNodeMemoryRequest), 167 }, 168 Limits: corev1.ResourceList{ 169 corev1.ResourceCPU: resource.MustParse(stacksv1alpha1.DefaultNodeCPULimit), 170 corev1.ResourceMemory: resource.MustParse(stacksv1alpha1.DefaultNodeMemoryLimit), 171 }, 172 } 173 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 174 Expect(fetched.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResources)) 175 }) 176 177 It("Should create node data persistent volume with correct resources", func() { 178 fetched := &corev1.PersistentVolumeClaim{} 179 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 180 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 181 expectedResources := corev1.VolumeResourceRequirements{ 182 Requests: corev1.ResourceList{ 183 corev1.ResourceStorage: resource.MustParse(stacksv1alpha1.DefaultNodeStorageRequest), 184 }, 185 } 186 Expect(fetched.Spec.Resources).To(Equal(expectedResources)) 187 }) 188 189 It("Should create node service", func() { 190 fetched := &corev1.Service{} 191 Expect(k8sClient.Get(context.Background(), key, fetched)).To(Succeed()) 192 Expect(fetched.OwnerReferences).To(ContainElements(nodeOwnerReference)) 193 Expect(fetched.Spec.Ports).To(ContainElements( 194 []corev1.ServicePort{ 195 { 196 Name: "p2p", 197 Port: int32(stacksv1alpha1.DefaultP2PPort), 198 TargetPort: intstr.FromString("p2p"), 199 Protocol: corev1.ProtocolTCP, 200 }, 201 { 202 Name: "rpc", 203 Port: int32(stacksv1alpha1.DefaultRPCPort), 204 TargetPort: intstr.FromString("rpc"), 205 Protocol: corev1.ProtocolTCP, 206 }, 207 }, 208 )) 209 }) 210 211 It(fmt.Sprintf("Should delete %s namespace", ns.Name), func() { 212 Expect(k8sClient.Delete(context.Background(), ns)).To(Succeed()) 213 }) 214 215 })