create_cluster.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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. c.Config().Repo.Cluster()
  39. capiCluster := porterv1.Kubernetes{
  40. ProjectId: int32(capiClusterReq.ProjectID),
  41. ClusterId: int32(capiClusterReq.ClusterID),
  42. }
  43. if capiClusterReq.CloudProvider == "aws" {
  44. capiCluster.CloudProvider = porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS
  45. capiCluster.Kind = porterv1.EnumKubernetesKind_ENUM_KUBERNETES_KIND_EKS
  46. capiCluster.CloudProviderCredentialsId = capiClusterReq.CloudProviderCredentialsID
  47. var capiNodeGroups []*porterv1.EKSNodeGroup
  48. for _, ng := range capiClusterReq.ClusterSettings.NodeGroups {
  49. cng := porterv1.EKSNodeGroup{
  50. InstanceType: ng.InstanceType,
  51. MinInstances: uint32(ng.MinInstances),
  52. MaxInstances: uint32(ng.MaxInstances),
  53. NodeGroupType: protoNodeGroupTypeLookup(ng.NodeGroupType),
  54. }
  55. capiNodeGroups = append(capiNodeGroups, &cng)
  56. }
  57. capiCluster.KindValues = &porterv1.Kubernetes_EksKind{
  58. EksKind: &porterv1.EKS{
  59. ClusterName: capiClusterReq.ClusterSettings.ClusterName,
  60. CidrRange: capiClusterReq.ClusterSettings.CIDRRange,
  61. ClusterVersion: capiClusterReq.ClusterSettings.ClusterVersion,
  62. Region: capiClusterReq.ClusterSettings.Region,
  63. NodeGroups: capiNodeGroups,
  64. },
  65. }
  66. }
  67. by, err := proto.Marshal(&capiCluster)
  68. if err != nil {
  69. e := fmt.Errorf("error marshalling proto: %w", err)
  70. c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
  71. return
  72. }
  73. subject := "porter.system.infrastructure.update"
  74. _, err = c.Config().NATS.JetStream.Publish(subject, by, nats.Context(ctx))
  75. if err != nil {
  76. e := fmt.Errorf("error publishing cluster for creation: %w", err)
  77. c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
  78. return
  79. }
  80. w.WriteHeader(http.StatusCreated)
  81. }
  82. var (
  83. apiNodeGroupToProtoNodeGroup = map[string]porterv1.NodeGroupType{
  84. "SYSTEM": porterv1.NodeGroupType_NODE_GROUP_TYPE_SYSTEM,
  85. "MONITORING": porterv1.NodeGroupType_NODE_GROUP_TYPE_MONITORING,
  86. "APPLICATION": porterv1.NodeGroupType_NODE_GROUP_TYPE_APPLICATION,
  87. "CUSTOM": porterv1.NodeGroupType_NODE_GROUP_TYPE_CUSTOM,
  88. }
  89. )
  90. // protoNodeGroupTypeLookup is a helper function for finding a nodegroup, and returning a default if its not found
  91. func protoNodeGroupTypeLookup(apiNodeGroup string) porterv1.NodeGroupType {
  92. if ngt, ok := apiNodeGroupToProtoNodeGroup[apiNodeGroup]; ok {
  93. return ngt
  94. }
  95. return porterv1.NodeGroupType_NODE_GROUP_TYPE_CUSTOM
  96. }