2
0

cluster.go 12 KB

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