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

Merge branch 'master' of github.com:porter-dev/porter into por-57-lack-of-information-for-error-boundary

jnfrati 4 лет назад
Родитель
Сommit
086e8c45cd

+ 0 - 2
api/client/api.go

@@ -104,8 +104,6 @@ func (c *Client) postRequest(relPath string, data interface{}, response interfac
 		return nil
 	}
 
-	fmt.Println(string(strData))
-
 	req, err := http.NewRequest(
 		"POST",
 		fmt.Sprintf("%s%s", c.BaseURL, relPath),

+ 19 - 7
api/server/authz/cluster.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"net/http"
+	"strings"
 
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
@@ -103,6 +104,7 @@ func (d *OutOfClusterAgentGetter) GetAgent(r *http.Request, cluster *models.Clus
 
 	// if agent not found in context, get the agent from out of cluster config
 	ooc := d.GetOutOfClusterConfig(cluster)
+	ooc.DefaultNamespace = getNamespaceFromRequest(r)
 
 	agent, err := kubernetes.GetAgentOutOfClusterConfig(ooc)
 
@@ -134,13 +136,7 @@ func (d *OutOfClusterAgentGetter) GetHelmAgent(r *http.Request, cluster *models.
 		return nil, err
 	}
 
-	// look for namespace in context, otherwise go with default
-	reqScopes, _ := r.Context().Value(types.RequestScopeCtxKey).(map[types.PermissionScope]*types.RequestAction)
-	namespace := "default"
-
-	if nsPolicy, ok := reqScopes[types.NamespaceScope]; ok {
-		namespace = nsPolicy.Resource.Name
-	}
+	namespace := getNamespaceFromRequest(r)
 
 	helmAgent, err := helm.GetAgentFromK8sAgent("secret", namespace, d.config.Logger, k8sAgent)
 
@@ -167,3 +163,19 @@ func (d *OutOfClusterAgentGetter) GetDynamicClient(r *http.Request, cluster *mod
 
 	return kubernetes.GetDynamicClientOutOfClusterConfig(d.GetOutOfClusterConfig(cluster))
 }
+
+func getNamespaceFromRequest(r *http.Request) string {
+	// look for namespace in context, otherwise go with default
+	reqScopes, _ := r.Context().Value(types.RequestScopeCtxKey).(map[types.PermissionScope]*types.RequestAction)
+	namespace := "default"
+
+	if nsPolicy, ok := reqScopes[types.NamespaceScope]; ok {
+		namespace = nsPolicy.Resource.Name
+	}
+
+	if strings.ToLower(namespace) == "all" {
+		namespace = ""
+	}
+
+	return namespace
+}

+ 14 - 1
api/server/handlers/release/ugprade.go

@@ -5,6 +5,8 @@ import (
 	"net/http"
 	"net/url"
 
+	semver "github.com/Masterminds/semver/v3"
+
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
@@ -18,6 +20,10 @@ import (
 	"helm.sh/helm/v3/pkg/release"
 )
 
+var (
+	createEnvSecretConstraint, _ = semver.NewConstraint(" < 0.1.0")
+)
+
 type UpgradeReleaseHandler struct {
 	handlers.PorterHandlerReadWriter
 	authz.KubernetesAgentGetter
@@ -191,12 +197,19 @@ func (c *UpgradeReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 					return
 				}
 
-				err = gaRunner.CreateEnvSecret()
+				actionVersion, err := semver.NewVersion(gaRunner.Version)
 
 				if err != nil {
 					c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 					return
 				}
+
+				if createEnvSecretConstraint.Check(actionVersion) {
+					if err := gaRunner.CreateEnvSecret(); err != nil {
+						c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+						return
+					}
+				}
 			}
 		}
 	}

+ 9 - 1
api/server/handlers/release/update_rollback.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"net/http"
 
+	semver "github.com/Masterminds/semver/v3"
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
@@ -91,12 +92,19 @@ func (c *RollbackReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 					return
 				}
 
-				err = gaRunner.CreateEnvSecret()
+				actionVersion, err := semver.NewVersion(gaRunner.Version)
 
 				if err != nil {
 					c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 					return
 				}
+
+				if createEnvSecretConstraint.Check(actionVersion) {
+					if err := gaRunner.CreateEnvSecret(); err != nil {
+						c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+						return
+					}
+				}
 			}
 		}
 	}

+ 1 - 0
dashboard/src/components/SaveButton.tsx

@@ -145,6 +145,7 @@ const ButtonWrapper = styled.div`
     const baseStyles = `
       display: flex;
       align-items: center;
