فهرست منبع

Add Metronome feature flag

Mauricio Araujo 2 سال پیش
والد
کامیت
0a74516698

+ 0 - 37
.github/workflows/porter_stack_porter-ui copy.yml

@@ -1,37 +0,0 @@
-'on':
-  push:
-    branches:
-      - metronome-integration
-name: Deploy Porter to Internal Tooling
-jobs:
-  build-go:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v3
-      - name: build-go
-        uses: ./.github/actions/build-go
-
-  build-npm:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v3
-      - name: build-npm
-        uses: ./.github/actions/build-npm
-
-  porter-deploy:
-    runs-on: ubuntu-latest
-    needs: [build-go, build-npm]
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v3
-      - name: porter-deploy
-        timeout-minutes: 30
-        uses: ./.github/actions/porter-deploy
-        with:
-          app: porter-ui
-          cluster: '37'
-          host: https://dashboard.internal-tools.porter.run
-          project: '18'
-          token: ${{ secrets.PORTER_STAGING_DEPLOYMENT }}

+ 4 - 0
api/server/handlers/billing/credits.go

@@ -33,6 +33,10 @@ func (c *GetCreditsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 	proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
 
+	if !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) {
+		c.WriteResult(w, r, "")
+	}
+
 	credits, err := c.Config().BillingManager.MetronomeClient.GetCustomerCredits(proj.UsageID)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error getting credits")

+ 48 - 43
api/server/handlers/billing/customer.go

@@ -37,63 +37,68 @@ func (c *CreateBillingCustomerHandler) ServeHTTP(w http.ResponseWriter, r *http.
 	proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 
-	if proj.BillingID != "" {
-		c.WriteResult(w, r, "")
-		return
-	}
+	var shouldUpdate bool
+	if proj.BillingID == "" {
+		// Create customer in Stripe
+		customerID, err := c.Config().BillingManager.StripeClient.CreateCustomer(ctx, user.Email, proj)
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error creating billing customer")
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error creating billing customer: %w", err)))
+			return
+		}
 
-	// Create customer in Stripe
-	customerID, err := c.Config().BillingManager.StripeClient.CreateCustomer(ctx, user.Email, proj)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error creating billing customer")
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error creating billing customer: %w", err)))
-		return
+		telemetry.WithAttributes(span,
+			telemetry.AttributeKV{Key: "project-id", Value: proj.ID},
+			telemetry.AttributeKV{Key: "customer-id", Value: proj.BillingID},
+			telemetry.AttributeKV{Key: "user-email", Value: user.Email},
+		)
+		proj.BillingID = customerID
+		shouldUpdate = true
 	}
 
-	telemetry.WithAttributes(span,
-		telemetry.AttributeKV{Key: "project-id", Value: proj.ID},
-		telemetry.AttributeKV{Key: "customer-id", Value: proj.BillingID},
-		telemetry.AttributeKV{Key: "user-email", Value: user.Email},
-	)
+	if proj.UsageID == uuid.Nil {
+		// Create Metronome customer and add to starter plan
+		if c.Config().ServerConf.MetronomeAPIKey != "" && c.Config().ServerConf.PorterCloudPlanID != "" &&
+			proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) {
+
+			// Create Metronome Customer
+			if c.Config().ServerConf.MetronomeAPIKey != "" {
+				usageID, err := c.Config().BillingManager.MetronomeClient.CreateCustomer(user.CompanyName, proj.Name, proj.ID, proj.BillingID)
+				if err != nil {
+					err = telemetry.Error(ctx, span, err, "error creating billing customer")
+					c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+					return
+				}
+				proj.UsageID = usageID
+			}
 
-	// Create Metronome customer and add to starter plan
-	if c.Config().ServerConf.MetronomeAPIKey != "" && c.Config().ServerConf.PorterCloudPlanID != "" &&
-		c.Config().ServerConf.EnableSandbox {
-		// Create Metronome Customer
-		if c.Config().ServerConf.MetronomeAPIKey != "" {
-			usageID, err := c.Config().BillingManager.MetronomeClient.CreateCustomer(user.CompanyName, proj.Name, proj.ID, proj.BillingID)
+			porterCloudPlanID, err := uuid.Parse(c.Config().ServerConf.PorterCloudPlanID)
 			if err != nil {
-				err = telemetry.Error(ctx, span, err, "error creating billing customer")
+				err = telemetry.Error(ctx, span, err, "error parsing starter plan id")
 				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
-			proj.UsageID = usageID
-		}
 
-		porterCloudPlanID, err := uuid.Parse(c.Config().ServerConf.PorterCloudPlanID)
-		if err != nil {
-			err = telemetry.Error(ctx, span, err, "error parsing starter plan id")
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-			return
+			// Add to starter plan
+			customerPlanID, err := c.Config().BillingManager.MetronomeClient.AddCustomerPlan(proj.UsageID, porterCloudPlanID)
+			if err != nil {
+				err = telemetry.Error(ctx, span, err, "error adding customer to starter plan")
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
+			}
+			proj.UsagePlanID = customerPlanID
+			shouldUpdate = true
 		}
+	}
 
-		// Add to starter plan
-		customerPlanID, err := c.Config().BillingManager.MetronomeClient.AddCustomerPlan(proj.UsageID, porterCloudPlanID)
+	if shouldUpdate {
+		// Update the project record with the customer ID
+		_, err := c.Repo().Project().UpdateProject(proj)
 		if err != nil {
-			err = telemetry.Error(ctx, span, err, "error adding customer to starter plan")
-			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			err := telemetry.Error(ctx, span, err, "error updating project")
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error updating project: %w", err)))
 			return
 		}
-		proj.UsagePlanID = customerPlanID
-	}
-
-	// Update the project record with the customer ID
-	proj.BillingID = customerID
-	_, err = c.Repo().Project().UpdateProject(proj)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error updating project")
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error updating project: %w", err)))
-		return
 	}
 
 	c.WriteResult(w, r, "")

+ 3 - 1
api/server/handlers/project/create.go

@@ -99,7 +99,9 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	}
 
 	// Create Metronome customer and add to starter plan
