Ver Fonte

Merge pull request #600 from porter-dev/0.3.0-local-version

[0.2.0] local version
abelanger5 há 5 anos atrás
pai
commit
26674b7f2f

+ 1 - 1
.github/workflows/release.yaml

@@ -84,7 +84,7 @@ jobs:
         run: |
           go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd.Version=${{steps.tag_name.outputs.tag}}'" -a -tags cli -o ./porter ./cli &
           go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -o ./docker-credential-porter ./cmd/docker-credential-porter/ &
-          go build -ldflags="-w -s" -a -o ./portersvr ./cmd/app/ &
+          go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -o ./portersvr ./cmd/app/ &
           wait
         env:
           GOOS: linux

+ 2 - 0
cli/cmd/server.go

@@ -208,6 +208,8 @@ func startLocal(
 		"SQL_LITE_PATH=" + sqlLitePath,
 		"STATIC_FILE_PATH=" + staticFilePath,
 		"REDIS_ENABLED=false",
+		"GITHUB_ENABLED=false",
+		"PROVISIONER_ENABLED=false",
 	}...)
 
 	cmdPorter.Stdout = os.Stdout

+ 1 - 1
cli/cmd/version.go

@@ -7,7 +7,7 @@ import (
 )
 
 // Version will be linked by an ldflag during build
-var Version string = "v0.1.0-beta.3.4"
+var Version string = "0.2.0"
 
 var versionCmd = &cobra.Command{
 	Use:     "version",

+ 1 - 0
cmd/app/main.go

@@ -107,6 +107,7 @@ func main() {
 		Repository: repo,
 		ServerConf: appConf.Server,
 		RedisConf:  &appConf.Redis,
+		CapConf: 	appConf.Capabilities,
 		DBConf:     appConf.Db,
 	})
 

+ 10 - 1
dashboard/src/main/Main.tsx

@@ -22,6 +22,7 @@ type StateType = {
   isLoggedIn: boolean;
   isEmailVerified: boolean;
   initialized: boolean;
+  local: boolean;
 };
 
 export default class Main extends Component<PropsType, StateType> {
@@ -30,6 +31,7 @@ export default class Main extends Component<PropsType, StateType> {
     isLoggedIn: false,
     isEmailVerified: false,
     initialized: localStorage.getItem("init") === "true",
+    local: false,
   };
 
   componentDidMount() {
@@ -53,6 +55,13 @@ export default class Main extends Component<PropsType, StateType> {
         }
       })
       .catch((err) => this.setState({ isLoggedIn: false, loading: false }));