+      z-index: 99;
     `;
 
     if (props.clearPosition) {

+ 2 - 1
dashboard/src/components/porter-form/PorterForm.tsx

@@ -42,6 +42,7 @@ interface Props {
   currentTab: string;
   setCurrentTab: (nt: string) => void;
   isLaunch?: boolean;
+  hideSpacer?: boolean;
 }
 
 const PorterForm: React.FC<Props> = (props) => {
@@ -204,7 +205,7 @@ const PorterForm: React.FC<Props> = (props) => {
       {props.showStateDebugger && (
         <Pre>{JSON.stringify(formState, undefined, 2)}</Pre>
       )}
-      <Spacer />
+      {!props.hideSpacer && <Spacer />}
     </>
   );
 };

+ 3 - 0
dashboard/src/components/porter-form/PorterFormWrapper.tsx

@@ -20,6 +20,7 @@ type PropsType = {
   showStateDebugger?: boolean;
   isLaunch?: boolean;
   includeHiddenFields?: boolean;
+  hideBottomSpacer?: boolean;
 };
 
 const PorterFormWrapper: React.FunctionComponent<PropsType> = ({
@@ -38,6 +39,7 @@ const PorterFormWrapper: React.FunctionComponent<PropsType> = ({
   showStateDebugger,
   isLaunch,
   includeHiddenFields,
+  hideBottomSpacer,
 }) => {
   const hashCode = (s: string) => {
     return s?.split("").reduce(function (a, b) {
@@ -90,6 +92,7 @@ const PorterFormWrapper: React.FunctionComponent<PropsType> = ({
           currentTab={currentTab}
           setCurrentTab={setCurrentTab}
           isLaunch={isLaunch}
+          hideSpacer={hideBottomSpacer}
         />
       </PorterFormContextProvider>
     </React.Fragment>

+ 15 - 11
dashboard/src/components/repo-selector/RepoList.tsx

@@ -136,25 +136,29 @@ const RepoList: React.FC<Props> = ({
           <Loading />
         </LoadingWrapper>
       );
-    } else if (repoError || accessError) {
+    } else if (repoError) {
       return <LoadingWrapper>Error loading repos.</LoadingWrapper>;
     } else if (repos.length == 0) {
-      return accessData.has_access ? (
+      if (accessError) {
+        return (
+          <LoadingWrapper>
+            No connected Github repos found.
+            <A href={"/api/integrations/github-app/oauth"}>
+              Authorize Porter to view your repositories.
+            </A>
+          </LoadingWrapper>
+        );
+      }
+
+      if (accessData.accounts?.length === 0) {
         <LoadingWrapper>
           No connected Github repos found. You can
           <A href={"/api/integrations/github-app/install"}>
             Install Porter in more repositories
           </A>
           .
-        </LoadingWrapper>
-      ) : (
-        <LoadingWrapper>
-          No connected Github repos found.
-          <A href={"/api/integrations/github-app/oauth"}>
-            Authorize Porter to view your repositories.
-          </A>
-        </LoadingWrapper>
-      );
+        </LoadingWrapper>;
+      }
     }
 
     // show 10 most recently used repos if user hasn't searched anything yet

+ 6 - 2
dashboard/src/main/CurrentError.tsx

@@ -26,11 +26,15 @@ export default class CurrentError extends Component<PropsType, StateType> {
   }
 
   render() {
+    let currentError = this.props.currentError;
+    if (!React.isValidElement(this.props.currentError)) {
+      currentError = String(this.props.currentError);
+    }
     if (this.props.currentError) {
       if (!this.state.expanded) {
         return (
           <StyledCurrentError>
-            <ErrorText>Error: {this.props.currentError}</ErrorText>
+            <ErrorText>Error: {currentError}</ErrorText>
             <ExpandButton onClick={() => this.setState({ expanded: true })}>
               <i className="material-icons">launch</i>
             </ExpandButton>
@@ -53,7 +57,7 @@ export default class CurrentError extends Component<PropsType, StateType> {
         <Overlay>
           <ExpandedError>
             Porter encountered an error. Full error log:
-            <CodeBlock>{this.props.currentError}</CodeBlock>
+            <CodeBlock>{currentError}</CodeBlock>
             <ExpandButtonAlt onClick={() => this.setState({ expanded: false })}>
               <i className="material-icons">remove</i>
             </ExpandButtonAlt>

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/NotificationSettingsSection.tsx

@@ -103,7 +103,7 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
         setSaveLoading(false);
       });
   };
-
+  
   return (
     <>
       <Heading>Notification Settings</Heading>
@@ -120,7 +120,7 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
           {hasNotifications != null && !hasNotifications ? (
             <Banner type="warning">
               No integration has been set up for notifications.{" "}
-              <A href="http://localhost:8080/integrations/slack">
+              <A href={`${window.location.protocol}//${window.location.host}/integrations/slack`} >
                 Connect to Slack
               </A>
             </Banner>

