cluster.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. package models
  2. import (
  3. "encoding/json"
  4. "github.com/porter-dev/porter/internal/models/integrations"
  5. "gorm.io/gorm"
  6. )
  7. // ClusterAuth is an auth mechanism that a cluster candidate can resolve
  8. type ClusterAuth string
  9. // The support cluster candidate auth mechanisms
  10. const (
  11. X509 ClusterAuth = "x509"
  12. Basic ClusterAuth = "basic"
  13. Bearer ClusterAuth = "bearerToken"
  14. OIDC ClusterAuth = "oidc"
  15. GCP ClusterAuth = "gcp-sa"
  16. AWS ClusterAuth = "aws-sa"
  17. DO ClusterAuth = "do-oauth"
  18. Local ClusterAuth = "local"
  19. )
  20. // Cluster is an integration that can connect to a Kubernetes cluster via
  21. // a specific auth mechanism
  22. type Cluster struct {
  23. gorm.Model
  24. // The auth mechanism that this cluster will use
  25. AuthMechanism ClusterAuth `json:"auth_mechanism"`
  26. // The project that this integration belongs to
  27. ProjectID uint `json:"project_id"`
  28. // Name of the cluster
  29. Name string `json:"name"`
  30. // Server endpoint for the cluster
  31. Server string `json:"server"`
  32. // Additional fields optionally used by the kube client
  33. ClusterLocationOfOrigin string `json:"location_of_origin,omitempty"`
  34. TLSServerName string `json:"tls-server-name,omitempty"`
  35. InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
  36. ProxyURL string `json:"proxy-url,omitempty"`
  37. UserLocationOfOrigin string
  38. UserImpersonate string `json:"act-as,omitempty"`
  39. UserImpersonateGroups string `json:"act-as-groups,omitempty"`
  40. InfraID uint `json:"infra_id"`
  41. // ------------------------------------------------------------------
  42. // All fields below this line are encrypted before storage
  43. // ------------------------------------------------------------------
  44. // The various auth mechanisms available to the integration
  45. KubeIntegrationID uint
  46. OIDCIntegrationID uint
  47. GCPIntegrationID uint
  48. AWSIntegrationID uint
  49. DOIntegrationID uint
  50. // A token cache that can be used by an auth mechanism, if desired
  51. TokenCache integrations.ClusterTokenCache `json:"token_cache"`
  52. // CertificateAuthorityData for the cluster, encrypted at rest
  53. CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
  54. }
  55. // ClusterExternal is an external Cluster to be shared over REST
  56. type ClusterExternal struct {
  57. ID uint `json:"id"`
  58. // The project that this integration belongs to
  59. ProjectID uint `json:"project_id"`
  60. // Name of the cluster
  61. Name string `json:"name"`
  62. // Server endpoint for the cluster
  63. Server string `json:"server"`
  64. // The integration service for this cluster
  65. Service integrations.IntegrationService `json:"service"`
  66. // The infra id, if cluster was provisioned with Porter
  67. InfraID uint `json:"infra_id"`
  68. // (optional) The aws integration id, if available
  69. AWSIntegrationID uint `json:"aws_integration_id"`
  70. }
  71. // Externalize generates an external Cluster to be shared over REST
  72. func (c *Cluster) Externalize() *ClusterExternal {
  73. serv := integrations.Kube
  74. if c.AWSIntegrationID != 0 {
  75. serv = integrations.EKS
  76. } else if c.GCPIntegrationID != 0 {
  77. serv = integrations.GKE
  78. } else if c.DOIntegrationID != 0 {
  79. serv = integrations.DOKS
  80. }
  81. return &ClusterExternal{
  82. ID: c.ID,
  83. ProjectID: c.ProjectID,
  84. Name: c.Name,
  85. Server: c.Server,
  86. Service: serv,
  87. InfraID: c.InfraID,
  88. AWSIntegrationID: c.AWSIntegrationID,
  89. }
  90. }
  91. type ClusterDetailedExternal struct {
  92. // Simple cluster external data
  93. ClusterExternal
  94. // The NGINX Ingress IP to access the cluster
  95. IngressIP string `json:"ingress_ip"`
  96. // Error displayed in case couldn't get the IP
  97. IngressError error `json:"ingress_error"`
  98. }
  99. func (c *Cluster) DetailedExternalize() *ClusterDetailedExternal {
  100. clusterExt := c.Externalize()
  101. return &ClusterDetailedExternal{
  102. ClusterExternal: *clusterExt,
  103. }
  104. }
  105. // ClusterCandidate is a cluster integration that requires additional action
  106. // from the user to set up.
  107. type ClusterCandidate struct {
  108. gorm.Model
  109. // The auth mechanism that this candidate will parse for
  110. AuthMechanism ClusterAuth `json:"auth_mechanism"`
  111. // The project that this integration belongs to
  112. ProjectID uint `json:"project_id"`
  113. // CreatedClusterID is the ID of the cluster that's eventually
  114. // created
  115. CreatedClusterID uint `json:"created_cluster_id"`
  116. // Resolvers are the list of resolvers: once all resolvers are "resolved," the
  117. // cluster will be created
  118. Resolvers []ClusterResolver `json:"resolvers"`
  119. // Name of the cluster
  120. Name string `json:"name"`
  121. // Server endpoint for the cluster
  122. Server string `json:"server"`
  123. // Name of the context that this was created from, if it exists
  124. ContextName string `json:"context_name"`
  125. // ------------------------------------------------------------------
  126. // All fields below this line are encrypted before storage
  127. // ------------------------------------------------------------------
  128. // The best-guess for the AWSClusterID, which is required by aws auth mechanisms
  129. // See https://github.com/kubernetes-sigs/aws-iam-authenticator#what-is-a-cluster-id
  130. AWSClusterIDGuess []byte `json:"aws_cluster_id_guess"`
  131. // The raw kubeconfig
  132. Kubeconfig []byte `json:"kubeconfig"`
  133. }
  134. // ClusterCandidateExternal represents the ClusterCandidate to be sent over REST
  135. type ClusterCandidateExternal struct {
  136. ID uint `json:"id"`
  137. // The project that this integration belongs to
  138. ProjectID uint `json:"project_id"`
  139. // CreatedClusterID is the ID of the cluster that's eventually
  140. // created
  141. CreatedClusterID uint `json:"created_cluster_id"`
  142. // Name of the cluster
  143. Name string `json:"name"`
  144. // Server endpoint for the cluster
  145. Server string `json:"server"`
  146. // Name of the context that this was created from, if it exists
  147. ContextName string `json:"context_name"`
  148. // Resolvers are the list of resolvers: once all resolvers are "resolved," the
  149. // cluster will be created
  150. Resolvers []ClusterResolverExternal `json:"resolvers"`
  151. // The best-guess for the AWSClusterID, which is required by aws auth mechanisms
  152. // See https://github.com/kubernetes-sigs/aws-iam-authenticator#what-is-a-cluster-id
  153. AWSClusterIDGuess string `json:"aws_cluster_id_guess"`
  154. }
  155. // Externalize generates an external ClusterCandidateExternal to be shared over REST
  156. func (cc *ClusterCandidate) Externalize() *ClusterCandidateExternal {
  157. resolvers := make([]ClusterResolverExternal, 0)
  158. for _, resolver := range cc.Resolvers {
  159. resolvers = append(resolvers, *resolver.Externalize())
  160. }
  161. return &ClusterCandidateExternal{
  162. ID: cc.ID,
  163. ProjectID: cc.ProjectID,
  164. CreatedClusterID: cc.CreatedClusterID,
  165. Name: cc.Name,
  166. Server: cc.Server,
  167. ContextName: cc.ContextName,
  168. Resolvers: resolvers,
  169. AWSClusterIDGuess: string(cc.AWSClusterIDGuess),
  170. }
  171. }
  172. // ClusterResolverName is the name for a cluster resolve
  173. type ClusterResolverName string
  174. // Options for the cluster resolver names
  175. const (
  176. ClusterCAData ClusterResolverName = "upload-cluster-ca-data"
  177. ClusterLocalhost = "rewrite-cluster-localhost"
  178. ClientCertData = "upload-client-cert-data"
  179. ClientKeyData = "upload-client-key-data"
  180. OIDCIssuerData = "upload-oidc-idp-issuer-ca-data"
  181. TokenData = "upload-token-data"
  182. GCPKeyData = "upload-gcp-key-data"
  183. AWSData = "upload-aws-data"
  184. )
  185. // ClusterResolverInfo contains the information for actions to be
  186. // performed in order to initialize a cluster
  187. type ClusterResolverInfo struct {
  188. // Docs is a link to documentation that helps resolve this manually
  189. Docs string `json:"docs"`
  190. // a comma-separated list of required fields to send in an action request
  191. Fields string `json:"fields"`
  192. }
  193. // ClusterResolverInfos is a map of the information for actions to be
  194. // performed in order to initialize a cluster
  195. var ClusterResolverInfos = map[ClusterResolverName]ClusterResolverInfo{
  196. ClusterCAData: ClusterResolverInfo{
  197. Docs: "https://github.com/porter-dev/porter",
  198. Fields: "cluster_ca_data",
  199. },
  200. ClusterLocalhost: ClusterResolverInfo{
  201. Docs: "https://github.com/porter-dev/porter",
  202. Fields: "cluster_hostname",
  203. },
  204. ClientCertData: ClusterResolverInfo{
  205. Docs: "https://github.com/porter-dev/porter",
  206. Fields: "client_cert_data",
  207. },
  208. ClientKeyData: ClusterResolverInfo{
  209. Docs: "https://github.com/porter-dev/porter",
  210. Fields: "client_key_data",
  211. },
  212. OIDCIssuerData: ClusterResolverInfo{
  213. Docs: "https://github.com/porter-dev/porter",
  214. Fields: "oidc_idp_issuer_ca_data",
  215. },
  216. TokenData: ClusterResolverInfo{
  217. Docs: "https://github.com/porter-dev/porter",
  218. Fields: "token_data",
  219. },
  220. GCPKeyData: ClusterResolverInfo{
  221. Docs: "https://github.com/porter-dev/porter",
  222. Fields: "gcp_key_data",
  223. },
  224. AWSData: ClusterResolverInfo{
  225. Docs: "https://github.com/porter-dev/porter",
  226. Fields: "aws_access_key_id,aws_secret_access_key,aws_cluster_id",
  227. },
  228. }
  229. // ClusterResolverAll is a helper type that contains the fields for
  230. // all possible resolvers, so that raw bytes can be unmarshaled in a single
  231. // read
  232. type ClusterResolverAll struct {
  233. ClusterCAData string `json:"cluster_ca_data,omitempty"`
  234. ClusterHostname string `json:"cluster_hostname,omitempty"`
  235. ClientCertData string `json:"client_cert_data,omitempty"`
  236. ClientKeyData string `json:"client_key_data,omitempty"`
  237. OIDCIssuerCAData string `json:"oidc_idp_issuer_ca_data,omitempty"`
  238. TokenData string `json:"token_data,omitempty"`
  239. GCPKeyData string `json:"gcp_key_data,omitempty"`
  240. AWSAccessKeyID string `json:"aws_access_key_id"`
  241. AWSSecretAccessKey string `json:"aws_secret_access_key"`
  242. AWSClusterID string `json:"aws_cluster_id"`
  243. }
  244. // ClusterResolver is an action that must be resolved to set up
  245. // a Cluster
  246. type ClusterResolver struct {
  247. gorm.Model
  248. // The ClusterCandidate that this is resolving
  249. ClusterCandidateID uint `json:"cluster_candidate_id"`
  250. // One of the ClusterResolverNames
  251. Name ClusterResolverName `json:"name"`
  252. // Resolved is true if this has been resolved, false otherwise
  253. Resolved bool `json:"resolved"`
  254. // Data is additional data for resolving the action, for example a file name,
  255. // context name, etc
  256. Data []byte `json:"data,omitempty"`
  257. }
  258. // ClusterResolverData is a map of key names to fields, which gets marshaled from
  259. // the raw JSON bytes stored in the ClusterResolver
  260. type ClusterResolverData map[string]string
  261. // ClusterResolverExternal is an external ClusterResolver to be shared over REST
  262. type ClusterResolverExternal struct {
  263. ID uint `json:"id"`
  264. // The ClusterCandidate that this is resolving
  265. ClusterCandidateID uint `json:"cluster_candidate_id"`
  266. // One of the ClusterResolverNames
  267. Name ClusterResolverName `json:"name"`
  268. // Resolved is true if this has been resolved, false otherwise
  269. Resolved bool `json:"resolved"`
  270. // Docs is a link to documentation that helps resolve this manually
  271. Docs string `json:"docs"`
  272. // Fields is a list of fields that must be sent with the resolving request
  273. Fields string `json:"fields"`
  274. // Data is additional data for resolving the action, for example a file name,
  275. // context name, etc
  276. Data ClusterResolverData `json:"data,omitempty"`
  277. }
  278. // Externalize generates an external ClusterResolver to be shared over REST
  279. func (cr *ClusterResolver) Externalize() *ClusterResolverExternal {
  280. info := ClusterResolverInfos[cr.Name]
  281. data := make(ClusterResolverData)
  282. json.Unmarshal(cr.Data, &data)
  283. return &ClusterResolverExternal{
  284. ID: cr.ID,
  285. ClusterCandidateID: cr.ClusterCandidateID,
  286. Name: cr.Name,
  287. Resolved: cr.Resolved,
  288. Docs: info.Docs,
  289. Fields: info.Fields,
  290. Data: data,
  291. }
  292. }