Просмотр исходного кода

Merge pull request #76 from porter-dev/staging

Staging
abelanger5 5 лет назад
Родитель
Сommit
ce250c48fa

+ 35 - 0
.darwin.goreleaser.yml

@@ -0,0 +1,35 @@
+before:
+  hooks:
+    - go mod download
+builds:
+  - id: "porter-cli"
+    binary: porter
+    env:
+      - CGO_ENABLED=1
+    dir: cli
+    main: ./main.go
+    goos:
+      - darwin
+    goarch:
+      - amd64
+    flags:
+      - -tags=cli
+    hooks:
+      post: gon gon.hcl
+archives:
+  - replacements:
+      darwin: Darwin
+      linux: Linux
+      windows: Windows
+      386: i386
+      amd64: x86_64
+checksum:
+  name_template: 'checksums.txt'
+snapshot:
+  name_template: "{{ .Tag }}-next"
+changelog:
+  sort: asc
+  filters:
+    exclude:
+      - '^docs:'
+      - '^test:'

+ 2 - 1
.gitignore

@@ -3,4 +3,5 @@
 app
 *.db
 test.yaml
-dist
+dist
+gon.hcl

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Porter Technologies Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 2 - 2
README.md

@@ -1,10 +1,10 @@
 # Porter
-
 Porter is a **dashboard for Helm** with support for the following features:
 - Visualization of all Helm releases with filtering by namespace
 - In-depth view of releases, including revision histories and component graphs
 - Rollback/update of existing releases, including editing of `values.yaml`
 