+ 5 - 20
dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx

@@ -135,8 +135,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
         });
       })
       .catch((err) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
 
         err = parsedErr || err.message || JSON.stringify(err);
 
@@ -238,8 +237,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
               resolve(res?.data?.external_url);
             })
             .catch((err) => {
-              let parsedErr =
-                err?.response?.data?.error;
+              let parsedErr = err?.response?.data?.error;
               err = parsedErr || err.message || JSON.stringify(err);
               setSaveValuesStatus(`Could not create subdomain: ${err}`);
 
@@ -291,8 +289,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
         }, 1000);
       })
       .catch((err: any) => {
-        let parsedErr =
-          err?.response?.data?.error;
+        let parsedErr = err?.response?.data?.error;
         err = parsedErr || err.message || JSON.stringify(err);
         setSaveValuesStatus(`Could not deploy template: ${err}`);
         setCurrentError(err);
@@ -340,20 +337,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
       setTemplateName(newTemplateName);
     }
 
-    if (currentPage === "workflow" && currentTab === "porter") {
-      const fullActionConfig = getFullActionConfig();
-      return (
-        <WorkflowPage
-          name={templateName}
-          namespace={selectedNamespace}
-          fullActionConfig={fullActionConfig}
-          shouldCreateWorkflow={shouldCreateWorkflow}
-          setShouldCreateWorkflow={setShouldCreateWorkflow}
-          setPage={setCurrentPage}
-        />
-      );
-    }
-
+    const fullActionConfig = getFullActionConfig();
     // Display main (non-source) settings page
     return (
       <SettingsPage
@@ -370,6 +354,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
         form={form}
         valuesToOverride={valuesToOverride}
         clearValuesToOverride={() => setValuesToOverride(null)}
+        fullActionConfig={fullActionConfig}
       />
     );
   };

+ 14 - 9
dashboard/src/main/home/launch/launch-flow/SettingsPage.tsx

@@ -4,7 +4,7 @@ import api from "shared/api";
 
 import { Context } from "shared/Context";
 
-import { ChoiceType, ClusterType } from "shared/types";
+import { ChoiceType, ClusterType, FullActionConfigType } from "shared/types";
 
 import { isAlphanumeric } from "shared/common";
 
@@ -15,6 +15,7 @@ import PorterFormWrapper from "components/porter-form/PorterFormWrapper";
 import Selector from "components/Selector";
 import Loading from "components/Loading";
 import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
+import WorkflowPage from "./WorkflowPage";
 
 type PropsType = WithAuthProps & {
   onSubmit: (x?: any) => void;
@@ -24,7 +25,7 @@ type PropsType = WithAuthProps & {
   form: any;
   valuesToOverride: any;
   clearValuesToOverride: () => void;
-
+  fullActionConfig: FullActionConfigType;
   templateName: string;
   setTemplateName: (x: string) => void;
   selectedNamespace: string;
@@ -157,6 +158,7 @@ class SettingsPage extends Component<PropsType, StateType> {
               console.log(val);
               onSubmit(val);
             }}
+            hideBottomSpacer={!!this.props.fullActionConfig?.git_repo}
           />
         </FadeWrapper>
       );
@@ -225,15 +227,10 @@ class SettingsPage extends Component<PropsType, StateType> {
     }
 
     if (hasSource) {
-      const [pageKey, pageName] =
-        sourceType === "repo"
-          ? ["workflow", "GitHub Actions"]
-          : ["source", "Source Settings"];
-
       return (
-        <BackButton width="155px" onClick={() => setPage(pageKey)}>
+        <BackButton width="155px" onClick={() => setPage("source")}>
           <i className="material-icons">first_page</i>
-          {pageName}
+          Source Settings
         </BackButton>
       );
     }
