create_cluster.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package project
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/nats-io/nats.go"
  6. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  7. "github.com/porter-dev/porter/api/server/handlers"
  8. "github.com/porter-dev/porter/api/server/shared"
  9. "github.com/porter-dev/porter/api/server/shared/apierrors"
  10. "github.com/porter-dev/porter/api/server/shared/config"
  11. "github.com/porter-dev/porter/api/types"
  12. "google.golang.org/protobuf/proto"
  13. )
  14. type CreateClusterHandler struct {
  15. handlers.PorterHandlerReadWriter
  16. }
  17. func NewProvisionClusterHandler(
  18. config *config.Config,
  19. decoderValidator shared.RequestDecoderValidator,
  20. writer shared.ResultWriter,
  21. ) *CreateClusterHandler {
  22. return &CreateClusterHandler{
  23. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  24. }
  25. }
  26. // ServeHTTP creates a CAPI cluster by adding the configuration to a NATS stream
  27. func (c *CreateClusterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  28. if !c.Config().DisableCAPIProvisioner {
  29. // TODO: delete this block after April 2023. It is only required whilst people are not easily able to get NATS and Cluster Control Plane running on their local environment
  30. w.WriteHeader(http.StatusCreated)
  31. return
  32. }
  33. var capiClusterReq types.CAPIClusterRequest
  34. ctx := r.Context()
  35. if ok := c.DecodeAndValidate(w, r, &capiClusterReq); !ok {
  36. return
  37. }
  38. capiCluster := porterv1.Kubernetes{
  39. ProjectId: int32(capiClusterReq.ProjectID),
  40. ClusterId: int32(capiClusterReq.ClusterID),
  41. }
  42. if capiClusterReq.CloudProvider == "aws" {
  43. capiCluster.CloudProvider = porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS
  44. capiCluster.Kind = porterv1.EnumKubernetesKind_ENUM_KUBERNETES_KIND_EKS
  45. capiCluster.CloudProviderCredentialsId = capiClusterReq.CloudProviderCredentialsID
  46. var capiNodeGroups []*porterv1.EKSNodeGroup
  47. for _, ng := range capiClusterReq.ClusterSettings.NodeGroups {
  48. cng := porterv1.EKSNodeGroup{
  49. InstanceType: ng.InstanceType,
  50. MinInstances: uint32(ng.MinInstances),
  51. MaxInstances: uint32(ng.MaxInstances),
  52. NodeGroupType: protoNodeGroupTypeLookup(ng.NodeGroupType),
  53. }
  54. capiNodeGroups = append(capiNodeGroups, &cng)
  55. }
  56. capiCluster.KindValues = &porterv1.Kubernetes_EksKind{
  57. EksKind: &porterv1.EKS{
  58. ClusterName: capiClusterReq.ClusterSettings.ClusterName,
  59. CidrRange: capiClusterReq.ClusterSettings.CIDRRange,
  60. ClusterVersion: capiClusterReq.ClusterSettings.ClusterVersion,
  61. Region: capiClusterReq.ClusterSettings.Region,
  62. NodeGroups: capiNodeGroups,
  63. },
  64. }
  65. }
  66. by, err := proto.Marshal(&capiCluster)
  67. if err != nil {
  68. e := fmt.Errorf("error marshalling proto: %w", err)
  69. c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
  70. return
  71. }
  72. subject := "porter.system.infrastructure.update"
  73. _, err = c.Config().NATS.JetStream.Publish(subject, by, nats.Context(ctx))
  74. if err != nil {
  75. e := fmt.Errorf("error publishing cluster for creation: %w", err)
  76. c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
  77. return
  78. }
  79. w.WriteHeader(http.StatusCreated)
  80. }
  81. var (
  82. apiNodeGroupToProtoNodeGroup = map[string]porterv1.NodeGroupType{
  83. "SYSTEM": porterv1.NodeGroupType_NODE_GROUP_TYPE_SYSTEM,
  84. "MONITORING": porterv1.NodeGroupType_NODE_GROUP_TYPE_MONITORING,
  85. "APPLICATION": porterv1.NodeGroupType_NODE_GROUP_TYPE_APPLICATION,
  86. "CUSTOM": porterv1.NodeGroupType_NODE_GROUP_TYPE_CUSTOM,
  87. }
  88. )
  89. // protoNodeGroupTypeLookup is a helper function for finding a nodegroup, and returning a default if its not found
  90. func protoNodeGroupTypeLookup(apiNodeGroup string) porterv1.NodeGroupType {
  91. if ngt, ok := apiNodeGroupToProtoNodeGroup[apiNodeGroup]; ok {
  92. return ngt
  93. }
  94. return porterv1.NodeGroupType_NODE_GROUP_TYPE_CUSTOM
  95. }