Selaa lähdekoodia

Merge branch 'nafees/stacks' into dev

Mohammed Nafees 3 vuotta sitten
vanhempi
sitoutus
7d5564cf8f

+ 9 - 0
api/server/handlers/stack/add_application.go

@@ -117,6 +117,14 @@ func (p *StackAddApplicationHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 		return
 	}
 
+	// re-read the stack to get the most upto date information
+	stack, err = p.Repo().Stack().ReadStackByID(proj.ID, stack.ID)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
 	registries, err := p.Repo().Registry().ListRegistriesByProjectID(cluster.ProjectID)
 
 	if err != nil {
@@ -144,6 +152,7 @@ func (p *StackAddApplicationHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 			registries: registries,
 			helmAgent:  helmAgent,
 			request:    req,
+			stack:      stack,
 		})
 
 		if err != nil {

+ 1 - 0
api/server/handlers/stack/create.go

@@ -198,6 +198,7 @@ func (p *StackCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 				registries: registries,
 				helmAgent:  helmAgent,
 				request:    appResource,
+				stack:      stack,
 			})
 
 			if err != nil {

+ 11 - 0
api/server/handlers/stack/helpers.go

@@ -17,6 +17,7 @@ type applyAppResourceOpts struct {
 	helmAgent  *helm.Agent
 	request    *types.CreateStackAppResourceRequest
 	registries []*models.Registry
+	stack      *models.Stack
 }
 
 func applyAppResource(opts *applyAppResourceOpts) (*release.Release, error) {
@@ -40,6 +41,16 @@ func applyAppResource(opts *applyAppResourceOpts) (*release.Release, error) {
 		Registries: opts.registries,
 	}
 
+	if conf.Values == nil {
+		conf.Values = make(map[string]interface{})
+	}
+
+	conf.Values["stack"] = map[string]interface{}{
+		"enabled":  true,
+		"name":     opts.stack.Name,
+		"revision": opts.stack.Revisions[0].ID,
+	}
+
 	return opts.helmAgent.InstallChart(conf, opts.config.DOConf)
 }
 

+ 5 - 1
dashboard/babel.config.json

@@ -1,5 +1,9 @@
 {
-  "plugins": ["lodash", "babel-plugin-styled-components", "@babel/plugin-syntax-dynamic-import"],
+  "plugins": [
+    "lodash",
+    "babel-plugin-styled-components",
+    "@babel/plugin-syntax-dynamic-import"
+  ],
   "presets": [
     "@babel/preset-env",
     "@babel/preset-react",

+ 5 - 0
dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx

@@ -71,6 +71,11 @@ export const Dashboard: React.FunctionComponent = () => {
     }
   }, [location]);
 
+  // Need to reset tab to reset views that don't auto-update on cluster switch (esp namespaces + settings)
+  useEffect(() => {
+    setCurrentTab("nodes");
+  }, [context.currentCluster]);
+
   return (
     <>
       <TitleSection>

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

@@ -364,7 +364,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
   const renderCurrentPage = () => {
     let { form, currentTab } = props;
 
-    if (currentPage === "source" && currentTab === "porter") {
+    if (currentPage === "source" && form?.hasSource) {
       return (
         <SourcePage
           sourceType={sourceType}

+ 2 - 2
dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx

@@ -115,7 +115,7 @@ const EditCollaboratorModal = () => {
   return (
     <>
       <ModalTitle>
-        Update {isInvite ? "Invite for" : "Collaborator"} {user?.email}
+        Update {isInvite ? "invite for" : "collaborator"} {user?.email}
       </ModalTitle>
       <Subtitle>Specify a different role for this user.</Subtitle>
       <RoleSelectorWrapper>
@@ -132,7 +132,7 @@ const EditCollaboratorModal = () => {
       </RoleSelectorWrapper>
 
       <SaveButton
-        text={`Update ${isInvite ? "Invite" : "Collaborator"}`}
+        text={`Update ${isInvite ? "invite" : "collaborator"}`}
         color="#616FEEcc"
         onClick={() => handleUpdate()}
         status={status}

+ 7 - 8
dashboard/src/main/home/project-settings/InviteList.tsx

@@ -27,7 +27,7 @@ export type Collaborator = {
   roles: string[];
 };
 
-const InvitePage: React.FunctionComponent<Props> = ({}) => {
+const InvitePage: React.FunctionComponent<Props> = ({ }) => {
   const {
     currentProject,
     setCurrentModal,
@@ -353,9 +353,8 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
     inviteList.sort((a, b) => (a.email > b.email ? 1 : -1));
     inviteList.sort((a, b) => (a.accepted > b.accepted ? 1 : -1));
     const buildInviteLink = (token: string) => `
-      ${isHTTPS ? "https://" : ""}${window.location.host}/api/projects/${
-      currentProject.id
-    }/invites/${token}
+      ${isHTTPS ? "https://" : ""}${window.location.host}/api/projects/${currentProject.id
+      }/invites/${token}
     `;
 
     if (!user) {
@@ -434,7 +433,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
   return (
     <>
       <>
-        <Heading isAtTop={true}>Share Project</Heading>
+        <Heading isAtTop={true}>Share project</Heading>
         <Helper>Generate a project invite for another user.</Helper>
         <InputRowWrapper>
           <InputRow
@@ -442,7 +441,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
             type="text"
             setValue={(newEmail: string) => setEmail(newEmail)}
             width="100%"
-            placeholder="ex: mrp@getporter.dev"
+            placeholder="ex: mrp@porter.run"
           />
         </InputRowWrapper>
         <Helper>Specify the roles for this user.</Helper>
@@ -460,7 +459,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
         </RoleSelectorWrapper>
         <ButtonWrapper>
           <InviteButton disabled={!hasSeats} onClick={() => validateEmail()}>
-            Create Invite
+            Create invite
           </InviteButton>
           {isInvalidEmail && (
             <Invalid>Invalid email address. Please try again.</Invalid>
@@ -473,7 +472,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
         </ButtonWrapper>
       </>
 
-      <Heading>Invites & Collaborators</Heading>
+      <Heading>Invites & collaborators</Heading>
       <Helper>Manage pending invites and view collaborators.</Helper>
       {isLoading && <Loading height={"30%"} />}
       {data?.length && !isLoading ? (

+ 2 - 2
dashboard/src/main/home/project-settings/ProjectSettings.tsx

@@ -88,7 +88,7 @@ class ProjectSettings extends Component<PropsType, StateType> {
 
       tabOptions.push({
         value: "additional-settings",
-        label: "Additional Settings",
+        label: "Additional settings",
       });
     }
 
@@ -178,7 +178,7 @@ class ProjectSettings extends Component<PropsType, StateType> {
   render() {
     return (
       <StyledProjectSettings>
-        <TitleSection>Project Settings</TitleSection>
+        <TitleSection>Project settings</TitleSection>
         <TabRegion
           currentTab={this.state.currentTab}
           setCurrentTab={(x: AvailableTabs) => this.setState({ currentTab: x })}

+ 26 - 18
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -44,9 +44,10 @@ export const ClusterSection: React.FC<Props> = ({
           <SideLine />
           <NavButton
             path="/applications"
+            targetClusterName={cluster?.name}
             active={
               currentCluster.id === clusterId &&
-              window.location.pathname === "/applications"
+              window.location.pathname.startsWith("/applications")
             }
           >
             <Img src={monoweb} />
@@ -54,9 +55,10 @@ export const ClusterSection: React.FC<Props> = ({
           </NavButton>
           <NavButton
             path="/jobs"
+            targetClusterName={cluster?.name}
             active={
               currentCluster.id === clusterId &&
-              window.location.pathname === "/jobs"
+              window.location.pathname.startsWith("/jobs")
             }
           >
             <Img src={monojob} />
@@ -64,9 +66,10 @@ export const ClusterSection: React.FC<Props> = ({
           </NavButton>
           <NavButton
             path="/env-groups"
+            targetClusterName={cluster?.name}
             active={
               currentCluster.id === clusterId &&
-              window.location.pathname === "/env-groups"
+              window.location.pathname.startsWith("/env-groups")
             }
           >
             <Img src={sliders} />
@@ -77,9 +80,10 @@ export const ClusterSection: React.FC<Props> = ({
             currentProject.enable_rds_databases && (
               <NavButton
                 path="/databases"
+                targetClusterName={cluster?.name}
                 active={
                   currentCluster.id === clusterId &&
-                  window.location.pathname === "/databases"
+                  window.location.pathname.startsWith("/databases")
                 }
               >
                 <Icon className="material-icons-outlined">storage</Icon>
@@ -89,9 +93,10 @@ export const ClusterSection: React.FC<Props> = ({
           {currentProject?.stacks_enabled ? (
             <NavButton
               path="/stacks"
+              targetClusterName={cluster?.name}
               active={
                 currentCluster.id === clusterId &&
-                window.location.pathname === "/stacks"
+                window.location.pathname.startsWith("/stacks")
               }
             >
               <Icon className="material-icons-outlined">lan</Icon>
@@ -99,11 +104,12 @@ export const ClusterSection: React.FC<Props> = ({
             </NavButton>
           ) : null}
           {currentProject?.preview_envs_enabled && (
-            <NavButton 
+            <NavButton
               path="/preview-environments"
+              targetClusterName={cluster?.name}
               active={
                 currentCluster.id === clusterId &&
-                window.location.pathname === "/preview-environments"
+                window.location.pathname.startsWith("/preview-environments")
               }
             >
               <InlineSVGWrapper
@@ -119,9 +125,10 @@ export const ClusterSection: React.FC<Props> = ({
           )}
           <NavButton
             path={"/cluster-dashboard"}
+            targetClusterName={cluster?.name}
             active={
               currentCluster.id === clusterId &&
-              window.location.pathname === "/cluster-dashboard"
+              window.location.pathname.startsWith("/cluster-dashboard")
             }
           >
             <Icon className="material-icons">device_hub</Icon>
@@ -134,18 +141,19 @@ export const ClusterSection: React.FC<Props> = ({
 
   return (
     <>
-      <ClusterSelector 
+      <ClusterSelector
         onClick={() => setIsExpanded(!isExpanded)}
         active={
-          !isExpanded && cluster.id === currentCluster.id && [
-            "/cluster-dashboard",
-            "/preview-environments",
-            "/stacks",
-            "/databases",
-            "/env-groups",
-            "/jobs",
-            "/applications"
-          ].includes(window.location.pathname)
+          !isExpanded &&
+          cluster.id === currentCluster.id && (
+            window.location.pathname.startsWith("/cluster-dashboard") ||
+            window.location.pathname.startsWith("/preview-environments") ||
+            window.location.pathname.startsWith("/stacks") ||
+            window.location.pathname.startsWith("/databases") ||
+            window.location.pathname.startsWith("/env-groups") ||
+            window.location.pathname.startsWith("/jobs") ||
+            window.location.pathname.startsWith("/applications")
+          )
         }
       >
         <LinkWrapper>

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

@@ -204,4 +204,4 @@ const InitializeButton = styled.div`
   :hover {
     background: #ffffff22;
   }
-`;
+`;

+ 4 - 6
dashboard/src/main/home/sidebar/SidebarLink.tsx

@@ -3,11 +3,9 @@ import { NavLink, NavLinkProps, useParams } from "react-router-dom";
 import { Context } from "shared/Context";
 import { useRouting } from "shared/routing";
 
-const SidebarLink: React.FC<{ path: string } & Omit<NavLinkProps, "to">> = ({
-  children,
-  path,
-  ...props
-}) => {
+const SidebarLink: React.FC<
+  { path: string; targetClusterName?: string } & Omit<NavLinkProps, "to">
+> = ({ children, path, ...props }) => {
   const params = useParams<{ namespace: string }>();
   const { getQueryParam } = useRouting();
   const { currentCluster, currentProject } = useContext(Context);
@@ -20,7 +18,7 @@ const SidebarLink: React.FC<{ path: string } & Omit<NavLinkProps, "to">> = ({
     let pathNamespace = params.namespace;
     const search = new URLSearchParams();
     if (currentCluster?.name) {
-      search.append("cluster", currentCluster.name);
+      search.append("cluster", props.targetClusterName || currentCluster.name);
     }
 
     if (currentProject?.id) {

+ 4 - 4
internal/repository/gorm/monitor.go

@@ -43,16 +43,16 @@ func (m *MonitorTestResultRepository) UpdateMonitorTestResult(monitor *models.Mo
 	return monitor, nil
 }
 
-func (m *MonitorTestResultRepository) ArchiveMonitorTestResults(recommenderID string) error {
-	query := m.db.Debug().Unscoped().Model(&models.MonitorTestResult{}).Where("last_recommender_run_id != ?", recommenderID)
+func (m *MonitorTestResultRepository) ArchiveMonitorTestResults(projectID, clusterID uint, recommenderID string) error {
+	query := m.db.Debug().Unscoped().Model(&models.MonitorTestResult{}).Where("project_id = ? AND cluster_id = ? AND last_recommender_run_id != ?", projectID, clusterID, recommenderID)
 
 	return query.Update("archived", true).Error
 }
 
-func (m *MonitorTestResultRepository) DeleteOldMonitorTestResults(recommenderID string) error {
+func (m *MonitorTestResultRepository) DeleteOldMonitorTestResults(projectID, clusterID uint, recommenderID string) error {
 	monitors := make([]*models.MonitorTestResult, 0)
 
-	query := m.db.Debug().Unscoped().Where("last_recommender_run_id != ?", recommenderID)
+	query := m.db.Debug().Unscoped().Where("project_id = ? AND cluster_id = ? AND last_recommender_run_id != ?", projectID, clusterID, recommenderID)
 
 	// we need to switch on the database type to delete records older than 24 hours
 	switch m.db.Dialector.Name() {

+ 2 - 2
internal/repository/monitor.go

@@ -7,6 +7,6 @@ type MonitorTestResultRepository interface {
 	ReadMonitorTestResult(projectID, clusterID uint, operationID string) (*models.MonitorTestResult, error)
 	UpdateMonitorTestResult(monitor *models.MonitorTestResult) (*models.MonitorTestResult, error)
 
-	ArchiveMonitorTestResults(recommenderID string) error
-	DeleteOldMonitorTestResults(recommenderID string) error
+	ArchiveMonitorTestResults(projectID, clusterID uint, recommenderID string) error
+	DeleteOldMonitorTestResults(projectID, clusterID uint, recommenderID string) error
 }

+ 2 - 2
internal/repository/test/monitor.go

@@ -23,10 +23,10 @@ func (n *MonitorTestResultRepository) UpdateMonitorTestResult(monitor *models.Mo
 	panic("not implemented") // TODO: Implement
 }
 
-func (n *MonitorTestResultRepository) ArchiveMonitorTestResults(recommenderID string) error {
+func (n *MonitorTestResultRepository) ArchiveMonitorTestResults(projectID, clusterID uint, recommenderID string) error {
 	panic("not implemented") // TODO: Implement
 }
 
-func (n *MonitorTestResultRepository) DeleteOldMonitorTestResults(recommenderID string) error {
+func (n *MonitorTestResultRepository) DeleteOldMonitorTestResults(projectID, clusterID uint, recommenderID string) error {
 	panic("not implemented") // TODO: Implement
 }

+ 13 - 6
workers/jobs/recommender.go

@@ -251,16 +251,23 @@ func (n *recommender) Run() error {
 				continue
 			}
 		}
-	}
 
-	// archive any test results which don't match
-	err := n.repo.MonitorTestResult().ArchiveMonitorTestResults(n.runRecommenderID)
+		err = n.repo.MonitorTestResult().ArchiveMonitorTestResults(ids.projectID, ids.clusterID, n.runRecommenderID)
 
-	if err != nil {
-		return err
+		if err != nil {
+			log.Printf("error archiving test results for cluster ID %d: %v", ids.clusterID, err)
+			continue
+		}
+
+		err = n.repo.MonitorTestResult().DeleteOldMonitorTestResults(ids.projectID, ids.clusterID, n.runRecommenderID)
+
+		if err != nil {
+			log.Printf("error deleting old test results for cluster ID %d: %v", ids.clusterID, err)
+			continue
+		}
 	}
 
-	return n.repo.MonitorTestResult().DeleteOldMonitorTestResults(n.runRecommenderID)
+	return nil
 }
 
 func (n *recommender) getMonitorTestResultFromQueryResult(cluster *models.Cluster, queryRes *opa.OPARecommenderQueryResult, recommenderID string) *models.MonitorTestResult {

+ 2 - 0
workers/main.go

@@ -66,6 +66,8 @@ func main() {
 	log.Printf("setting max worker count to: %d\n", envDecoder.MaxWorkers)
 	log.Printf("setting max job queue count to: %d\n", envDecoder.MaxQueue)
 
+	log.Printf("legacy project ids are: %v", envDecoder.LegacyProjectIDs)
+
 	db, err := adapter.New(&envDecoder.DBConf)
 
 	if err != nil {