@@ -251,6 +248,7 @@ class SettingsPage extends Component<PropsType, StateType> {
         <StyledSettingsPage>
           {this.renderHeaderSection()}
           {this.props.isCloning && this.getNameInput()}
+
           <Heading>Destination</Heading>
           <Helper>
             Specify the cluster and namespace you would like to deploy your
@@ -295,6 +293,13 @@ class SettingsPage extends Component<PropsType, StateType> {
             />
           </ClusterSection>
           {this.renderSettingsRegion()}
+          {this.props.fullActionConfig?.git_repo && (
+            <WorkflowPage
+              fullActionConfig={this.props.fullActionConfig}
+              name={this.props.templateName}
+              namespace={this.props.selectedNamespace}
+            />
+          )}
         </StyledSettingsPage>
       </PaddingWrapper>
     );

+ 3 - 8
dashboard/src/main/home/launch/launch-flow/SourcePage.tsx

@@ -238,17 +238,12 @@ class SourcePage extends Component<PropsType, StateType> {
   };
 
   handleContinue = () => {
-    const { sourceType, setPage } = this.props;
-
-    if (sourceType === "repo") {
-      setPage("workflow");
-    } else {
-      setPage("settings");
-    }
+    const { setPage } = this.props;
+    setPage("settings");
   };
 
   render() {
-    let { templateName, setTemplateName, setPage } = this.props;
+    let { templateName, setTemplateName } = this.props;
 
     return (
       <StyledSourcePage>

+ 41 - 43
dashboard/src/main/home/launch/launch-flow/WorkflowPage.tsx

@@ -1,5 +1,4 @@
 import React, { useContext, useEffect, useState } from "react";
-import { RouteComponentProps } from "react-router";
 import { FullActionConfigType } from "../../../../shared/types";
 import api from "../../../../shared/api";
 import { Context } from "../../../../shared/Context";
@@ -7,16 +6,14 @@ import styled from "styled-components";
 import YamlEditor from "../../../../components/YamlEditor";
 import Loading from "../../../../components/Loading";
 import Helper from "../../../../components/form-components/Helper";
-import CheckboxRow from "../../../../components/form-components/CheckboxRow";
-import SaveButton from "../../../../components/SaveButton";
 
 type PropsType = {
   name: string;
   namespace: string;
   fullActionConfig: FullActionConfigType;
-  shouldCreateWorkflow: boolean;
-  setShouldCreateWorkflow: (x: (prevState: boolean) => boolean) => void;
-  setPage: (x: string) => void;
+  shouldCreateWorkflow?: boolean;
+  setShouldCreateWorkflow?: (x: (prevState: boolean) => boolean) => void;
+  setPage?: (x: string) => void;
 };
 
 const WorkflowPage: React.FC<PropsType> = (props) => {
@@ -25,10 +22,11 @@ const WorkflowPage: React.FC<PropsType> = (props) => {
   const [isLoading, setIsLoading] = useState(true);
   const [hasError, setHasError] = useState(false);
   const [workflowYAML, setWorkflowYAML] = useState("");
+  const [isExpanded, setIsExpanded] = useState(false);
 
   useEffect(() => {
     const { currentCluster, currentProject } = context;
-
+    let isSubscribed = true;
     api
       .getGHAWorkflowTemplate(
         "<token>",
@@ -43,12 +41,17 @@ const WorkflowPage: React.FC<PropsType> = (props) => {
         }
       )
       .then((res) => {
-        setWorkflowYAML(res.data);
-        setIsLoading(false);
+        if (isSubscribed) {
+          setWorkflowYAML(res.data);
+          setIsLoading(false);
+        }
       })
       .catch((err) => setHasError(true))
       .finally(() => setIsLoading(false));
-  }, []);
+    return () => {
+      isSubscribed = false;
+    };
+  }, [props.name, props.namespace, props.fullActionConfig]);
 
   const renderWorkflow = () => {
     if (isLoading) {
@@ -64,37 +67,35 @@ const WorkflowPage: React.FC<PropsType> = (props) => {
         </Placeholder>
       );
     }
-    return <YamlEditor value={workflowYAML} readOnly={true} />;
-  };
-
-  const getButtonHelper = () => {
-    if (props.shouldCreateWorkflow) {
-      return "Both secrets and workflow will be created";
-    } else {
-      return "Only secrets will be created";
-    }
+    return <AnimatedYamlEditor value={workflowYAML} readOnly={true} />;
   };
 
   return (
     <StyledWorkflowPage>
-      <BackButton width="155px" onClick={() => props.setPage("source")}>
-        <i className="material-icons">first_page</i>
-        Source Settings
-      </BackButton>
       <Heading>GitHub Actions</Heading>
       <Helper>
         To auto-deploy each time you push changes, Porter will write GitHub
         Secrets and this GitHub Actions workflow to your repository.
       </Helper>
-      {renderWorkflow()}
-      <CheckboxRow
-        toggle={() => props.setShouldCreateWorkflow((x: boolean) => !x)}
-        checked={props.shouldCreateWorkflow}
-        label="Create workflow file"
-      />
+      <ExpandableButton onClick={() => setIsExpanded((prev) => !prev)}>
+        Show Porter workflow{" "}
+        <i className="material-icons-outlined">
+          {isExpanded ? "keyboard_arrow_up" : "keyboard_arrow_down"}
+        </i>
+      </ExpandableButton>
+      {isExpanded && renderWorkflow()}
       <Helper>
-        You may copy the YAML to an existing workflow and uncheck this box to
-        prevent Porter from creating a new workflow file.
+        <GitHubActionLink show={!props.shouldCreateWorkflow}>
+          If you want to create a custom workflow file for your deployments, we
+          recommend you <b>deploy from docker instead</b>, and checkout this
+          guide:{" "}
+          <a
+            href="https://docs.porter.run/docs/auto-deploy-requirements#cicd-with-github-actions"
+            target="_blank"
+          >
+            CI/CD with GitHub Actions
+          </a>
+        </GitHubActionLink>
         <GitHubActionLink show={!props.shouldCreateWorkflow}>
           The GitHub Action can be found at{" "}
           <a
@@ -106,13 +107,6 @@ const WorkflowPage: React.FC<PropsType> = (props) => {
         </GitHubActionLink>
       </Helper>
       <Buffer />
-      <SaveButton
-        text="Continue"
-        makeFlush={true}
-        disabled={hasError}
-        onClick={() => props.setPage("settings")}
-        helper={getButtonHelper()}
-      />
     </StyledWorkflowPage>
   );
 };
@@ -158,19 +152,20 @@ const Buffer = styled.div`
   height: 35px;
 `;
 
-const BackButton = styled.div`
+const ExpandableButton = styled.div`
+  position: relative;
   display: flex;
   align-items: center;
   justify-content: space-between;
   cursor: pointer;
   font-size: 13px;
   margin-top: 25px;
-  height: 35px;
+  height: 40px;
   padding: 5px 13px;
   padding-right: 15px;
   border: 1px solid #ffffff55;
-  border-radius: 100px;
-  width: ${(props: { width: string }) => props.width};
+  border-radius: 5px;
+  width: 100%;
   color: white;
   background: #ffffff11;
 
@@ -180,12 +175,15 @@ const BackButton = styled.div`
 
   > i {
     color: white;
-    font-size: 16px;
+    font-size: 24px;
     margin-right: 6px;
     margin-left: -2px;
   }
 `;
 
+// This should carry animations for the yaml editor to be more gently introduce into the page
+const AnimatedYamlEditor = styled(YamlEditor)``;
+
 const GitHubActionLink = styled.p`
   visibility: ${(props: { show: boolean }) =>
     props.show ? "visible" : "hidden"};

+ 12 - 10
dashboard/src/main/home/modals/AccountSettingsModal.tsx

@@ -67,8 +67,19 @@ const AccountSettingsModal = () => {
         </LoadingWrapper>
       ) : (
         <>
+          {accessError && (
+            <ListWrapper>
+              <Helper>
+                No connected repositories found.
+                <A href={"/api/integrations/github-app/oauth"}>
+                  Authorize Porter to view your repositories.
+                </A>
+              </Helper>
+            </ListWrapper>
+          )}
+
           {/* Will be styled (and show what account is connected) later */}
-          {accessData.accounts?.length > 0 ? (
+          {!accessError && accessData.accounts?.length >= 0 && (
             <Placeholder>
               <User>
                 You are currently authorized as <B>{accessData.username}</B> and
@@ -107,15 +118,6 @@ const AccountSettingsModal = () => {
                 </>
               )}
             </Placeholder>
-          ) : (
-            <ListWrapper>
-              <Helper>
-                No connected repositories found.
-                <A href={"/api/integrations/github-app/oauth"}>
-                  Authorize Porter to view your repositories.
-                </A>
-              </Helper>
-            </ListWrapper>
           )}
         </>
       )}

+ 1 - 1
dashboard/src/shared/PorterErrorBoundary.tsx

@@ -18,7 +18,7 @@ const PorterErrorBoundary: React.FC<PorterErrorBoundaryProps> = ({
 }) => {
   const handleError = (err: Error, info: { componentStack: string }) => {
     StackTrace.fromError(err).then((error) => {
-      if (process.env.SENTRY_ENABLED) {
+      if (process.env.ENABLE_SENTRY) {
         Sentry.captureException(error, (scope) => {
           scope.setTags({
             error_boundary_location: errorBoundaryLocation,