+    
+    api.getCapabilities("", {}, {})
+    .then((res) => {
+      console.log(res.data)
+      this.setState({local: !res.data?.provisioner})
+    })
+    .catch((err) => console.log(err));
   }
 
   initialize = () => {
@@ -100,7 +109,7 @@ export default class Main extends Component<PropsType, StateType> {
     }
 
     // if logged in but not verified, block until email verification
-    if (this.state.isLoggedIn && !this.state.isEmailVerified) {
+    if (!this.state.local && this.state.isLoggedIn && !this.state.isEmailVerified) {
       return (
         <Switch>
           <Route

+ 29 - 10
dashboard/src/main/auth/Login.tsx

@@ -16,6 +16,7 @@ type StateType = {
   password: string;
   emailError: boolean;
   credentialError: boolean;
+  hasGithub: boolean;
 };
 
 export default class Login extends Component<PropsType, StateType> {
@@ -24,6 +25,7 @@ export default class Login extends Component<PropsType, StateType> {
     password: "",
     emailError: false,
     credentialError: false,
+    hasGithub: true,
   };
 
   handleKeyDown = (e: any) => {
@@ -36,6 +38,13 @@ export default class Login extends Component<PropsType, StateType> {
     emailFromCLI
       ? this.setState({ email: emailFromCLI })
       : document.addEventListener("keydown", this.handleKeyDown);
+
+    // get capabilities to case on github
+    api.getCapabilities("", {}, {})
+    .then((res) => {
+      this.setState({hasGithub: res.data?.github})
+    })
+    .catch((err) => console.log(err));
   }
 
   componentWillUnmount() {
@@ -105,6 +114,25 @@ export default class Login extends Component<PropsType, StateType> {
     window.location.href = redirectUrl;
   };
 
+  renderGithubSection = () => {
+    if (this.state.hasGithub) {
+      return (
+        <>
+          <OAuthButton onClick={this.githubRedirect}>
+          <IconWrapper>
+            <Icon src={github} />
+            Log in with GitHub
+          </IconWrapper>
+          </OAuthButton>
+          <OrWrapper>
+            <Line />
+            <Or>or</Or>
+          </OrWrapper>
+        </>
+      )
+    }
+  }
+
   render() {
     let { email, password, credentialError, emailError } = this.state;
 
@@ -117,16 +145,7 @@ export default class Login extends Component<PropsType, StateType> {
           <FormWrapper>
             <Logo src={logo} />
             <Prompt>Log in to Porter</Prompt>
-            <OAuthButton onClick={this.githubRedirect}>
-              <IconWrapper>
-                <Icon src={github} />
-                Log in with GitHub
-              </IconWrapper>
-            </OAuthButton>
-            <OrWrapper>
-              <Line />
-              <Or>or</Or>
-            </OrWrapper>
+            {this.renderGithubSection()}
             <DarkMatter />
             <InputWrapper>
               <Input

+ 30 - 10
dashboard/src/main/auth/Register.tsx

@@ -17,6 +17,7 @@ type StateType = {
   confirmPassword: string;
   emailError: boolean;
   confirmPasswordError: boolean;
+  hasGithub: boolean;
 };
 
 export default class Register extends Component<PropsType, StateType> {
@@ -26,6 +27,7 @@ export default class Register extends Component<PropsType, StateType> {
     confirmPassword: "",
     emailError: false,
     confirmPasswordError: false,
+    hasGithub: true,
   };
 
   handleKeyDown = (e: any) => {
@@ -34,6 +36,13 @@ export default class Register extends Component<PropsType, StateType> {
 
   componentDidMount() {
     document.addEventListener("keydown", this.handleKeyDown);
+
+    // get capabilities to case on github
+    api.getCapabilities("", {}, {})
+    .then((res) => {      
+      this.setState({hasGithub: res.data?.github})
+    })
+    .catch((err) => console.log(err));
   }
 
   componentWillUnmount() {
@@ -106,6 +115,26 @@ export default class Register extends Component<PropsType, StateType> {
     }
   };
 
+  renderGithubSection = () => {
+    if (this.state.hasGithub) {
+      return (
+        <>
+          <OAuthButton onClick={this.githubRedirect}>
+              <IconWrapper>
+                <Icon src={github} />
+                Sign up with GitHub
+              </IconWrapper>
+            </OAuthButton>
+            <OrWrapper>
+              <Line />
+              <Or>or</Or>
+            </OrWrapper>
+        </>
+      )
+    }
+  }
+
+
   render() {
     let {
       email,
@@ -124,16 +153,7 @@ export default class Register extends Component<PropsType, StateType> {
           <FormWrapper>
             <Logo src={logo} />
             <Prompt>Sign up for Porter</Prompt>
-            <OAuthButton onClick={this.githubRedirect}>
-              <IconWrapper>
-                <Icon src={github} />
-                Sign up with GitHub
-              </IconWrapper>
-            </OAuthButton>
-            <OrWrapper>
-              <Line />
-              <Or>or</Or>
-            </OrWrapper>
+            {this.renderGithubSection()}
             <DarkMatter />
             <InputWrapper>
               <Input

+ 21 - 0
dashboard/src/main/home/Home.tsx

@@ -82,6 +82,25 @@ class Home extends Component<PropsType, StateType> {
       });
   };
 
+  getCapabilities = () => {
+    let { currentProject } = this.props;
+    if (!currentProject) return;
+
+    api
+      .getCapabilities(
+        "<token>",
+        {},
+        {}
+      )
+      .then((res) => {
+        console.log(res.data)
+        this.context.setCapabilities(res.data)
+      })
+      .catch((err) => {
+        console.log(err)
+      });
+  }
+
   getProjects = (id?: number) => {
     let { user, setProjects } = this.context;
     let { currentProject } = this.props;
@@ -222,6 +241,7 @@ class Home extends Component<PropsType, StateType> {
     this.setState({ ghRedirect: urlParams.get("gh_oauth") !== null });
     urlParams.delete("gh_oauth");
     this.getProjects(defaultProjectId);
+    this.getCapabilities();
   }
 
   // TODO: Need to handle the following cases. Do a deep rearchitecture (Prov -> Dashboard?) if need be:
@@ -237,6 +257,7 @@ class Home extends Component<PropsType, StateType> {
         this.checkDO();
       } else {
         this.initializeView();
+        this.getCapabilities();
       }
     }
   }

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

@@ -82,7 +82,7 @@ class Dashboard extends Component<PropsType, StateType> {
             <i className="material-icons">info</i>
             Create a cluster to link to this project.
           </Banner>
-          <ProvisionerSettings infras={this.state.infras} />
+          <ProvisionerSettings infras={this.state.infras} provisioner={true} />
         </>
       );
     } else {
@@ -95,8 +95,21 @@ class Dashboard extends Component<PropsType, StateType> {
   };
 
   render() {
-    let { currentProject } = this.context;
+    let { currentProject, capabilities } = this.context;
     let { onShowProjectSettings } = this;
+
+    let tabOptions = [
+      { label: "Project Overview", value: "overview" },
+      { label: "Create a Cluster", value: "create-cluster" },
+      { label: "Provisioner Status", value: "provisioner" },
+    ]
+    
+    if (!capabilities?.provisioner) {
+      tabOptions = [
+        { label: "Project Overview", value: "overview" },
+      ]
+    }
+
     return (
       <>
         {currentProject && (

+ 4 - 2
dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx

@@ -581,10 +581,12 @@ class LaunchTemplate extends Component<PropsType, StateType> {
 
   // Display if current template uses source (image or repo)
   renderSourceSelectorContent = () => {
+    let { capabilities } = this.context;
+
     if (this.state.sourceType === "") {
       return (
         <BlockList>
-          <Block
+          {capabilities.github && (<Block
             onClick={() => {
               this.setState({ sourceType: "repo" });
             }}
@@ -594,7 +596,7 @@ class LaunchTemplate extends Component<PropsType, StateType> {
             <BlockDescription>
               Deploy using source from a Git repo.
             </BlockDescription>
-          </Block>
+          </Block>)}
           <Block
             onClick={() => {
               this.setState({ sourceType: "registry" });

+ 9 - 1
dashboard/src/main/home/navbar/Navbar.tsx

@@ -40,10 +40,18 @@ export default class Navbar extends Component<PropsType, StateType> {
     }
   };
 
+  renderFeedbackButton = () => {
+    if (this.context?.capabilities?.provisioner) {
+      return (
+        <Feedback currentView={this.props.currentView} />
+      )
+    }
+  }
+
   render() {
     return (
       <StyledNavbar>
-        <Feedback currentView={this.props.currentView} />
+        {this.renderFeedbackButton()}
         <NavButton
           selected={this.state.showDropdown}
           onClick={() =>

+ 2 - 1
dashboard/src/main/home/new-project/NewProject.tsx

@@ -23,6 +23,7 @@ export default class NewProject extends Component<PropsType, StateType> {
   };
 
   render() {
+    let { capabilities } = this.context;
     let { projectName } = this.state;
     return (
       <StyledNewProject>
@@ -58,7 +59,7 @@ export default class NewProject extends Component<PropsType, StateType> {
             width="470px"
           />
         </InputWrapper>
-        <ProvisionerSettings isInNewProject={true} projectName={projectName} />
+        <ProvisionerSettings isInNewProject={true} projectName={projectName} provisioner={capabilities?.provisioner} />
         <Br />
       </StyledNewProject>
     );

+ 81 - 29
dashboard/src/main/home/provisioner/ProvisionerSettings.tsx

@@ -17,6 +17,7 @@ type PropsType = RouteComponentProps & {
   isInNewProject?: boolean;
   projectName?: string;
   infras?: InfraType[];
+  provisioner?: boolean;
 };
 
 type StateType = {
@@ -42,11 +43,20 @@ class NewProject extends Component<PropsType, StateType> {
     this.props.history.push("dashboard?tab=overview");
   };
 
-  renderSelectedProvider = () => {
+  renderSelectedProvider = (override?: string) => {
     let { selectedProvider } = this.state;
     let { projectName, infras } = this.props;
 
+    if (override) {
+      selectedProvider = override;
+    }
+
     let renderSkipHelper = () => {
+
+      if (!this.props.provisioner) {
+        return;
+      }
+
       return (
         <>
           {selectedProvider === "skipped" ? (
@@ -125,19 +135,80 @@ class NewProject extends Component<PropsType, StateType> {
     }
   };
 
-  render() {
+  renderFooter = () => {
     let { selectedProvider } = this.state;
     let { isInNewProject } = this.props;
+    let { provisioner } = this.props;
+    let helper = provisioner ? "Note: Provisioning can take up to 15 minutes" : ""
+
+    if (isInNewProject && !selectedProvider) {
+      return (
+        <>
+          <Helper>
+            Already have a Kubernetes cluster?
+            <Highlight
+              onClick={() => this.setState({ selectedProvider: "skipped" })}
+            >
+              Skip
+            </Highlight>
+          </Helper>
+          <Br />
+          <SaveButton
+            text="Submit"
+            disabled={true}
+            onClick={() => {}}
+            makeFlush={true}
+            helper={helper}
+          />
+        </>
+      )
+    }
+  }
+
+  componentDidMount() {
+    let { provisioner } = this.props;
+
+    if (!provisioner) {
+      this.setState({selectedProvider: "skipped"})
+    }
+  }
+
+  componentDidUpdate(prevProps: PropsType) {
+    if (
+      prevProps.provisioner !== this.props.provisioner
+    ) {
+      if (!this.props.provisioner) {
+        this.setState({selectedProvider: "skipped"})
+      }
+    }
+  }
+
+  renderHelperText = () => {
+    let { isInNewProject, provisioner } = this.props;
+    if (!provisioner) {
+      return;
+    }
+
+    if (isInNewProject) {
+      return (
+        <>
+          Select your hosting backend:<Required>*</Required>
+        </>
+      )
+    } else {
+      return (
+        "Need a cluster? Provision through Porter:"
+      )
+    }
+  }
+
+  render() {
+    let { selectedProvider } = this.state;
+
     return (
       <StyledProvisionerSettings>
         <Helper>
-          {isInNewProject ? (
-            <>
-              Select your hosting backend:<Required>*</Required>
-            </>
-          ) : (
-            "Need a cluster? Provision through Porter:"
-          )}
+          {this.renderHelperText()}
         </Helper>
         {!selectedProvider ? (
           <BlockList>
@@ -160,26 +231,7 @@ class NewProject extends Component<PropsType, StateType> {
         ) : (
           <>{this.renderSelectedProvider()}</>
         )}
-        {isInNewProject && !selectedProvider && (
-          <>
-            <Helper>
-              Already have a Kubernetes cluster?
-              <Highlight
-                onClick={() => this.setState({ selectedProvider: "skipped" })}
-              >
-                Skip
-              </Highlight>
-            </Helper>
-            <Br />
-            <SaveButton
-              text="Submit"
-              disabled={true}
-              onClick={() => {}}
-              makeFlush={true}
-              helper="Note: Provisioning can take up to 15 minutes"
-            />
-          </>
-        )}
+        {this.renderFooter()}
       </StyledProvisionerSettings>
     );
   }

+ 5 - 1
dashboard/src/shared/Context.tsx

@@ -1,6 +1,6 @@
 import React, { Component } from "react";
 
-import { ProjectType, ClusterType } from "shared/types";
+import { ProjectType, ClusterType, CapabilityType } from "shared/types";
 
 const Context = React.createContext({});
 
@@ -63,6 +63,10 @@ class ContextProvider extends Component {
     setDevOpsMode: (devOpsMode: boolean) => {
       this.setState({ devOpsMode });
     },
+    capabilities: null as CapabilityType,
+    setCapabilities: (capabilities: CapabilityType) => {
+      this.setState({ capabilities })
+    },
     clearContext: () => {
       this.setState({
         currentModal: null,

+ 5 - 0
dashboard/src/shared/api.tsx

@@ -629,6 +629,10 @@ const getUser = baseApi<{}, { id: number }>("GET", (pathParams) => {
   return `/api/users/${pathParams.id}`;
 });
 
+const getCapabilities = baseApi<{}, {}>("GET", () => {
+  return `/api/capabilities`;
+});
+
 const linkGithubProject = baseApi<
   {},
   {
@@ -818,6 +822,7 @@ export default {
   destroyDOKS,
   getBranchContents,
   getBranches,
+  getCapabilities,
   getChart,
   getCharts,
   getChartComponents,

+ 5 - 0
dashboard/src/shared/types.tsx

@@ -167,3 +167,8 @@ export interface ActionConfigType {
   image_repo_uri: string;
   git_repo_id: number;
 }
+
+export interface CapabilityType {
+  github: boolean;
+  provisioner: boolean;
+}

+ 6 - 0
internal/config/config.go

@@ -14,6 +14,7 @@ type Conf struct {
 	Db     DBConf
 	K8s    K8sConf
 	Redis  RedisConf
+	Capabilities CapConf
 }
 
 // ServerConf is the server configuration
@@ -70,6 +71,11 @@ type K8sConf struct {
 	IsTesting bool `env:"K8S_IS_TESTING,default=false"`
 }
 
+type CapConf struct {
+	Provisioner bool `env:"PROVISIONER_ENABLED,default=true"`
+	Github bool `env:"GITHUB_ENABLED,default=true"`
+}
+
 // FromEnv generates a configuration from environment variables
 func FromEnv() *Conf {
 	var c Conf

+ 5 - 0
server/api/api.go

@@ -42,6 +42,7 @@ type AppConfig struct {
 	ServerConf config.ServerConf
 	RedisConf  *config.RedisConf
 	DBConf     config.DBConf
+	CapConf config.CapConf
 
 	// TestAgents if API is in testing mode
 	TestAgents *TestAgents
@@ -71,6 +72,9 @@ type App struct {
 	// config for db
 	DBConf config.DBConf
 
+	// config for capabilities
+	CapConf config.CapConf
+
 	// oauth-specific clients
 	GithubUserConf    *oauth2.Config
 	GithubProjectConf *oauth2.Config
@@ -102,6 +106,7 @@ func New(conf *AppConfig) (*App, error) {
 		ServerConf: conf.ServerConf,
 		RedisConf:  conf.RedisConf,
 		DBConf:     conf.DBConf,
+		CapConf: 	conf.CapConf,
 		TestAgents: conf.TestAgents,
 		db:         conf.DB,
 		validator:  validator,

+ 28 - 0
server/api/capability_handler.go

@@ -0,0 +1,28 @@
+package api
+
+import (
+	"encoding/json"
+	"net/http"
+)
+
+// CapabilitiesExternal represents the Capabilities struct that will be sent over REST
+type CapabilitiesExternal struct {
+	Provisioner bool `json:"provisioner"`
+	GitHub bool	`json:"github"`
+}
+
+// HandleGetCapabilities gets the capabilities of the server
+func (app *App) HandleGetCapabilities(w http.ResponseWriter, r *http.Request) {
+
+	cap := app.CapConf
+
+	capExternal := &CapabilitiesExternal{
+		Provisioner: cap.Provisioner,
+		GitHub: cap.Github,
+	}
+
+	if err := json.NewEncoder(w).Encode(capExternal); err != nil {
+		app.handleErrorFormDecoding(err, ErrK8sDecode, w)
+		return
+	}
+}

+ 7 - 0
server/router/router.go

@@ -1352,6 +1352,13 @@ func New(a *api.App) *chi.Mux {
 				mw.ReadAccess,
 			),
 		)
+
+		// capabilities
+		r.Method(
+			"GET",
+			"/capabilities",
+			http.HandlerFunc(a.HandleGetCapabilities),
+		)
 	})
 
 	staticFilePath := a.ServerConf.StaticFilePath