-	if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" {
+	if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" &&
+		proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) {
+
 		// Create Metronome Customer
 		if p.Config().ServerConf.MetronomeAPIKey != "" {
 			usageID, err := p.Config().BillingManager.MetronomeClient.CreateCustomer(user.CompanyName, proj.Name, proj.ID, proj.BillingID)

+ 2 - 1
api/server/handlers/project/delete.go

@@ -92,7 +92,8 @@ func (p *ProjectDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" {
+	if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" &&
+		proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) {
 		err = p.Config().BillingManager.MetronomeClient.EndCustomerPlan(proj.UsageID, proj.UsagePlanID)
 		if err != nil {
 			e := "error ending billing plan"

+ 3 - 3
api/server/shared/config/env/envconfs.go

@@ -71,9 +71,9 @@ type ServerConf struct {
 	SendgridDeleteProjectTemplateID    string `env:"SENDGRID_DELETE_PROJECT_TEMPLATE_ID"`
 	SendgridSenderEmail                string `env:"SENDGRID_SENDER_EMAIL"`
 
-	StripeSecretKey      string    `env:"STRIPE_SECRET_KEY"`
-	StripePublishableKey string    `env:"STRIPE_PUBLISHABLE_KEY"`
-	MetronomeAPIKey      string    `env:"METRONOME_API_KEY"`
+	StripeSecretKey      string `env:"STRIPE_SECRET_KEY"`
+	StripePublishableKey string `env:"STRIPE_PUBLISHABLE_KEY"`
+	MetronomeAPIKey      string `env:"METRONOME_API_KEY"`
 	PorterCloudPlanID    string `env:"PORTER_CLOUD_PLAN_ID"`
 
 	SlackClientID     string `env:"SLACK_CLIENT_ID"`

+ 1 - 0
api/types/project.go

@@ -39,6 +39,7 @@ type Project struct {
 	BetaFeaturesEnabled             bool    `json:"beta_features_enabled"`
 	CapiProvisionerEnabled          bool    `json:"capi_provisioner_enabled"`
 	BillingEnabled                  bool    `json:"billing_enabled"`
+	MetronomeEnabled                bool    `json:"metronome_enabled"`
 	DBEnabled                       bool    `json:"db_enabled"`
 	EFSEnabled                      bool    `json:"efs_enabled"`
 	EnableReprovision               bool    `json:"enable_reprovision"`

+ 24 - 17
dashboard/src/main/home/project-settings/BillingPage.tsx

@@ -26,6 +26,9 @@ import BillingModal from "../modals/BillingModal";
 function BillingPage(): JSX.Element {
   const { setCurrentOverlay } = useContext(Context);
   const [shouldCreate, setShouldCreate] = useState(false);
+  const { currentProject } = useContext(Context);
+
+  const { credits } = usePorterCredits();
 
   const {
     paymentMethodList,
@@ -37,8 +40,6 @@ function BillingPage(): JSX.Element {
 
   const { refetchPaymentEnabled } = checkIfProjectHasPayment();
 
-  const { credits } = usePorterCredits();
-
   const formatCredits = (credits: number): string => {
     return (credits / 100).toFixed(2);
   };
@@ -62,21 +63,27 @@ function BillingPage(): JSX.Element {
 
   return (
     <>
-      <Text size={16}>Porter credit balance</Text>
-      <Spacer y={1} />
-      <Text color="helper">
-        View the amount of Porter credits you have available to spend on
-        resources within this project.
-      </Text>
-      <Spacer y={1} />
-      <Container row>
-        <Image src={gift} style={{ marginTop: "-2px" }} />
-        <Spacer inline x={1} />
-        <Text size={20}>
-          {credits > 0 ? `$${formatCredits(credits)}` : "$ 0.00"}
-        </Text>
-      </Container>
-      <Spacer y={2} />
+      {currentProject?.metronome_enabled ? (
+        <div>
+          <Text size={16}>Porter credit balance</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            View the amount of Porter credits you have available to spend on
+            resources within this project.
+          </Text>
+          <Spacer y={1} />
+          <Container row>
+            <Image src={gift} style={{ marginTop: "-2px" }} />
+            <Spacer inline x={1} />
+            <Text size={20}>
+              {credits > 0 ? `$${formatCredits(credits)}` : "$ 0.00"}
+            </Text>
+          </Container>
+          <Spacer y={2} />
+        </div>
+      ) : (
+        <div></div>
+      )}
       <Text size={16}>Payment methods</Text>
       <Spacer y={1} />
       <Text color="helper">

+ 7 - 6
dashboard/src/shared/types.tsx

@@ -289,15 +289,15 @@ export type FormElement = {
 export type RepoType = {
   FullName: string;
 } & (
-    | {
+  | {
       Kind: "github";
       GHRepoID: number;
     }
-    | {
+  | {
       Kind: "gitlab";
       GitIntegrationId: number;
     }
-  );
+);
 
 export type FileType = {
   path: string;
@@ -316,6 +316,7 @@ export type ProjectType = {
   azure_enabled: boolean;
   beta_features_enabled: boolean;
   billing_enabled: boolean;
+  metronome_enabled: boolean;
   capi_provisioner_enabled: boolean;
   db_enabled: boolean;
   efs_enabled: boolean;
@@ -377,15 +378,15 @@ export type ActionConfigType = {
   image_repo_uri: string;
   dockerfile_path?: string;
 } & (
-    | {
+  | {
       kind: "gitlab";
       gitlab_integration_id: number;
     }
-    | {
+  | {
       kind: "github";
       git_repo_id: number;
     }
-  );
+);
 
 export type GithubActionConfigType = ActionConfigType & {
   kind: "github";

+ 4 - 1
internal/models/project.go

@@ -27,7 +27,8 @@ const (
 	CapiProvisionerEnabled FeatureFlagLabel = "capi_provisioner_enabled"
 
 	// BillingEnabled enables the "Billing" tab and all Stripe integrations
-	BillingEnabled FeatureFlagLabel = "billing_enabled"
+	BillingEnabled   FeatureFlagLabel = "billing_enabled"
+	MetronomeEnabled FeatureFlagLabel = "metronome_enabled"
 
 	// DBEnabled enables the "Databases" tab
 	DBEnabled FeatureFlagLabel = "db_enabled"
@@ -98,6 +99,7 @@ var ProjectFeatureFlags = map[FeatureFlagLabel]bool{
 	BetaFeaturesEnabled:             false,
 	CapiProvisionerEnabled:          true,
 	BillingEnabled:                  false,
+	MetronomeEnabled:                false,
 	DBEnabled:                       false,
 	EFSEnabled:                      false,
 	EnableReprovision:               false,
@@ -303,6 +305,7 @@ func (p *Project) ToProjectType(launchDarklyClient *features.Client) types.Proje
 		BetaFeaturesEnabled:             p.GetFeatureFlag(BetaFeaturesEnabled, launchDarklyClient),
 		CapiProvisionerEnabled:          p.GetFeatureFlag(CapiProvisionerEnabled, launchDarklyClient),
 		BillingEnabled:                  p.GetFeatureFlag(BillingEnabled, launchDarklyClient),
+		MetronomeEnabled:                p.GetFeatureFlag(MetronomeEnabled, launchDarklyClient),
 		DBEnabled:                       p.GetFeatureFlag(DBEnabled, launchDarklyClient),
 		EFSEnabled:                      p.GetFeatureFlag(EFSEnabled, launchDarklyClient),
 		EnableReprovision:               p.GetFeatureFlag(EnableReprovision, launchDarklyClient),