project.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. package models
  2. import (
  3. "fmt"
  4. "gorm.io/gorm"
  5. "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
  6. "github.com/porter-dev/porter/api/types"
  7. "github.com/porter-dev/porter/internal/features"
  8. ints "github.com/porter-dev/porter/internal/models/integrations"
  9. )
  10. // FeatureFlagLabel strongly types project feature flags
  11. type FeatureFlagLabel string
  12. const (
  13. // APITokensEnabled allows users to create Bearer tokens for use with the Porter API
  14. // #nosec G101 - Not actually an api token
  15. APITokensEnabled FeatureFlagLabel = "api_tokens_enabled"
  16. // AzureEnabled enables Azure Provisioning
  17. AzureEnabled FeatureFlagLabel = "azure_enabled"
  18. // CapiProvisionerEnabled enables the CAPI Provisioning flow
  19. CapiProvisionerEnabled FeatureFlagLabel = "capi_provisioner_enabled"
  20. // BillingEnabled enables the "Billing" tab and all Stripe integrations
  21. BillingEnabled FeatureFlagLabel = "billing_enabled"
  22. // DBEnabled enables the "Databases" tab
  23. DBEnabled FeatureFlagLabel = "db_enabled"
  24. // EFSEnabled enables the "EFS" checkbox in App Settings
  25. EFSEnabled FeatureFlagLabel = "efs_enabled"
  26. // EnableReprovision enables the provisioning button after initial creation of the cluster
  27. EnableReprovision FeatureFlagLabel = "enable_reprovision"
  28. // FullAddOns shows all addons, not just curated
  29. FullAddOns FeatureFlagLabel = "full_add_ons"
  30. // GPUEnabled enables the "GPU for users"
  31. GPUEnabled FeatureFlagLabel = "gpu_enabled"
  32. // HelmValuesEnabled shows the helm values tab for porter apps (when simplified_view_enabled=true)
  33. HelmValuesEnabled FeatureFlagLabel = "helm_values_enabled"
  34. // ManagedInfraEnabled uses terraform provisioning instead of capi
  35. ManagedInfraEnabled FeatureFlagLabel = "managed_infra_enabled"
  36. // MultiCluster allows multiple clusters in simplified view (simplified_view_enabled=true)
  37. MultiCluster FeatureFlagLabel = "multi_cluster"
  38. // PreviewEnvsEnabled allows legacy user the ability to see preview environments in sidebar (simplified_view_enabled=false)
  39. PreviewEnvsEnabled FeatureFlagLabel = "preview_envs_enabled"
  40. // RDSDatabasesEnabled allows for users to provision RDS instances within their cluster vpc
  41. RDSDatabasesEnabled FeatureFlagLabel = "rds_databases_enabled"
  42. // QuotaIncrease enables whether we allow for auto increase of quota_increase
  43. QuotaIncrease FeatureFlagLabel = "quota_increase"
  44. // SimplifiedViewEnabled shows the new UI dashboard or not
  45. SimplifiedViewEnabled FeatureFlagLabel = "simplified_view_enabled"
  46. // SOC2ControlsEnabled decides on whether the SOC2 Compliance UI is shown on the infrastructure tab
  47. SOC2ControlsEnabled FeatureFlagLabel = "soc2_controls_enabled"
  48. // StacksEnabled uses stack view for legacy (simplified_view_enabled=false)
  49. StacksEnabled FeatureFlagLabel = "stacks_enabled"
  50. // ValidateApplyV2 controls whether apps deploys use a porter app revision contract vs helm
  51. ValidateApplyV2 FeatureFlagLabel = "validate_apply_v2"
  52. // BetaFeaturesEnabled controls whether a project uses beta features
  53. BetaFeaturesEnabled FeatureFlagLabel = "beta_features_enabled"
  54. // AWSACKAuthEnabled controls whether a project's AWS access is governed through AWS ACK
  55. AWSACKAuthEnabled FeatureFlagLabel = "aws_ack_auth_enabled"
  56. // ManagedDeploymentTargetsEnabled controls whether a project can use managed deployment targets
  57. ManagedDeploymentTargetsEnabled FeatureFlagLabel = "managed_deployment_targets_enabled"
  58. // AdvancedInfraEnabled controls whether a project can use advanced infrastructure settings
  59. AdvancedInfraEnabled FeatureFlagLabel = "advanced_infra_enabled"
  60. // AdvancedRbacEnabled controls whether a project can use advanced rbac settings
  61. AdvancedRbacEnabled FeatureFlagLabel = "advanced_rbac_enabled"
  62. )
  63. // ProjectFeatureFlags keeps track of all project-related feature flags
  64. var ProjectFeatureFlags = map[FeatureFlagLabel]bool{
  65. APITokensEnabled: false,
  66. AWSACKAuthEnabled: false,
  67. AzureEnabled: false,
  68. BetaFeaturesEnabled: false,
  69. CapiProvisionerEnabled: true,
  70. BillingEnabled: false,
  71. DBEnabled: false,
  72. EFSEnabled: false,
  73. EnableReprovision: false,
  74. FullAddOns: false,
  75. GPUEnabled: false,
  76. HelmValuesEnabled: false,
  77. ManagedInfraEnabled: false,
  78. MultiCluster: false,
  79. PreviewEnvsEnabled: false,
  80. QuotaIncrease: false,
  81. RDSDatabasesEnabled: false,
  82. SimplifiedViewEnabled: true,
  83. SOC2ControlsEnabled: false,
  84. StacksEnabled: false,
  85. ValidateApplyV2: true,
  86. ManagedDeploymentTargetsEnabled: false,
  87. AdvancedInfraEnabled: false,
  88. AdvancedRbacEnabled: false,
  89. }
  90. type ProjectPlan string
  91. const (
  92. ProjectPlanBasic ProjectPlan = "basic"
  93. ProjectPlanTeam ProjectPlan = "team"
  94. ProjectPlanGrowth ProjectPlan = "growth"
  95. ProjectPlanEnterprise ProjectPlan = "enterprise"
  96. )
  97. // Project type that extends gorm.Model
  98. type Project struct {
  99. gorm.Model `gorm:"embedded"`
  100. Name string `json:"name"`
  101. Roles []Role `json:"roles"`
  102. // BillingID corresponds to the id generated by the billing provider
  103. BillingID string
  104. BillingEnabled bool
  105. ProjectUsageID uint
  106. ProjectUsageCacheID uint
  107. // linked repos
  108. GitRepos []GitRepo `json:"git_repos,omitempty"`
  109. // linked registries
  110. Registries []Registry `json:"registries,omitempty"`
  111. // linked clusters
  112. Clusters []Cluster `json:"clusters"`
  113. ClusterCandidates []ClusterCandidate `json:"cluster_candidates"`
  114. // linked databases
  115. Databases []Database `json:"databases"`
  116. // linked helm repos
  117. HelmRepos []HelmRepo `json:"helm_repos"`
  118. // invitations to the project
  119. Invites []Invite `json:"invites"`
  120. // provisioned aws infra
  121. Infras []Infra `json:"infras"`
  122. // auth mechanisms
  123. KubeIntegrations []ints.KubeIntegration `json:"kube_integrations"`
  124. BasicIntegrations []ints.BasicIntegration `json:"basic_integrations"`
  125. OIDCIntegrations []ints.OIDCIntegration `json:"oidc_integrations"`
  126. OAuthIntegrations []ints.OAuthIntegration `json:"oauth_integrations"`
  127. AWSIntegrations []ints.AWSIntegration `json:"aws_integrations"`
  128. GCPIntegrations []ints.GCPIntegration `json:"gcp_integrations"`
  129. AzureIntegrations []ints.AzureIntegration `json:"azure_integrations"`
  130. GitlabIntegrations []ints.GitlabIntegration `json:"gitlab_integrations"`
  131. // Deprecated: use p.GetFeatureFlag(PreviewEnvsEnabled, *features.Client) instead
  132. PreviewEnvsEnabled bool
  133. // Deprecated: use p.GetFeatureFlag(RDSDatabasesEnabled, *features.Client) instead
  134. RDSDatabasesEnabled bool
  135. // Deprecated: use p.GetFeatureFlag(ManagedInfraEnabled, *features.Client) instead
  136. ManagedInfraEnabled bool
  137. // Deprecated: use p.GetFeatureFlag(StacksEnabled, *features.Client) instead
  138. StacksEnabled bool
  139. // Deprecated: use p.GetFeatureFlag(APITokensEnabled, *features.Client) instead
  140. APITokensEnabled bool
  141. // Deprecated: use p.GetFeatureFlag(CapiProvisionerEnabled, *features.Client) instead
  142. CapiProvisionerEnabled bool
  143. // Deprecated: use p.GetFeatureFlag(SimplifiedViewEnabled, *features.Client) instead
  144. SimplifiedViewEnabled bool
  145. // Deprecated: use p.GetFeatureFlag(AzureEnabled, *features.Client) instead
  146. AzureEnabled bool
  147. // Deprecated: use p.GetFeatureFlag(HelmValuesEnabled, *features.Client) instead
  148. HelmValuesEnabled bool
  149. // Deprecated: use p.GetFeatureFlag(MultiCluster, *features.Client) instead
  150. MultiCluster bool `gorm:"default:false"`
  151. // Deprecated: use p.GetFeatureFlag(FullAddOns, *features.Client) instead
  152. FullAddOns bool `gorm:"default:false"`
  153. // Deprecated: use p.GetFeatureFlag(ValidateApplyV2, *features.Client) instead
  154. ValidateApplyV2 bool `gorm:"default:false"`
  155. // Deprecated: use p.GetFeatureFlag(EnableReprovision, *features.Client) instead
  156. EnableSandbox bool `gorm:"default:false"`
  157. EnableReprovision bool `gorm:"default:false"`
  158. AdvancedInfraEnabled bool `gorm:"default:false"`
  159. AdvancedRbacEnabled bool `gorm:"default:false"`
  160. }
  161. // GetFeatureFlag calls launchdarkly for the specified flag
  162. // and returns the configured value
  163. func (p *Project) GetFeatureFlag(flagName FeatureFlagLabel, launchDarklyClient *features.Client) bool {
  164. if launchDarklyClient.UseDatabase() {
  165. // case switch things
  166. switch flagName {
  167. case "api_tokens_enabled":
  168. return p.APITokensEnabled
  169. case "azure_enabled":
  170. return p.AzureEnabled
  171. case "capi_provisioner_enabled":
  172. return p.CapiProvisionerEnabled
  173. case "billing_enabled":
  174. return p.BillingEnabled
  175. case "db_enabled":
  176. return false
  177. case "enable_reprovision":
  178. return p.EnableReprovision
  179. case "full_add_ons":
  180. return p.FullAddOns
  181. case "gpu_enabled":
  182. return false
  183. case "helm_values_enabled":
  184. return p.HelmValuesEnabled
  185. case "managed_infra_enabled":
  186. return p.ManagedInfraEnabled
  187. case "multi_cluster":
  188. return p.MultiCluster
  189. case "preview_envs_enabled":
  190. return p.PreviewEnvsEnabled
  191. case "quota_increase":
  192. return false
  193. case "rds_databases_enabled":
  194. return p.RDSDatabasesEnabled
  195. case "simplified_view_enabled":
  196. return p.SimplifiedViewEnabled
  197. case "soc2_controls_enabled":
  198. return false
  199. case "stacks_enabled":
  200. return p.StacksEnabled
  201. case "validate_apply_v2":
  202. return p.ValidateApplyV2
  203. case "efs_enabled":
  204. return false
  205. case "aws_ack_auth_enabled":
  206. return false
  207. case "advanced_infra_enabled":
  208. return false
  209. case "advanced_rbac_enabled":
  210. return p.AdvancedRbacEnabled
  211. }
  212. }
  213. projectID := p.ID
  214. projectName := p.Name
  215. ldContext := getProjectContext(projectID, projectName)
  216. defaultValue := ProjectFeatureFlags[flagName]
  217. value, _ := launchDarklyClient.BoolVariation(string(flagName), ldContext, defaultValue)
  218. return value
  219. }
  220. // ToProjectType generates an external types.Project to be shared over REST
  221. func (p *Project) ToProjectType(launchDarklyClient *features.Client) types.Project {
  222. roles := make([]*types.Role, 0)
  223. for _, role := range p.Roles {
  224. roles = append(roles, role.ToRoleType())
  225. }
  226. projectID := p.ID
  227. projectName := p.Name
  228. return types.Project{
  229. ID: projectID,
  230. Name: projectName,
  231. Roles: roles,
  232. APITokensEnabled: p.GetFeatureFlag(APITokensEnabled, launchDarklyClient),
  233. AWSACKAuthEnabled: p.GetFeatureFlag(AWSACKAuthEnabled, launchDarklyClient),
  234. AzureEnabled: p.GetFeatureFlag(AzureEnabled, launchDarklyClient),
  235. BetaFeaturesEnabled: p.GetFeatureFlag(BetaFeaturesEnabled, launchDarklyClient),
  236. CapiProvisionerEnabled: p.GetFeatureFlag(CapiProvisionerEnabled, launchDarklyClient),
  237. BillingEnabled: p.GetFeatureFlag(BillingEnabled, launchDarklyClient),
  238. DBEnabled: p.GetFeatureFlag(DBEnabled, launchDarklyClient),
  239. EFSEnabled: p.GetFeatureFlag(EFSEnabled, launchDarklyClient),
  240. EnableReprovision: p.GetFeatureFlag(EnableReprovision, launchDarklyClient),
  241. FullAddOns: p.GetFeatureFlag(FullAddOns, launchDarklyClient),
  242. GPUEnabled: p.GetFeatureFlag(GPUEnabled, launchDarklyClient),
  243. HelmValuesEnabled: p.GetFeatureFlag(HelmValuesEnabled, launchDarklyClient),
  244. ManagedInfraEnabled: p.GetFeatureFlag(ManagedInfraEnabled, launchDarklyClient),
  245. MultiCluster: p.GetFeatureFlag(MultiCluster, launchDarklyClient),
  246. PreviewEnvsEnabled: p.GetFeatureFlag(PreviewEnvsEnabled, launchDarklyClient),
  247. QuotaIncrease: p.GetFeatureFlag(QuotaIncrease, launchDarklyClient),
  248. RDSDatabasesEnabled: p.GetFeatureFlag(RDSDatabasesEnabled, launchDarklyClient),
  249. SimplifiedViewEnabled: p.GetFeatureFlag(SimplifiedViewEnabled, launchDarklyClient),
  250. SOC2ControlsEnabled: p.GetFeatureFlag(SOC2ControlsEnabled, launchDarklyClient),
  251. StacksEnabled: p.GetFeatureFlag(StacksEnabled, launchDarklyClient),
  252. ValidateApplyV2: p.GetFeatureFlag(ValidateApplyV2, launchDarklyClient),
  253. ManagedDeploymentTargetsEnabled: p.GetFeatureFlag(ManagedDeploymentTargetsEnabled, launchDarklyClient),
  254. AdvancedInfraEnabled: p.GetFeatureFlag(AdvancedInfraEnabled, launchDarklyClient),
  255. SandboxEnabled: p.EnableSandbox,
  256. AdvancedRbacEnabled: p.GetFeatureFlag(AdvancedRbacEnabled, launchDarklyClient),
  257. }
  258. }
  259. // ToProjectListType returns a "minified" version of a Project
  260. // suitable for api responses to GET /projects
  261. // TODO: update this in the future to use default values for all
  262. // the feature flags instead of trying to retrieve them from the database
  263. func (p *Project) ToProjectListType() *types.ProjectList {
  264. var roles []types.Role
  265. for _, role := range p.Roles {
  266. roles = append(roles, *role.ToRoleType())
  267. }
  268. return &types.ProjectList{
  269. ID: p.ID,
  270. Name: p.Name,
  271. // note: all of these fields should be considered deprecated
  272. // in an api response
  273. Roles: roles,
  274. BillingEnabled: p.BillingEnabled,
  275. PreviewEnvsEnabled: p.PreviewEnvsEnabled,
  276. RDSDatabasesEnabled: p.RDSDatabasesEnabled,
  277. ManagedInfraEnabled: p.ManagedInfraEnabled,
  278. StacksEnabled: p.StacksEnabled,
  279. APITokensEnabled: p.APITokensEnabled,
  280. DBEnabled: false,
  281. CapiProvisionerEnabled: p.CapiProvisionerEnabled,
  282. SimplifiedViewEnabled: p.SimplifiedViewEnabled,
  283. AzureEnabled: p.AzureEnabled,
  284. HelmValuesEnabled: p.HelmValuesEnabled,
  285. MultiCluster: p.MultiCluster,
  286. EnableReprovision: p.EnableReprovision,
  287. ValidateApplyV2: p.ValidateApplyV2,
  288. FullAddOns: p.FullAddOns,
  289. AdvancedInfraEnabled: p.AdvancedInfraEnabled,
  290. AdvancedRbacEnabled: p.AdvancedRbacEnabled,
  291. }
  292. }
  293. func getProjectContext(projectID uint, projectName string) ldcontext.Context {
  294. projectIdentifier := fmt.Sprintf("project-%d", projectID)
  295. launchDarklyName := fmt.Sprintf("%s: %s", projectIdentifier, projectName)
  296. return ldcontext.NewBuilder(projectIdentifier).
  297. Kind("project").
  298. Name(launchDarklyName).
  299. SetInt("project_id", int(projectID)).
  300. Build()
  301. }