+![Graph View](https://user-images.githubusercontent.com/65516095/96605367-221abe00-12c4-11eb-8915-25e70fe7929a.png)
 **What's next for Porter?** View our [roadmap](https://github.com/porter-dev/porter/projects/1), or read our [mission statement](#mission-statement). 
 
 ## Quick Start
@@ -44,4 +44,4 @@ More specifically, we have the following long-term goals:
 - **Improve the development experience for packaging and releasing Kubernetes applications**
 - **Increase interoperability of Kubernetes tooling without compromising usability**
 
-Why did we begin with Helm? Helm is the most popular auxiliary Kubernetes tool, and can function in nearly all parts of deployment lifecycle. We think of the various features of Helm in the following manner, adapted from [Brian Grant's Helm Summit talk](https://www.youtube.com/watch?v=F-TlC8nIz8s) (slides [here](https://docs.google.com/presentation/d/10dp4hKciccincnH6pAFf7t31s82iNvtt_mwhlUbeCDw/edit#slide=id.g32690131a8_0_5)): package management, dependency management, application metadata, parameterization, templating, deployment/config revision management, lifecycle management hooks, and application probes. Along with these fundamental features, an expanding number of [command plugins](https://helm.sh/docs/community/related/#helm-plugins) for more specific use-cases have started to become popular in the Helm ecosystem. If we can build a better workflow for both application developers and application operators by improving the user experience for most of these Helm features, we can generalize and expand this workflow to support alternative tooling that exists in the [Kubernetes application management ecosystem](https://docs.google.com/spreadsheets/d/1FCgqz1Ci7_VCz_wdh8vBitZ3giBtac_H8SBw4uxnrsE/edit#gid=0). 
+Why did we begin with Helm? Helm is the most popular auxiliary Kubernetes tool, and can function in nearly all parts of deployment lifecycle. We think of the various features of Helm in the following manner, adapted from [Brian Grant's Helm Summit talk](https://www.youtube.com/watch?v=F-TlC8nIz8s) (slides [here](https://docs.google.com/presentation/d/10dp4hKciccincnH6pAFf7t31s82iNvtt_mwhlUbeCDw/edit#slide=id.g32690131a8_0_5)): package management, dependency management, application metadata, parameterization, templating, deployment/config revision management, lifecycle management hooks, and application probes. Along with these fundamental features, an expanding number of [command plugins](https://helm.sh/docs/community/related/#helm-plugins) for more specific use-cases have started to become popular in the Helm ecosystem. If we can build a better workflow for both application developers and application operators by improving the user experience for most of these Helm features, we can generalize and expand this workflow to support alternative tooling that exists in the [Kubernetes application management ecosystem](https://docs.google.com/spreadsheets/d/1FCgqz1Ci7_VCz_wdh8vBitZ3giBtac_H8SBw4uxnrsE/edit#gid=0). 

+ 2 - 2
dashboard/src/main/Login.tsx

@@ -43,7 +43,7 @@ export default class Login extends Component<PropsType, StateType> {
   handleLogin = (): void => {
     let { email, password } = this.state;
     let { authenticate } = this.props;
-    let { setCurrentError, setUserId } = this.context;
+    let { setCurrentError, setUser } = this.context;
 
     // Check for valid input
     if (!emailRegex.test(email)) {
@@ -55,7 +55,7 @@ export default class Login extends Component<PropsType, StateType> {
         password: password
       }, {}, (err: any, res: any) => {
         // TODO: case and set credential error
-        setUserId(res?.data?.id)
+        setUser(res?.data?.id, res?.data?.email)
         err ? setCurrentError(err.response.data.errors[0]) : authenticate();
       });
     }

+ 2 - 2
dashboard/src/main/Main.tsx

@@ -30,14 +30,14 @@ export default class Main extends Component<PropsType, StateType> {
   }
 
   componentDidMount() {
-    let { setUserId } = this.context;
+    let { setUser } = this.context;
     api.checkAuth('', {}, {}, (err: any, res: any) => {      
       if (err && err.response.status == 403) {
         this.setState({ isLoggedIn: false, loading: false })
       }
 
       if (res && res.data) {
-        setUserId(res.data.id);
+        setUser(res?.data?.id, res?.data?.email);
         this.setState({ isLoggedIn: true, initialized: true, loading: false });
       } else {
         this.setState({ isLoggedIn: false, loading: false })

+ 2 - 2
dashboard/src/main/Register.tsx

@@ -42,7 +42,7 @@ export default class Register extends Component<PropsType, StateType> {
   handleRegister = (): void => {
     let { email, password, confirmPassword } = this.state;
     let { authenticate } = this.props;
-    let { setCurrentError, setUserId } = this.context;
+    let { setCurrentError, setUser } = this.context;
 
     if (!emailRegex.test(email)) {
       this.setState({ emailError: true });
@@ -60,7 +60,7 @@ export default class Register extends Component<PropsType, StateType> {
         email: email,
         password: password
       }, {}, (err: any, res: any) => {
-        setUserId(res?.data?.id)
+        setUser(res?.data?.id, res?.data?.email)
         err ? setCurrentError(err.response.data.errors[0]) : authenticate();
       });
     } 

+ 8 - 8
dashboard/src/main/home/modals/ClusterConfigModal.tsx

@@ -36,10 +36,10 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
   };
   
   updateChecklist = () => {
-    let { setCurrentError, userId } = this.context;
+    let { setCurrentError, user } = this.context;
 
     // Parse kubeconfig to retrieve all possible clusters
-    api.getContexts('<token>', {}, { id: userId }, (err: any, res: any) => {
+    api.getContexts('<token>', {}, { id: user.userId }, (err: any, res: any) => {
       if (err) {
         // setCurrentError(JSON.stringify(err));
       } else {
@@ -49,13 +49,13 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
   }
 
   componentDidMount() {
-    let { setCurrentError, userId, currentModalData } = this.context;
+    let { setCurrentError, user, currentModalData } = this.context;
 
     if (currentModalData && currentModalData.currentTab) {
       this.setState({ currentTab: 'select' });
     }
 
-    api.getUser('<token>', {}, { id: userId }, (err: any, res: any) => {
+    api.getUser('<token>', {}, { id: user.userId }, (err: any, res: any) => {
       if (err) {
         // setCurrentError(JSON.stringify(err));
       } else if (res.data.rawKubeConfig !== '') {
@@ -101,13 +101,13 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
 
   handleSaveKubeconfig = () => {
     let { rawKubeconfig } = this.state;
-    let { userId } = this.context;
+    let { user } = this.context;
 
     this.setState({ saveKubeconfigStatus: 'loading' });
     api.updateUser(
       '<token>',
       { rawKubeConfig: rawKubeconfig },
-      { id: userId },
+      { id: user.userId },
       (err: any, res: any) => {
         if (err) {
           this.setState({ saveKubeconfigStatus: 'error' });
@@ -126,7 +126,7 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
 
   handleSaveSelected = () => {
     let { kubeContexts } = this.state;
-    let { userId } = this.context;
+    let { user } = this.context;
 
     this.setState({ saveSelectedStatus: 'loading' });
     let allowedContexts: string[] = [];
@@ -139,7 +139,7 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
     api.updateUser(
       '<token>',
       { allowedContexts },
-      { id: userId },
+      { id: user.userId },
       (err: any, res: any) => {
         if (err) {
           this.setState({ saveSelectedStatus: 'error' });

+ 2 - 2
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -30,10 +30,10 @@ export default class ClusterSection extends Component<PropsType, StateType> {
   };
 
   updateClusters = () => {
-    let { setCurrentError, userId, setCurrentCluster } = this.context;
+    let { setCurrentError, user, setCurrentCluster } = this.context;
 
     // TODO: query with selected filter once implemented
-    api.getContexts('<token>', {}, { id: userId }, (err: any, res: any) => {
+    api.getContexts('<token>', {}, { id: user.userId }, (err: any, res: any) => {
       if (err) {
 
         // Assume intializing if no contexts

+ 1 - 1
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -117,7 +117,7 @@ export default class Sidebar extends Component<PropsType, StateType> {
             <RingWrapper>
               <UserIcon src={gradient} />
             </RingWrapper>
-            <UserName>bob_ross</UserName>
+            <UserName>{this.context.user.email}</UserName>
           </UserSection>
 
           <SidebarLabel>Current Cluster</SidebarLabel>

+ 3 - 3
dashboard/src/shared/Context.tsx

@@ -40,9 +40,9 @@ class ContextProvider extends Component {
     setCurrentCluster: (currentCluster: string): void => {
       this.setState({ currentCluster });
     },
-    userId: null as number | null,
-    setUserId: (userId: number): void => {
-      this.setState({ userId });
+    user: null as any,
+    setUser: (userId: number, email: string): void => {
+      this.setState({ user: {userId, email} });
     },
     devOpsMode: true,
     setDevOpsMode: (devOpsMode: boolean): void => {

+ 16 - 0
scripts/release.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Step 0 -- ensure that:
+# (1) GITHUB_TOKEN exists as an env variable
+# (2) Apple ID password exists in keychain
+
+# Step 1 -- build for linux/windows inside a docker container
+docker run --rm --privileged \
+-v $PWD:/go/src/github.com/porter-dev/porter \
+-v /var/run/docker.sock:/var/run/docker.sock \
+-w /go/src/github.com/porter-dev/porter \
+-e GORELEASER_GITHUB_TOKEN='$GITHUB_TOKEN' \
+mailchain/goreleaser-xcgo "--rm-dist"
+
+# Step 2 -- build for MacOS using notarization tool
+goreleaser --config .darwin.goreleaser.yml

+ 11 - 6
server/api/user_handler.go

@@ -48,11 +48,12 @@ func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
 		app.logger.Info().Msgf("New user created: %d", user.ID)
 		session.Values["authenticated"] = true
 		session.Values["user_id"] = user.ID
+		session.Values["email"] = user.Email
 		session.Save(r, w)
 
 		w.WriteHeader(http.StatusCreated)
 
-		if err := app.sendUserID(w, user.ID); err != nil {
+		if err := app.sendUser(w, user.ID, user.Email); err != nil {
 			app.handleErrorFormDecoding(err, ErrUserDecode, w)
 			return
 		}
@@ -65,13 +66,14 @@ func (app *App) HandleAuthCheck(w http.ResponseWriter, r *http.Request) {
 
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
 	}
 
 	userID, _ := session.Values["user_id"].(uint)
-
+	email, _ := session.Values["email"].(string)
 	w.WriteHeader(http.StatusOK)
 
-	if err := app.sendUserID(w, userID); err != nil {
+	if err := app.sendUser(w, userID, email); err != nil {
 		app.handleErrorFormDecoding(err, ErrUserDecode, w)
 		return
 	}
@@ -116,13 +118,14 @@ func (app *App) HandleLoginUser(w http.ResponseWriter, r *http.Request) {
 	// Set user as authenticated
 	session.Values["authenticated"] = true
 	session.Values["user_id"] = storedUser.ID
+	session.Values["email"] = storedUser.Email
 	if err := session.Save(r, w); err != nil {
 		app.logger.Warn().Err(err)
 	}
 
 	w.WriteHeader(http.StatusOK)
 
-	if err := app.sendUserID(w, storedUser.ID); err != nil {
+	if err := app.sendUser(w, storedUser.ID, storedUser.Email); err != nil {
 		app.handleErrorFormDecoding(err, ErrUserDecode, w)
 		return
 	}
@@ -138,6 +141,7 @@ func (app *App) HandleLogoutUser(w http.ResponseWriter, r *http.Request) {
 
 	session.Values["authenticated"] = false
 	session.Values["user_id"] = nil
+	session.Values["email"] = nil
 	session.Save(r, w)
 	w.WriteHeader(http.StatusOK)
 }
@@ -340,9 +344,10 @@ func doesUserExist(repo *repository.Repository, user *models.User) *HTTPError {
 	return nil
 }
 
-func (app *App) sendUserID(w http.ResponseWriter, userID uint) error {
+func (app *App) sendUser(w http.ResponseWriter, userID uint, email string) error {
 	resUser := &models.UserExternal{
-		ID: userID,
+		ID:    userID,
+		Email: email,
 	}
 	if err := json.NewEncoder(w).Encode(resUser); err != nil {
 		return err