github.com/argoproj/argo-events@v1.9.1/controllers/eventbus/installer/nats.go (about) 1 package installer 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "strings" 8 "time" 9 10 "go.uber.org/zap" 11 appv1 "k8s.io/api/apps/v1" 12 corev1 "k8s.io/api/core/v1" 13 apierrors "k8s.io/apimachinery/pkg/api/errors" 14 apiresource "k8s.io/apimachinery/pkg/api/resource" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/labels" 17 "k8s.io/apimachinery/pkg/runtime/schema" 18 "k8s.io/apimachinery/pkg/util/intstr" 19 "k8s.io/client-go/kubernetes" 20 "sigs.k8s.io/controller-runtime/pkg/client" 21 22 "github.com/argoproj/argo-events/common" 23 "github.com/argoproj/argo-events/controllers" 24 controllerscommon "github.com/argoproj/argo-events/controllers/common" 25 "github.com/argoproj/argo-events/pkg/apis/eventbus/v1alpha1" 26 ) 27 28 const ( 29 clientPort = int32(4222) 30 clusterPort = int32(6222) 31 monitorPort = int32(8222) 32 33 // annotation key on serverAuthSecret and clientAuthsecret 34 authStrategyAnnoKey = "strategy" 35 // key of client auth secret 36 clientAuthSecretKey = "client-auth" 37 // key of server auth secret 38 serverAuthSecretKey = "auth" 39 // key of stan.conf in the configmap 40 configMapKey = "stan-config" 41 42 // default nats streaming version to be installed 43 defaultSTANVersion = "0.22.1" 44 ) 45 46 // natsInstaller is used create a NATS installation. 47 type natsInstaller struct { 48 client client.Client 49 kubeClient kubernetes.Interface 50 eventBus *v1alpha1.EventBus 51 config *controllers.GlobalConfig 52 labels map[string]string 53 logger *zap.SugaredLogger 54 } 55 56 // NewNATSInstaller returns a new NATS installer 57 func NewNATSInstaller(client client.Client, eventBus *v1alpha1.EventBus, config *controllers.GlobalConfig, labels map[string]string, kubeClient kubernetes.Interface, logger *zap.SugaredLogger) Installer { 58 return &natsInstaller{ 59 client: client, 60 kubeClient: kubeClient, 61 eventBus: eventBus, 62 config: config, 63 labels: labels, 64 logger: logger.Named("nats"), 65 } 66 } 67 68 // Install creats a StatefulSet and a Service for NATS 69 func (i *natsInstaller) Install(ctx context.Context) (*v1alpha1.BusConfig, error) { 70 natsObj := i.eventBus.Spec.NATS 71 if natsObj == nil || natsObj.Native == nil { 72 return nil, fmt.Errorf("invalid request") 73 } 74 75 svc, err := i.createStanService(ctx) 76 if err != nil { 77 return nil, err 78 } 79 cm, err := i.createConfigMap(ctx) 80 if err != nil { 81 return nil, err 82 } 83 // default to none 84 defaultAuthStrategy := v1alpha1.AuthStrategyNone 85 authStrategy := natsObj.Native.Auth 86 if authStrategy == nil { 87 authStrategy = &defaultAuthStrategy 88 } 89 serverAuthSecret, clientAuthSecret, err := i.createAuthSecrets(ctx, *authStrategy) 90 if err != nil { 91 return nil, err 92 } 93 94 if err := i.createStatefulSet(ctx, svc.Name, cm.Name, serverAuthSecret.Name); err != nil { 95 return nil, err 96 } 97 i.eventBus.Status.MarkDeployed("Succeeded", "NATS is deployed") 98 clusterID := generateClusterID(i.eventBus) 99 busConfig := &v1alpha1.BusConfig{ 100 NATS: &v1alpha1.NATSConfig{ 101 URL: fmt.Sprintf("nats://%s:%s", generateServiceName(i.eventBus), strconv.Itoa(int(clientPort))), 102 ClusterID: &clusterID, 103 Auth: authStrategy, 104 }, 105 } 106 if *authStrategy != v1alpha1.AuthStrategyNone { 107 busConfig.NATS.AccessSecret = &corev1.SecretKeySelector{ 108 Key: clientAuthSecretKey, 109 LocalObjectReference: corev1.LocalObjectReference{ 110 Name: clientAuthSecret.Name, 111 }, 112 } 113 } 114 return busConfig, nil 115 } 116 117 // Uninstall deletes those objects not handeled by cascade deletion. 118 func (i *natsInstaller) Uninstall(ctx context.Context) error { 119 return i.uninstallPVCs(ctx) 120 } 121 122 func (i *natsInstaller) uninstallPVCs(ctx context.Context) error { 123 // StatefulSet doesn't clean up PVC, needs to do it separately 124 // https://github.com/kubernetes/kubernetes/issues/55045 125 log := i.logger 126 pvcs, err := i.getPVCs(ctx, i.labels) 127 if err != nil { 128 log.Errorw("Failed to get PVCs created by nats streaming statefulset when uninstalling", zap.Error(err)) 129 return err 130 } 131 for _, pvc := range pvcs { 132 err = i.client.Delete(ctx, &pvc) 133 if err != nil { 134 log.Errorw("Failed to delete pvc when uninstalling", zap.Any("pvcName", pvc.Name), zap.Error(err)) 135 return err 136 } 137 log.Infow("Pvc deleted", "pvcName", pvc.Name) 138 } 139 return nil 140 } 141 142 // Create a service for nats streaming 143 func (i *natsInstaller) createStanService(ctx context.Context) (*corev1.Service, error) { 144 log := i.logger 145 svc, err := i.getStanService(ctx) 146 if err != nil && !apierrors.IsNotFound(err) { 147 i.eventBus.Status.MarkDeployFailed("GetServiceFailed", "Get existing service failed") 148 log.Errorw("Error getting existing service", zap.Error(err)) 149 return nil, err 150 } 151 expectedSvc, err := i.buildStanService() 152 if err != nil { 153 i.eventBus.Status.MarkDeployFailed("BuildServiceFailed", "Failed to build a service spec") 154 log.Errorw("Error building service spec", zap.Error(err)) 155 return nil, err 156 } 157 if svc != nil { 158 // TODO: potential issue here - if service spec is updated manually, reconciler will not change it back. 159 // Revisit it later to see if it is needed to compare the spec. 160 if svc.Annotations != nil && svc.Annotations[common.AnnotationResourceSpecHash] != expectedSvc.Annotations[common.AnnotationResourceSpecHash] { 161 svc.Spec = expectedSvc.Spec 162 svc.SetLabels(expectedSvc.Labels) 163 svc.Annotations[common.AnnotationResourceSpecHash] = expectedSvc.Annotations[common.AnnotationResourceSpecHash] 164 err = i.client.Update(ctx, svc) 165 if err != nil { 166 i.eventBus.Status.MarkDeployFailed("UpdateServiceFailed", "Failed to update existing service") 167 log.Errorw("Error updating existing service", zap.Error(err)) 168 return nil, err 169 } 170 log.Infow("Service is updated", "serviceName", svc.Name) 171 } 172 return svc, nil 173 } 174 err = i.client.Create(ctx, expectedSvc) 175 if err != nil { 176 i.eventBus.Status.MarkDeployFailed("CreateServiceFailed", "Failed to create a service") 177 log.Errorw("Error creating a service", zap.Error(err)) 178 return nil, err 179 } 180 log.Infow("Service is created", "serviceName", expectedSvc.Name) 181 return expectedSvc, nil 182 } 183 184 // Create a Configmap for NATS config 185 func (i *natsInstaller) createConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { 186 log := i.logger 187 cm, err := i.getConfigMap(ctx) 188 if err != nil && !apierrors.IsNotFound(err) { 189 i.eventBus.Status.MarkDeployFailed("GetConfigMapFailed", "Failed to get existing configmap") 190 log.Errorw("Error getting existing configmap", zap.Error(err)) 191 return nil, err 192 } 193 expectedCm, err := i.buildConfigMap() 194 if err != nil { 195 i.eventBus.Status.MarkDeployFailed("BuildConfigMapFailed", "Failed to build a configmap spec") 196 log.Errorw("Error building configmap spec", zap.Error(err)) 197 return nil, err 198 } 199 if cm != nil { 200 // TODO: Potential issue about comparing hash 201 if cm.Annotations != nil && cm.Annotations[common.AnnotationResourceSpecHash] != expectedCm.Annotations[common.AnnotationResourceSpecHash] { 202 cm.Data = expectedCm.Data 203 cm.SetLabels(expectedCm.Labels) 204 cm.Annotations[common.AnnotationResourceSpecHash] = expectedCm.Annotations[common.AnnotationResourceSpecHash] 205 err := i.client.Update(ctx, cm) 206 if err != nil { 207 i.eventBus.Status.MarkDeployFailed("UpdateConfigMapFailed", "Failed to update existing configmap") 208 log.Errorw("Error updating configmap", zap.Error(err)) 209 return nil, err 210 } 211 log.Infow("Updated configmap", "configmapName", cm.Name) 212 } 213 return cm, nil 214 } 215 err = i.client.Create(ctx, expectedCm) 216 if err != nil { 217 i.eventBus.Status.MarkDeployFailed("CreateConfigMapFailed", "Failed to create configmap") 218 log.Errorw("Error creating a configmap", zap.Error(err)) 219 return nil, err 220 } 221 log.Infow("Created configmap", "configmapName", expectedCm.Name) 222 return expectedCm, nil 223 } 224 225 // create server and client auth secrets 226 func (i *natsInstaller) createAuthSecrets(ctx context.Context, strategy v1alpha1.AuthStrategy) (*corev1.Secret, *corev1.Secret, error) { 227 log := i.logger 228 sSecret, err := i.getServerAuthSecret(ctx) 229 if err != nil && !apierrors.IsNotFound(err) { 230 i.eventBus.Status.MarkDeployFailed("GetServerAuthSecretFailed", "Failed to get existing server auth secret") 231 log.Errorw("Error getting existing server auth secret", zap.Error(err)) 232 return nil, nil, err 233 } 234 cSecret, err := i.getClientAuthSecret(ctx) 235 if err != nil && !apierrors.IsNotFound(err) { 236 i.eventBus.Status.MarkDeployFailed("GetClientAuthSecretFailed", "Failed to get existing client auth secret") 237 log.Errorw("Error getting existing client auth secret", zap.Error(err)) 238 return nil, nil, err 239 } 240 if strategy != v1alpha1.AuthStrategyNone { // Do not checkout AuthStrategyNone because it only has server auth secret 241 if sSecret != nil && cSecret != nil && sSecret.Annotations != nil && cSecret.Annotations != nil { 242 if sSecret.Annotations[authStrategyAnnoKey] == string(strategy) && cSecret.Annotations[authStrategyAnnoKey] == string(strategy) { 243 // If the secrets are already existing, and strategy didn't change, reuse them without updating. 244 return sSecret, cSecret, nil 245 } 246 } 247 } 248 249 switch strategy { 250 case v1alpha1.AuthStrategyNone: 251 // Clean up client auth secret if existing 252 if cSecret != nil { 253 err = i.client.Delete(ctx, cSecret) 254 if err != nil { 255 i.eventBus.Status.MarkDeployFailed("DeleteClientAuthSecretFailed", "Failed to delete the client auth secret") 256 log.Errorw("Error deleting client auth secret", zap.Error(err)) 257 return nil, nil, err 258 } 259 log.Info("Deleted server auth secret") 260 } 261 if sSecret != nil && sSecret.Annotations != nil && sSecret.Annotations[authStrategyAnnoKey] == string(strategy) && len(sSecret.Data[serverAuthSecretKey]) == 0 { 262 // If the server auth secret is already existing, strategy didn't change, and the secret is empty string, reuse it without updating. 263 return sSecret, nil, nil 264 } 265 // Only create an empty server auth secret 266 expectedSSecret, err := i.buildServerAuthSecret(strategy, "") 267 if err != nil { 268 i.eventBus.Status.MarkDeployFailed("BuildServerAuthSecretFailed", "Failed to build a server auth secret spec") 269 log.Errorw("Error building server auth secret spec", zap.Error(err)) 270 return nil, nil, err 271 } 272 if sSecret != nil { 273 sSecret.ObjectMeta.Labels = expectedSSecret.Labels 274 sSecret.ObjectMeta.Annotations = expectedSSecret.Annotations 275 sSecret.Data = expectedSSecret.Data 276 err = i.client.Update(ctx, sSecret) 277 if err != nil { 278 i.eventBus.Status.MarkDeployFailed("UpdateServerAuthSecretFailed", "Failed to update the server auth secret") 279 log.Errorw("Error updating server auth secret", zap.Error(err)) 280 return nil, nil, err 281 } 282 log.Infow("Updated server auth secret", "serverAuthSecretName", sSecret.Name) 283 return sSecret, nil, nil 284 } 285 err = i.client.Create(ctx, expectedSSecret) 286 if err != nil { 287 i.eventBus.Status.MarkDeployFailed("CreateServerAuthSecretFailed", "Failed to create a server auth secret") 288 log.Errorw("Error creating server auth secret", zap.Error(err)) 289 return nil, nil, err 290 } 291 log.Infow("Created server auth secret", "serverAuthSecretName", expectedSSecret.Name) 292 return expectedSSecret, nil, nil 293 case v1alpha1.AuthStrategyToken: 294 token := common.RandomString(64) 295 serverAuthText := fmt.Sprintf(`authorization { 296 token: "%s" 297 }`, token) 298 clientAuthText := fmt.Sprintf("token: \"%s\"", token) 299 // Create server auth secret 300 expectedSSecret, err := i.buildServerAuthSecret(strategy, serverAuthText) 301 if err != nil { 302 i.eventBus.Status.MarkDeployFailed("BuildServerAuthSecretFailed", "Failed to build a server auth secret spec") 303 log.Errorw("Error building server auth secret spec", zap.Error(err)) 304 return nil, nil, err 305 } 306 returnedSSecret := expectedSSecret 307 if sSecret == nil { 308 err = i.client.Create(ctx, expectedSSecret) 309 if err != nil { 310 i.eventBus.Status.MarkDeployFailed("CreateServerAuthSecretFailed", "Failed to create a server auth secret") 311 log.Errorw("Error creating server auth secret", zap.Error(err)) 312 return nil, nil, err 313 } 314 log.Infow("Created server auth secret", "serverAuthSecretName", expectedSSecret.Name) 315 } else { 316 sSecret.Data = expectedSSecret.Data 317 sSecret.SetLabels(expectedSSecret.Labels) 318 sSecret.SetAnnotations(expectedSSecret.Annotations) 319 err = i.client.Update(ctx, sSecret) 320 if err != nil { 321 i.eventBus.Status.MarkDeployFailed("UpdateServerAuthSecretFailed", "Failed to update the server auth secret") 322 log.Errorw("Error updating server auth secret", zap.Error(err)) 323 return nil, nil, err 324 } 325 log.Infow("Updated server auth secret", "serverAuthSecretName", sSecret.Name) 326 returnedSSecret = sSecret 327 } 328 // create client auth secret 329 expectedCSecret, err := i.buildClientAuthSecret(strategy, clientAuthText) 330 if err != nil { 331 i.eventBus.Status.MarkDeployFailed("BuildClientAuthSecretFailed", "Failed to build a client auth secret spec") 332 log.Errorw("Error building client auth secret spec", zap.Error(err)) 333 return nil, nil, err 334 } 335 returnedCSecret := expectedCSecret 336 if cSecret == nil { 337 err = i.client.Create(ctx, expectedCSecret) 338 if err != nil { 339 i.eventBus.Status.MarkDeployFailed("CreateClientAuthSecretFailed", "Failed to create a client auth secret") 340 log.Errorw("Error creating client auth secret", zap.Error(err)) 341 return nil, nil, err 342 } 343 log.Infow("Created client auth secret", "clientAuthSecretName", expectedCSecret.Name) 344 } else { 345 cSecret.Data = expectedCSecret.Data 346 cSecret.SetLabels(expectedCSecret.Labels) 347 cSecret.SetAnnotations(expectedCSecret.Annotations) 348 err = i.client.Update(ctx, cSecret) 349 if err != nil { 350 i.eventBus.Status.MarkDeployFailed("UpdateClientAuthSecretFailed", "Failed to update the client auth secret") 351 log.Errorw("Error updating client auth secret", zap.Error(err)) 352 return nil, nil, err 353 } 354 log.Infow("Updated client auth secret", "clientAuthSecretName", cSecret.Name) 355 returnedCSecret = cSecret 356 } 357 return returnedSSecret, returnedCSecret, nil 358 default: 359 i.eventBus.Status.MarkDeployFailed("UnsupportedAuthStrategy", "Unsupported auth strategy") 360 return nil, nil, fmt.Errorf("unsupported auth strategy") 361 } 362 } 363 364 // Create a StatefulSet 365 func (i *natsInstaller) createStatefulSet(ctx context.Context, serviceName, configmapName, authSecretName string) error { 366 log := i.logger 367 ss, err := i.getStatefulSet(ctx) 368 if err != nil && !apierrors.IsNotFound(err) { 369 i.eventBus.Status.MarkDeployFailed("GetStatefulSetFailed", "Failed to get existing statefulset") 370 log.Errorw("Error getting existing statefulset", zap.Error(err)) 371 return err 372 } 373 expectedSs, err := i.buildStatefulSet(serviceName, configmapName, authSecretName) 374 if err != nil { 375 i.eventBus.Status.MarkDeployFailed("BuildStatefulSetFailed", "Failed to build a statefulset spec") 376 log.Errorw("Error building statefulset spec", zap.Error(err)) 377 return err 378 } 379 if ss != nil { 380 if ss.Annotations != nil && ss.Annotations[common.AnnotationResourceSpecHash] == expectedSs.Annotations[common.AnnotationResourceSpecHash] { 381 return nil 382 } 383 ss.SetLabels(expectedSs.Labels) 384 ss.Annotations[common.AnnotationResourceSpecHash] = expectedSs.Annotations[common.AnnotationResourceSpecHash] 385 ss.Spec = expectedSs.Spec 386 if err := i.client.Update(ctx, ss); err != nil { 387 i.eventBus.Status.MarkDeployFailed("UpdateStatefulSetFailed", "Failed to update a statefulset") 388 log.Errorw("Error updating a statefulset", zap.Error(err)) 389 return err 390 } 391 log.Infow("Statefulset is updated", "statefulsetName", ss.Name) 392 return nil 393 } 394 if err := i.client.Create(ctx, expectedSs); err != nil { 395 i.eventBus.Status.MarkDeployFailed("CreateStatefulSetFailed", "Failed to create a statefulset") 396 log.Errorw("Error creating a statefulset", zap.Error(err)) 397 return err 398 } 399 log.Infow("Statefulset is created", "statefulsetName", expectedSs.Name) 400 return nil 401 } 402 403 // buildStanService builds a Service for NATS streaming 404 func (i *natsInstaller) buildStanService() (*corev1.Service, error) { 405 svc := &corev1.Service{ 406 ObjectMeta: metav1.ObjectMeta{ 407 Name: generateServiceName(i.eventBus), 408 Namespace: i.eventBus.Namespace, 409 Labels: i.mergeEventBusLabels(stanServiceLabels(i.labels)), 410 }, 411 Spec: corev1.ServiceSpec{ 412 ClusterIP: corev1.ClusterIPNone, 413 Ports: []corev1.ServicePort{ 414 // Prefix tcp- to enable clients to connect from 415 // an istio-enabled namespace, following: 416 // https://github.com/nats-io/nats-operator/issues/88 417 // https://github.com/istio/istio/issues/28623 418 {Name: "tcp-client", Port: clientPort}, 419 {Name: "cluster", Port: clusterPort}, 420 {Name: "monitor", Port: monitorPort}, 421 }, 422 Type: corev1.ServiceTypeClusterIP, 423 Selector: i.labels, 424 }, 425 } 426 if err := controllerscommon.SetObjectMeta(i.eventBus, svc, v1alpha1.SchemaGroupVersionKind); err != nil { 427 return nil, err 428 } 429 return svc, nil 430 } 431 432 // buildConfigMap builds a ConfigMap for NATS streaming 433 func (i *natsInstaller) buildConfigMap() (*corev1.ConfigMap, error) { 434 clusterID := generateClusterID(i.eventBus) 435 svcName := generateServiceName(i.eventBus) 436 ssName := generateStatefulSetName(i.eventBus) 437 replicas := i.eventBus.Spec.NATS.Native.GetReplicas() 438 if replicas < 3 { 439 replicas = 3 440 } 441 maxAge := common.STANMaxAge 442 if i.eventBus.Spec.NATS.Native.MaxAge != nil { 443 maxAge = *i.eventBus.Spec.NATS.Native.MaxAge 444 } 445 _, err := time.ParseDuration(maxAge) 446 if err != nil { 447 return nil, err 448 } 449 maxMsgs := common.STANMaxMsgs 450 if i.eventBus.Spec.NATS.Native.MaxMsgs != nil { 451 maxMsgs = *i.eventBus.Spec.NATS.Native.MaxMsgs 452 } 453 maxSubs := common.STANMaxSubs 454 if i.eventBus.Spec.NATS.Native.MaxSubs != nil { 455 maxSubs = *i.eventBus.Spec.NATS.Native.MaxSubs 456 } 457 maxBytes := common.STANMaxBytes 458 if i.eventBus.Spec.NATS.Native.MaxBytes != nil { 459 maxBytes = *i.eventBus.Spec.NATS.Native.MaxBytes 460 } 461 maxPayload := common.STANMaxPayload 462 if i.eventBus.Spec.NATS.Native.MaxPayload != nil { 463 maxPayload = *i.eventBus.Spec.NATS.Native.MaxPayload 464 } 465 raftHeartbeatTimeout := common.STANRaftHeartbeatTimeout 466 if i.eventBus.Spec.NATS.Native.RaftHeartbeatTimeout != nil { 467 raftHeartbeatTimeout = *i.eventBus.Spec.NATS.Native.RaftHeartbeatTimeout 468 } 469 _, err = time.ParseDuration(raftHeartbeatTimeout) 470 if err != nil { 471 return nil, err 472 } 473 raftElectionTimeout := common.STANRaftElectionTimeout 474 if i.eventBus.Spec.NATS.Native.RaftElectionTimeout != nil { 475 raftElectionTimeout = *i.eventBus.Spec.NATS.Native.RaftElectionTimeout 476 } 477 _, err = time.ParseDuration(raftElectionTimeout) 478 if err != nil { 479 return nil, err 480 } 481 raftLeaseTimeout := common.STANRaftLeaseTimeout 482 if i.eventBus.Spec.NATS.Native.RaftLeaseTimeout != nil { 483 raftLeaseTimeout = *i.eventBus.Spec.NATS.Native.RaftLeaseTimeout 484 } 485 _, err = time.ParseDuration(raftLeaseTimeout) 486 if err != nil { 487 return nil, err 488 } 489 raftCommitTimeout := common.STANRaftCommitTimeout 490 if i.eventBus.Spec.NATS.Native.RaftCommitTimeout != nil { 491 raftCommitTimeout = *i.eventBus.Spec.NATS.Native.RaftCommitTimeout 492 } 493 _, err = time.ParseDuration(raftCommitTimeout) 494 if err != nil { 495 return nil, err 496 } 497 peers := []string{} 498 routes := []string{} 499 for j := 0; j < replicas; j++ { 500 peers = append(peers, fmt.Sprintf("\"%s-%s\"", ssName, strconv.Itoa(j))) 501 routes = append(routes, fmt.Sprintf("nats://%s-%s.%s.%s.svc:%s", ssName, strconv.Itoa(j), svcName, i.eventBus.Namespace, strconv.Itoa(int(clusterPort)))) 502 } 503 conf := fmt.Sprintf(`http: %s 504 include ./auth.conf 505 cluster { 506 port: %s 507 routes: [%s] 508 cluster_advertise: $CLUSTER_ADVERTISE 509 connect_retries: 10 510 } 511 max_payload: %s 512 streaming { 513 id: %s 514 store: file 515 dir: /data/stan/store 516 cluster { 517 node_id: $POD_NAME 518 peers: [%s] 519 log_path: /data/stan/logs 520 raft_heartbeat_timeout: "%s" 521 raft_election_timeout: "%s" 522 raft_lease_timeout: "%s" 523 raft_commit_timeout: "%s" 524 } 525 store_limits { 526 max_age: %s 527 max_msgs: %v 528 max_bytes: %s 529 max_subs: %v 530 } 531 }`, strconv.Itoa(int(monitorPort)), strconv.Itoa(int(clusterPort)), strings.Join(routes, ","), maxPayload, clusterID, strings.Join(peers, ","), raftHeartbeatTimeout, raftElectionTimeout, raftLeaseTimeout, raftCommitTimeout, maxAge, maxMsgs, maxBytes, maxSubs) 532 cm := &corev1.ConfigMap{ 533 ObjectMeta: metav1.ObjectMeta{ 534 Namespace: i.eventBus.Namespace, 535 Name: generateConfigMapName(i.eventBus), 536 Labels: i.mergeEventBusLabels(i.labels), 537 }, 538 Data: map[string]string{ 539 configMapKey: conf, 540 }, 541 } 542 if err := controllerscommon.SetObjectMeta(i.eventBus, cm, v1alpha1.SchemaGroupVersionKind); err != nil { 543 return nil, err 544 } 545 return cm, nil 546 } 547 548 // buildServerAuthSecret builds a secret for NATS auth config 549 // Parameter - authStrategy: will be added to annoations 550 // Parameter - secret 551 // Example: 552 // 553 // authorization { 554 // token: "abcd1234" 555 // } 556 func (i *natsInstaller) buildServerAuthSecret(authStrategy v1alpha1.AuthStrategy, secret string) (*corev1.Secret, error) { 557 s := &corev1.Secret{ 558 ObjectMeta: metav1.ObjectMeta{ 559 Namespace: i.eventBus.Namespace, 560 Name: generateServerAuthSecretName(i.eventBus), 561 Labels: serverAuthSecretLabels(i.labels), 562 Annotations: map[string]string{authStrategyAnnoKey: string(authStrategy)}, 563 }, 564 Type: corev1.SecretTypeOpaque, 565 Data: map[string][]byte{ 566 serverAuthSecretKey: []byte(secret), 567 }, 568 } 569 if err := controllerscommon.SetObjectMeta(i.eventBus, s, v1alpha1.SchemaGroupVersionKind); err != nil { 570 return nil, err 571 } 572 return s, nil 573 } 574 575 // buildClientAuthSecret builds a secret for NATS client auth 576 func (i *natsInstaller) buildClientAuthSecret(authStrategy v1alpha1.AuthStrategy, secret string) (*corev1.Secret, error) { 577 s := &corev1.Secret{ 578 ObjectMeta: metav1.ObjectMeta{ 579 Namespace: i.eventBus.Namespace, 580 Name: generateClientAuthSecretName(i.eventBus), 581 Labels: clientAuthSecretLabels(i.labels), 582 Annotations: map[string]string{authStrategyAnnoKey: string(authStrategy)}, 583 }, 584 Type: corev1.SecretTypeOpaque, 585 Data: map[string][]byte{ 586 clientAuthSecretKey: []byte(secret), 587 }, 588 } 589 if err := controllerscommon.SetObjectMeta(i.eventBus, s, v1alpha1.SchemaGroupVersionKind); err != nil { 590 return nil, err 591 } 592 return s, nil 593 } 594 595 // buildStatefulSet builds a StatefulSet for nats streaming 596 func (i *natsInstaller) buildStatefulSet(serviceName, configmapName, authSecretName string) (*appv1.StatefulSet, error) { 597 // Use provided serviceName, configMapName to build the spec 598 // to avoid issues when naming convention changes 599 spec, err := i.buildStatefulSetSpec(serviceName, configmapName, authSecretName) 600 if err != nil { 601 return nil, err 602 } 603 ss := &appv1.StatefulSet{ 604 ObjectMeta: metav1.ObjectMeta{ 605 Namespace: i.eventBus.Namespace, 606 Name: generateStatefulSetName(i.eventBus), 607 Labels: i.mergeEventBusLabels(i.labels), 608 }, 609 Spec: *spec, 610 } 611 if err := controllerscommon.SetObjectMeta(i.eventBus, ss, v1alpha1.SchemaGroupVersionKind); err != nil { 612 return nil, err 613 } 614 return ss, nil 615 } 616 617 func (i *natsInstaller) buildStatefulSetSpec(serviceName, configmapName, authSecretName string) (*appv1.StatefulSetSpec, error) { 618 stanVersion, err := i.config.GetSTANVersion(defaultSTANVersion) 619 if err != nil { 620 return nil, fmt.Errorf("failed to get nats streaming version, err: %w", err) 621 } 622 // Streaming requires minimal size 3. 623 replicas := i.eventBus.Spec.NATS.Native.Replicas 624 if replicas < 3 { 625 replicas = 3 626 } 627 var stanContainerPullPolicy, metricsContainerPullPolicy corev1.PullPolicy 628 var stanContainerSecurityContext, metricsContainerSecurityContext *corev1.SecurityContext 629 stanContainerResources := corev1.ResourceRequirements{ 630 Requests: corev1.ResourceList{ 631 corev1.ResourceCPU: apiresource.MustParse("0"), 632 }, 633 } 634 containerTmpl := i.eventBus.Spec.NATS.Native.ContainerTemplate 635 if containerTmpl != nil { 636 stanContainerResources = containerTmpl.Resources 637 stanContainerPullPolicy = containerTmpl.ImagePullPolicy 638 stanContainerSecurityContext = containerTmpl.SecurityContext 639 } 640 metricsContainerResources := corev1.ResourceRequirements{} 641 metricsContainerTmpl := i.eventBus.Spec.NATS.Native.MetricsContainerTemplate 642 if metricsContainerTmpl != nil { 643 metricsContainerResources = metricsContainerTmpl.Resources 644 metricsContainerPullPolicy = metricsContainerTmpl.ImagePullPolicy 645 metricsContainerSecurityContext = metricsContainerTmpl.SecurityContext 646 } 647 648 podTemplateLabels := make(map[string]string) 649 if i.eventBus.Spec.NATS.Native.Metadata != nil && 650 len(i.eventBus.Spec.NATS.Native.Metadata.Labels) > 0 { 651 for k, v := range i.eventBus.Spec.NATS.Native.Metadata.Labels { 652 podTemplateLabels[k] = v 653 } 654 } 655 for k, v := range i.labels { 656 podTemplateLabels[k] = v 657 } 658 spec := appv1.StatefulSetSpec{ 659 Replicas: &replicas, 660 ServiceName: serviceName, 661 Selector: &metav1.LabelSelector{ 662 MatchLabels: i.labels, 663 }, 664 Template: corev1.PodTemplateSpec{ 665 ObjectMeta: metav1.ObjectMeta{ 666 Labels: podTemplateLabels, 667 }, 668 Spec: corev1.PodSpec{ 669 NodeSelector: i.eventBus.Spec.NATS.Native.NodeSelector, 670 Tolerations: i.eventBus.Spec.NATS.Native.Tolerations, 671 SecurityContext: i.eventBus.Spec.NATS.Native.SecurityContext, 672 ImagePullSecrets: i.eventBus.Spec.NATS.Native.ImagePullSecrets, 673 ServiceAccountName: i.eventBus.Spec.NATS.Native.ServiceAccountName, 674 PriorityClassName: i.eventBus.Spec.NATS.Native.PriorityClassName, 675 Priority: i.eventBus.Spec.NATS.Native.Priority, 676 Affinity: i.eventBus.Spec.NATS.Native.Affinity, 677 Volumes: []corev1.Volume{ 678 { 679 Name: "config-volume", 680 VolumeSource: corev1.VolumeSource{ 681 Projected: &corev1.ProjectedVolumeSource{ 682 Sources: []corev1.VolumeProjection{ 683 { 684 ConfigMap: &corev1.ConfigMapProjection{ 685 LocalObjectReference: corev1.LocalObjectReference{ 686 Name: configmapName, 687 }, 688 Items: []corev1.KeyToPath{ 689 { 690 Key: configMapKey, 691 Path: "stan.conf", 692 }, 693 }, 694 }, 695 }, 696 { 697 Secret: &corev1.SecretProjection{ 698 LocalObjectReference: corev1.LocalObjectReference{ 699 Name: authSecretName, 700 }, 701 Items: []corev1.KeyToPath{ 702 { 703 Key: serverAuthSecretKey, 704 Path: "auth.conf", 705 }, 706 }, 707 }, 708 }, 709 }, 710 }, 711 }, 712 }, 713 }, 714 Containers: []corev1.Container{ 715 { 716 Name: "stan", 717 Image: stanVersion.NATSStreamingImage, 718 ImagePullPolicy: stanContainerPullPolicy, 719 Ports: []corev1.ContainerPort{ 720 {Name: "client", ContainerPort: clientPort}, 721 {Name: "cluster", ContainerPort: clusterPort}, 722 {Name: "monitor", ContainerPort: monitorPort}, 723 }, 724 Command: []string{"/nats-streaming-server", "-sc", "/etc/stan-config/stan.conf"}, 725 Env: []corev1.EnvVar{ 726 {Name: "POD_NAME", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"}}}, 727 {Name: "POD_NAMESPACE", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.namespace"}}}, 728 {Name: "CLUSTER_ADVERTISE", Value: "$(POD_NAME)." + generateServiceName(i.eventBus) + ".$(POD_NAMESPACE).svc"}, 729 }, 730 VolumeMounts: []corev1.VolumeMount{ 731 {Name: "config-volume", MountPath: "/etc/stan-config"}, 732 }, 733 Resources: stanContainerResources, 734 SecurityContext: stanContainerSecurityContext, 735 LivenessProbe: &corev1.Probe{ 736 ProbeHandler: corev1.ProbeHandler{ 737 HTTPGet: &corev1.HTTPGetAction{ 738 Path: "/", 739 Port: intstr.FromInt(int(monitorPort)), 740 }, 741 }, 742 InitialDelaySeconds: 10, 743 TimeoutSeconds: 5, 744 }, 745 }, 746 { 747 Name: "metrics", 748 Image: stanVersion.MetricsExporterImage, 749 ImagePullPolicy: metricsContainerPullPolicy, 750 Ports: []corev1.ContainerPort{ 751 {Name: "metrics", ContainerPort: common.EventBusMetricsPort}, 752 }, 753 Args: []string{"-connz", "-routez", "-subz", "-varz", "-channelz", "-serverz", fmt.Sprintf("http://localhost:%s", strconv.Itoa(int(monitorPort)))}, 754 Resources: metricsContainerResources, 755 SecurityContext: metricsContainerSecurityContext, 756 }, 757 }, 758 }, 759 }, 760 } 761 if i.eventBus.Spec.NATS.Native.Metadata != nil { 762 spec.Template.SetAnnotations(i.eventBus.Spec.NATS.Native.Metadata.Annotations) 763 } 764 if i.eventBus.Spec.NATS.Native.Persistence != nil { 765 volMode := corev1.PersistentVolumeFilesystem 766 pvcName := generatePVCName(i.eventBus) 767 // Default volume size 768 volSize := apiresource.MustParse("10Gi") 769 if i.eventBus.Spec.NATS.Native.Persistence.VolumeSize != nil { 770 volSize = *i.eventBus.Spec.NATS.Native.Persistence.VolumeSize 771 } 772 // Default to ReadWriteOnce 773 accessMode := corev1.ReadWriteOnce 774 if i.eventBus.Spec.NATS.Native.Persistence.AccessMode != nil { 775 accessMode = *i.eventBus.Spec.NATS.Native.Persistence.AccessMode 776 } 777 spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{ 778 { 779 ObjectMeta: metav1.ObjectMeta{ 780 Name: pvcName, 781 }, 782 Spec: corev1.PersistentVolumeClaimSpec{ 783 AccessModes: []corev1.PersistentVolumeAccessMode{ 784 accessMode, 785 }, 786 VolumeMode: &volMode, 787 StorageClassName: i.eventBus.Spec.NATS.Native.Persistence.StorageClassName, 788 Resources: corev1.ResourceRequirements{ 789 Requests: corev1.ResourceList{ 790 corev1.ResourceStorage: volSize, 791 }, 792 }, 793 }, 794 }, 795 } 796 volumeMounts := spec.Template.Spec.Containers[0].VolumeMounts 797 volumeMounts = append(volumeMounts, corev1.VolumeMount{Name: pvcName, MountPath: "/data/stan"}) 798 spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts 799 } else { 800 // When the POD is runasnonroot, it can not create the dir /data/stan 801 // Use an emptyDirVolume to workaround the issue 802 emptyDirVolName := "stan-data" 803 volumes := spec.Template.Spec.Volumes 804 volumes = append(volumes, corev1.Volume{Name: emptyDirVolName, VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}) 805 spec.Template.Spec.Volumes = volumes 806 volumeMounts := spec.Template.Spec.Containers[0].VolumeMounts 807 volumeMounts = append(volumeMounts, corev1.VolumeMount{Name: emptyDirVolName, MountPath: "/data/stan"}) 808 spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts 809 } 810 return &spec, nil 811 } 812 813 func (i *natsInstaller) getStanService(ctx context.Context) (*corev1.Service, error) { 814 return i.getService(ctx, stanServiceLabels(i.labels)) 815 } 816 817 func (i *natsInstaller) getService(ctx context.Context, labels map[string]string) (*corev1.Service, error) { 818 // Why not using getByName()? 819 // Naming convention might be changed. 820 sl := &corev1.ServiceList{} 821 err := i.client.List(ctx, sl, &client.ListOptions{ 822 Namespace: i.eventBus.Namespace, 823 LabelSelector: labelSelector(labels), 824 }) 825 if err != nil { 826 return nil, err 827 } 828 for _, svc := range sl.Items { 829 if metav1.IsControlledBy(&svc, i.eventBus) { 830 return &svc, nil 831 } 832 } 833 return nil, apierrors.NewNotFound(schema.GroupResource{}, "") 834 } 835 836 func (i *natsInstaller) getConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { 837 cml := &corev1.ConfigMapList{} 838 err := i.client.List(ctx, cml, &client.ListOptions{ 839 Namespace: i.eventBus.Namespace, 840 LabelSelector: labelSelector(i.labels), 841 }) 842 if err != nil { 843 return nil, err 844 } 845 for _, cm := range cml.Items { 846 if metav1.IsControlledBy(&cm, i.eventBus) { 847 return &cm, nil 848 } 849 } 850 return nil, apierrors.NewNotFound(schema.GroupResource{}, "") 851 } 852 853 // get server auth secret 854 func (i *natsInstaller) getServerAuthSecret(ctx context.Context) (*corev1.Secret, error) { 855 return i.getSecret(ctx, serverAuthSecretLabels(i.labels)) 856 } 857 858 // get client auth secret 859 func (i *natsInstaller) getClientAuthSecret(ctx context.Context) (*corev1.Secret, error) { 860 return i.getSecret(ctx, clientAuthSecretLabels(i.labels)) 861 } 862 863 func (i *natsInstaller) getSecret(ctx context.Context, labels map[string]string) (*corev1.Secret, error) { 864 sl, err := i.kubeClient.CoreV1().Secrets(i.eventBus.Namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector(labels).String()}) 865 if err != nil { 866 return nil, err 867 } 868 for _, s := range sl.Items { 869 if metav1.IsControlledBy(&s, i.eventBus) { 870 return &s, nil 871 } 872 } 873 return nil, apierrors.NewNotFound(schema.GroupResource{}, "") 874 } 875 876 func (i *natsInstaller) getStatefulSet(ctx context.Context) (*appv1.StatefulSet, error) { 877 // Why not using getByName()? 878 // Naming convention might be changed. 879 ssl := &appv1.StatefulSetList{} 880 err := i.client.List(ctx, ssl, &client.ListOptions{ 881 Namespace: i.eventBus.Namespace, 882 LabelSelector: labelSelector(i.labels), 883 }) 884 if err != nil { 885 return nil, err 886 } 887 for _, ss := range ssl.Items { 888 if metav1.IsControlledBy(&ss, i.eventBus) { 889 return &ss, nil 890 } 891 } 892 return nil, apierrors.NewNotFound(schema.GroupResource{}, "") 893 } 894 895 // get PVCs created by streaming statefulset 896 // they have same labels as the statefulset 897 func (i *natsInstaller) getPVCs(ctx context.Context, labels map[string]string) ([]corev1.PersistentVolumeClaim, error) { 898 pvcl := &corev1.PersistentVolumeClaimList{} 899 err := i.client.List(ctx, pvcl, &client.ListOptions{ 900 Namespace: i.eventBus.Namespace, 901 LabelSelector: labelSelector(labels), 902 }) 903 if err != nil { 904 return nil, err 905 } 906 return pvcl.Items, nil 907 } 908 909 func (i *natsInstaller) mergeEventBusLabels(given map[string]string) map[string]string { 910 result := map[string]string{} 911 if i.eventBus.Labels != nil { 912 for k, v := range i.eventBus.Labels { 913 result[k] = v 914 } 915 } 916 for k, v := range given { 917 result[k] = v 918 } 919 return result 920 } 921 922 func serverAuthSecretLabels(given map[string]string) map[string]string { 923 result := map[string]string{"server-auth-secret": "yes"} 924 for k, v := range given { 925 result[k] = v 926 } 927 return result 928 } 929 930 func clientAuthSecretLabels(given map[string]string) map[string]string { 931 result := map[string]string{"client-auth-secret": "yes"} 932 for k, v := range given { 933 result[k] = v 934 } 935 return result 936 } 937 938 func stanServiceLabels(given map[string]string) map[string]string { 939 result := map[string]string{"stan": "yes"} 940 for k, v := range given { 941 result[k] = v 942 } 943 return result 944 } 945 946 func labelSelector(labelMap map[string]string) labels.Selector { 947 return labels.SelectorFromSet(labelMap) 948 } 949 950 func generateServiceName(eventBus *v1alpha1.EventBus) string { 951 return fmt.Sprintf("eventbus-%s-stan-svc", eventBus.Name) 952 } 953 954 func generateConfigMapName(eventBus *v1alpha1.EventBus) string { 955 return fmt.Sprintf("eventbus-%s-stan-configmap", eventBus.Name) 956 } 957 958 func generateServerAuthSecretName(eventBus *v1alpha1.EventBus) string { 959 return fmt.Sprintf("eventbus-%s-server", eventBus.Name) 960 } 961 962 func generateClientAuthSecretName(eventBus *v1alpha1.EventBus) string { 963 return fmt.Sprintf("eventbus-%s-client", eventBus.Name) 964 } 965 966 func generateStatefulSetName(eventBus *v1alpha1.EventBus) string { 967 return fmt.Sprintf("eventbus-%s-stan", eventBus.Name) 968 } 969 970 func generateClusterID(eventBus *v1alpha1.EventBus) string { 971 return fmt.Sprintf("eventbus-%s", eventBus.Name) 972 } 973 974 // PVC name used in streaming statefulset 975 func generatePVCName(eventBus *v1alpha1.EventBus) string { 976 return fmt.Sprintf("stan-%s-vol", eventBus.Name) 977 }