Преглед на файлове

feature flag for capi provisioner

Justin Rhee преди 3 години
родител
ревизия
8d679e103c

+ 10 - 8
api/types/project.go

@@ -1,14 +1,15 @@
 package types
 
 type Project struct {
-	ID                  uint    `json:"id"`
-	Name                string  `json:"name"`
-	Roles               []*Role `json:"roles"`
-	PreviewEnvsEnabled  bool    `json:"preview_envs_enabled"`
-	RDSDatabasesEnabled bool    `json:"enable_rds_databases"`
-	ManagedInfraEnabled bool    `json:"managed_infra_enabled"`
-	APITokensEnabled    bool    `json:"api_tokens_enabled"`
-	StacksEnabled       bool    `json:"stacks_enabled"`
+	ID                     uint    `json:"id"`
+	Name                   string  `json:"name"`
+	Roles                  []*Role `json:"roles"`
+	PreviewEnvsEnabled     bool    `json:"preview_envs_enabled"`
+	RDSDatabasesEnabled    bool    `json:"enable_rds_databases"`
+	ManagedInfraEnabled    bool    `json:"managed_infra_enabled"`
+	APITokensEnabled       bool    `json:"api_tokens_enabled"`
+	StacksEnabled          bool    `json:"stacks_enabled"`
+	CAPIProvisionerEnabled bool    `json:"capi_provisioner_enabled"`
 }
 
 type FeatureFlags struct {
@@ -16,6 +17,7 @@ type FeatureFlags struct {
 	ManagedInfraEnabled        string `json:"managed_infra_enabled,omitempty"`
 	StacksEnabled              string `json:"stacks_enabled,omitempty"`
 	ManagedDatabasesEnabled    string `json:"managed_databases_enabled,omitempty"`
+	CAPIProvisionerEnabled     string `json:"capi_provisioner_enabled,omitempty"`
 }
 
 type CreateProjectRequest struct {

+ 37 - 3
dashboard/src/components/CredentialsForm.tsx

@@ -8,6 +8,7 @@ import Heading from "components/form-components/Heading";
 import Helper from "./form-components/Helper";
 
 type Props = {
+  goBack: () => void;
 };
 
 type AWSCredential = {
@@ -20,6 +21,7 @@ type AWSCredential = {
 
 
 const CredentialsForm: React.FC<Props> = ({
+  goBack,
 }) => {
   const { currentProject } = useContext(Context);
   const [awsCredentials, setAWSCredentials] = useState<AWSCredential[]>(null);
@@ -50,6 +52,9 @@ const CredentialsForm: React.FC<Props> = ({
   return (
     <StyledCredentialsForm>
       <Heading isAtTop>
+        <BackButton onClick={goBack}>
+          <i className="material-icons">keyboard_backspace</i>
+        </BackButton>
         AWS credentials
       </Heading>
       <Helper>
@@ -64,7 +69,7 @@ const CredentialsForm: React.FC<Props> = ({
               awsCredentials.map((cred: AWSCredential, i: number) => {
                 return (
                   <Credential key={cred.id} isLast={awsCredentials.length - 1 === i}>
-                    {cred.aws_arn || "n/a"}
+                    <Name>{cred.aws_arn || "n/a"}</Name>
                   </Credential>
                 )
               })
@@ -78,17 +83,46 @@ const CredentialsForm: React.FC<Props> = ({
 
 export default CredentialsForm;
 
+const BackButton = styled.div`
+  width: 30px;
+  height: 30px;
+  margin-left: -5px;
+  margin-right: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1;
+  border-radius: 50%;
+  right: 10px;
+  top: 10px;
+  cursor: pointer;
+  :hover {
+    background-color: #ffffff11;
+  }
+
+  > i {
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;
+
+const Name = styled.div`
+  font-size: 14px;
+  font-weight: 500;
+`;
+
 const Credential = styled.div<{ isLast?: boolean}>`
   height: 50px;
   display: flex;
   align-items: center;
   padding: 20px;
-  border-bottom: ${props => props.isLast ? "" : "1px solid #aaaabb"};
+  border-bottom: ${props => props.isLast ? "" : "1px solid #7a7b80"};
+  background: #ffffff11;
 `;
 
 const CredentialList = styled.div`
   width: 100%;
-  border: 1px solid #aaaabb;
+  border: 1px solid #7a7b80;
   border-radius: 5px;
 `;
 

+ 5 - 1
dashboard/src/components/ProvisionerFlow.tsx

@@ -53,7 +53,11 @@ const ProvisionerFlow: React.FC<Props> = ({
       </StyledProvisionerFlow>
     );
   } else if (currentStep === "credentials") {
-    return <CredentialsForm />;
+    return (
+      <CredentialsForm 
+        goBack={() => setCurrentStep("cloud")}
+      />
+    );
   }
 };
 

+ 69 - 2
dashboard/src/main/home/dashboard/Dashboard.tsx

@@ -7,11 +7,17 @@ import { InfraType } from "shared/types";
 import api from "shared/api";
 
 import { RouteComponentProps, withRouter } from "react-router";
+
+import ProvisionerSettings from "../provisioner/ProvisionerSettings";
+import ClusterPlaceholderContainer from "./ClusterPlaceholderContainer";
+import TabRegion from "components/TabRegion";
 import FormDebugger from "components/porter-form/FormDebugger";
 import TitleSection from "components/TitleSection";
 import ClusterSection from "./ClusterSection";
+import { StatusPage } from "../onboarding/steps/ProvisionResources/forms/StatusPage";
+import Banner from "components/Banner";
 
-import { pushFiltered } from "shared/routing";
+import { pushFiltered, pushQueryParams } from "shared/routing";
 import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
 
 type PropsType = RouteComponentProps &
@@ -87,14 +93,63 @@ class Dashboard extends Component<PropsType, StateType> {
     }
   }
 
+  currentTab = () => new URLSearchParams(this.props.location.search).get("tab");
+
+  renderTabContents = () => {
+    if (this.currentTab() === "provisioner") {
+      return (
+        <StatusPage
+          filter={[]}
+          project_id={this.props.projectId}
+          setInfraStatus={() => null}
+        />
+      );
+    } else if (this.currentTab() === "create-cluster") {
+      let helperText = "Create a cluster to link to this project";
+      let helperType = "info";
+      if (
+        true
+      ) {
+        helperText =
+          "You need to update your billing to provision or connect a new cluster";
+        helperType = "warning";
+      }
+      return (
+        <>
+          <Banner type={helperType} noMargin>
+            {helperText}
+          </Banner>
+          <Br />
+          <ProvisionerSettings infras={this.state.infras} provisioner={true} />
+        </>
+      );
+    } else {
+      return <ClusterPlaceholderContainer />;
+    }
+  };
+
   onShowProjectSettings = () => {
     pushFiltered(this.props, "/project-settings", ["project_id"]);
   };
 
+  setCurrentTab = (x: string) => pushQueryParams(this.props, { tab: x });
+
   render() {
     let { currentProject, capabilities } = this.context;
     let { onShowProjectSettings } = this;
 
+    let tabOptions = [{ label: "Connected clusters", value: "overview" }];
+
+    if (this.props.isAuthorized("cluster", "", ["get", "create"])) {
+      tabOptions.push({ label: "Create a cluster", value: "create-cluster" });
+    }
+
+    tabOptions.push({ label: "Provisioner status", value: "provisioner" });
+
+    if (!capabilities?.provisioner) {
+      tabOptions = [{ label: "Project overview", value: "overview" }];
+    }
+
     return (
       <>
         {currentProject && (
@@ -137,7 +192,19 @@ class Dashboard extends Component<PropsType, StateType> {
                     .
                   </Description>
                 </InfoSection>
-                <ClusterSection />
+                {
+                  currentProject.capi_provisioner_enabled ? (
+                    <ClusterSection />
+                  ) : (
+                    <TabRegion
+                      currentTab={this.currentTab()}
+                      setCurrentTab={this.setCurrentTab}
+                      options={tabOptions}
+                    >
+                      {this.renderTabContents()}
+                    </TabRegion>
+                  )
+                }
               </>
             )}
           </DashboardWrapper>

+ 4 - 0
ee/api/server/handlers/billing/webhook.go

@@ -107,6 +107,10 @@ func (c *BillingWebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 		project.PreviewEnvsEnabled = previewEnvsEnabled
 	}
 
+	if capiProvisionerEnabled, err := strconv.ParseBool(features.CAPIProvisionerEnabled); err == nil {
+		project.CAPIProvisionerEnabled = capiProvisionerEnabled
+	}
+
 	_, err = c.Repo().Project().UpdateProject(project)
 
 	if err != nil {

+ 1 - 0
ee/billing/client.go

@@ -289,5 +289,6 @@ func (c *Client) ParseProjectUsageFromWebhook(payload []byte) (*cemodels.Project
 			ManagedInfraEnabled:        usageData.ManagedInfraEnabled,
 			StacksEnabled:              usageData.StacksEnabled,
 			ManagedDatabasesEnabled:    usageData.ManagedDatabasesEnabled,
+			CAPIProvisionerEnabled:     usageData.CAPIProvisionerEnabled,
 		}, nil
 }

+ 1 - 0
ee/billing/types.go

@@ -27,6 +27,7 @@ type APIWebhookRequest struct {
 	ManagedInfraEnabled        string `json:"managed_infra_enabled,omitempty"`
 	StacksEnabled              string `json:"stacks_enabled,omitempty"`
 	ManagedDatabasesEnabled    string `json:"managed_databases_enabled,omitempty"`
+	CAPIProvisionerEnabled     string `json:"capi_provisioner_enabled,omitempty"`
 }
 
 type CreateBillingCookieRequest struct {

+ 15 - 13
internal/models/project.go

@@ -58,11 +58,12 @@ type Project struct {
 	AzureIntegrations  []ints.AzureIntegration  `json:"azure_integrations"`
 	GitlabIntegrations []ints.GitlabIntegration `json:"gitlab_integrations"`
 
-	PreviewEnvsEnabled  bool
-	RDSDatabasesEnabled bool
-	ManagedInfraEnabled bool
-	StacksEnabled       bool
-	APITokensEnabled    bool
+	PreviewEnvsEnabled     bool
+	RDSDatabasesEnabled    bool
+	ManagedInfraEnabled    bool
+	StacksEnabled          bool
+	APITokensEnabled       bool
+	CAPIProvisionerEnabled bool
 }
 
 // ToProjectType generates an external types.Project to be shared over REST
@@ -74,13 +75,14 @@ func (p *Project) ToProjectType() *types.Project {
 	}
 
 	return &types.Project{
-		ID:                  p.ID,
-		Name:                p.Name,
-		Roles:               roles,
-		PreviewEnvsEnabled:  p.PreviewEnvsEnabled,
-		RDSDatabasesEnabled: p.RDSDatabasesEnabled,
-		ManagedInfraEnabled: p.ManagedInfraEnabled,
-		StacksEnabled:       p.StacksEnabled,
-		APITokensEnabled:    p.APITokensEnabled,
+		ID:                     p.ID,
+		Name:                   p.Name,
+		Roles:                  roles,
+		PreviewEnvsEnabled:     p.PreviewEnvsEnabled,
+		RDSDatabasesEnabled:    p.RDSDatabasesEnabled,
+		ManagedInfraEnabled:    p.ManagedInfraEnabled,
+		StacksEnabled:          p.StacksEnabled,
+		APITokensEnabled:       p.APITokensEnabled,
+		CAPIProvisionerEnabled: p.CAPIProvisionerEnabled,
 	}
 }