ソースを参照

Merge branch 'master' into capi-integration

jusrhee 3 年 前
コミット
91667649e9

+ 6 - 1
api/server/handlers/cluster/delete.go

@@ -42,9 +42,14 @@ func (c *ClusterDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 				c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
 				return
 			}
+			if cluster.Status == types.UpdatingUnavailable || cluster.Status == types.Updating {
+				e := fmt.Errorf("unable to delete cluster %d that is updating", cluster.ID)
+				c.HandleAPIError(w, r, apierrors.NewErrInternal(e))
+				return
+			}
 			var revisionID string
 			for _, rev := range revisions {
-				if rev.Condition == "SUCCESS" {
+				if rev.Condition != "" {
 					revisionID = rev.ID.String()
 					break
 				}

+ 16 - 3
cli/cmd/docker/agent.go

@@ -315,7 +315,12 @@ func (a *Agent) PushImage(image string) error {
 
 	termFd, isTerm := term.GetFdInfo(os.Stderr)
 
-	return jsonmessage.DisplayJSONMessagesStream(out, os.Stderr, termFd, isTerm, nil)
+	err = jsonmessage.DisplayJSONMessagesStream(out, os.Stderr, termFd, isTerm, nil)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 func (a *Agent) getPullOptions(image string) (types.ImagePullOptions, error) {
@@ -361,13 +366,21 @@ func (a *Agent) getEncodedRegistryAuth(image string) (string, error) {
 		return "", err
 	}
 
+	serverAddress := serverURL
+	if !strings.Contains(serverURL, "https://") {
+		serverAddress = fmt.Sprintf("https://%s", serverURL)
+	}
+
 	authConfig := types.AuthConfig{
 		Username:      user,
 		Password:      secret,
-		ServerAddress: "https://" + serverURL,
+		ServerAddress: serverAddress,
 	}
 
-	authConfigBytes, _ := json.Marshal(authConfig)
+	authConfigBytes, err := json.Marshal(authConfig)
+	if err != nil {
+		return "", fmt.Errorf("unable to marshal docker auth config: %w", err)
+	}
 
 	return base64.URLEncoding.EncodeToString(authConfigBytes), nil
 }

+ 3 - 3
cli/cmd/errors/error_handler.go

@@ -3,6 +3,7 @@ package errors
 import (
 	"fmt"
 	"os"
+	"time"
 
 	"github.com/fatih/color"
 	"github.com/getsentry/sentry-go"
@@ -35,9 +36,8 @@ func (h *sentryErrorHandler) HandleError(err error) {
 			})
 		})
 
-		if eventID := localHub.CaptureException(err); eventID == nil {
-			color.New(color.FgRed).Fprintf(os.Stderr, "error in sending exception to sentry\n")
-		}
+		localHub.CaptureException(err)
+		sentry.Flush(2 * time.Second)
 	}
 
 	color.New(color.FgRed).Fprintf(os.Stderr, "error: %s\n", err.Error())

+ 1 - 1
dashboard/src/components/ProvisionerForm.tsx

@@ -33,7 +33,7 @@ const ProvisionerForm: React.FC<Props> = ({
         Configure settings
       </Heading>
       <Helper>
-        Configure settings for your new cluster.
+        Configure settings for your AWS environment.
       </Helper>
       {useAssumeRole ? (
         <ProvisionerSettings credentialId={credentialId} AWSAccountID={AWSAccountID} />

+ 4 - 23
dashboard/src/components/ProvisionerSettings.tsx

@@ -199,6 +199,7 @@ const ProvisionerSettings: React.FC<Props> = props => {
         currentCluster.status === "UPDATING_UNAVAILABLE"
       )
     );
+    setClusterName(`${currentProject.name}-cluster`);
   }, []);
 
   useEffect(() => {
@@ -225,20 +226,10 @@ const ProvisionerSettings: React.FC<Props> = props => {
     if (!props.clusterId) {
       return (
         <>
-          <Heading isAtTop>Cluster configuration</Heading>
+          <Heading isAtTop>Select an AWS region</Heading>
           <Helper>
-            Porter will create a new cluster for your applications in the specified region.
+            Porter will automatically provision your infrastructure in the specified region.
           </Helper>
-          <InputRow
-            width="350px"
-            isRequired
-            disabled={isReadOnly}
-            type="string"
-            value={clusterName}
-            setValue={(x: string) => setClusterName(x)}
-            label="🏷️ Cluster name"
-            placeholder="ex: total-perspective-vortex"
-          />
           <SelectRow
             options={regionOptions}
             width="350px"
@@ -247,7 +238,7 @@ const ProvisionerSettings: React.FC<Props> = props => {
             scrollBuffer={true}
             dropdownMaxHeight="240px"
             setActiveValue={setAwsRegion}
-            label="📍 Select an AWS region"
+            label="📍 AWS region"
           />
         </>
       )
@@ -257,16 +248,6 @@ const ProvisionerSettings: React.FC<Props> = props => {
     return (
       <>
         <Heading isAtTop>EKS configuration</Heading>
-        <InputRow
-          width="350px"
-          isRequired
-          disabled={isReadOnly || true}
-          type="string"
-          value={clusterName}
-          setValue={(x: string) => setClusterName(x)}
-          label="🏷️ Cluster name"
-          placeholder="ex: total-perspective-vortex"
-        />
         <SelectRow
           options={regionOptions}
           width="350px"

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

@@ -6,7 +6,7 @@ type Props = {
   text?: string;
   onClick: () => void;
   disabled?: boolean;
-  status?: React.ReactNode | null;
+  status?: string | null;
   color?: string;
   rounded?: boolean;
   helper?: string | null;

+ 2 - 2
dashboard/src/main/home/Home.tsx

@@ -404,9 +404,9 @@ const Home: React.FC<Props> = props => {
               return <Onboarding />;
             }}
           />
-          {user?.isPorterUser || overrideInfraTabEnabled({
+          {(user?.isPorterUser || overrideInfraTabEnabled({
             projectID: currentProject?.id,
-          }) && (
+          })) && (
             <Route
               path="/infrastructure"
               render={() => {

+ 14 - 14
dashboard/src/main/home/cluster-dashboard/dashboard/ClusterRevisionSelector.tsx

@@ -8,13 +8,13 @@ import warning from "assets/warning.png";
 import { readableDate } from "shared/string_utils";
 import { Context } from "shared/Context";
 import ExpandableSection from "components/porter/ExpandableSection";
-import { 
-  Contract, 
-  Cluster, 
-  EKS, 
-  NodeGroupType, 
-  EnumKubernetesKind, 
-  EnumCloudProvider 
+import {
+  Contract,
+  Cluster,
+  EKS,
+  NodeGroupType,
+  EnumKubernetesKind,
+  EnumCloudProvider
 } from "@porter-dev/api-contracts";
 import Spacer from "components/porter/Spacer";
 
@@ -51,7 +51,7 @@ const ClusterRevisionSelector: React.FC<Props> = ({
 
       if (data[0].condition !== "") {
         setFailedContractId(data[0].id);
-        setProvisionFailureReason(data[0].condition);
+        setProvisionFailureReason(data[0].condition_metadata?.message || data[0].condition);
       }
     }
 
@@ -97,7 +97,7 @@ const ClusterRevisionSelector: React.FC<Props> = ({
   useEffect(() => {
     updateContracts();
   }, [currentCluster]);
-  
+
   const createContract = () => {
     if (false) {
       api.createContract(
@@ -113,11 +113,11 @@ const ClusterRevisionSelector: React.FC<Props> = ({
     }
   };
 
-const deleteContract = () => {
+  const deleteContract = () => {
     api.deleteContract(
       "<token>",
       {},
-      { 
+      {
         project_id: currentProject.id,
         revision_id: failedContractId,
       }
@@ -182,7 +182,7 @@ const deleteContract = () => {
         {
           failedContractId && (
             <DeleteButton>
-              <i 
+              <i
                 className="material-icons-outlined"
                 onClick={deleteContract}
               >
@@ -337,7 +337,7 @@ const RollbackButton = styled.div`
     props.disabled ? "#aaaabbee" : "#616FEEcc"};
   :hover {
     background: ${(props: { disabled: boolean }) =>
-      props.disabled ? "" : "#405eddbb"};
+    props.disabled ? "" : "#405eddbb"};
   }
 `;
 
@@ -351,7 +351,7 @@ const Tr = styled.tr`
     props.selected ? "#ffffff11" : ""};
   :hover {
     background: ${(props: { disableHover?: boolean; selected?: boolean }) =>
-      props.disableHover ? "" : "#ffffff22"};
+    props.disableHover ? "" : "#ffffff22"};
   }
 `;
 

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

@@ -262,9 +262,9 @@ const NavButton = styled(SidebarLink)`
   }
 
   > i {
-    font-size: 20px;
-    padding-top: 4px;
+    font-size: 18px;
     border-radius: 3px;
+    margin-left: 2px;
     margin-right: 10px;
   }
 `;

+ 1 - 1
go.mod

@@ -74,7 +74,7 @@ require (
 	github.com/glebarez/sqlite v1.6.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.0.59
+	github.com/porter-dev/api-contracts v0.0.60
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
 	github.com/xanzy/go-gitlab v0.68.0

+ 2 - 2
go.sum

@@ -1466,8 +1466,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
-github.com/porter-dev/api-contracts v0.0.59 h1:A0r7WJ29qng67p8kFsLAqGSdTR86pS2WjjyYe2PF+Do=
-github.com/porter-dev/api-contracts v0.0.59/go.mod h1:qr2L58mJLr5DUGV5OPw3REiSrQvJq6TgkKyEWP95dyU=
+github.com/porter-dev/api-contracts v0.0.60 h1:wqsN9XkqcQLIiOK2wdTyknIG8ZCw9y80SX9lpnri0is=
+github.com/porter-dev/api-contracts v0.0.60/go.mod h1:qr2L58mJLr5DUGV5OPw3REiSrQvJq6TgkKyEWP95dyU=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935 h1:hfb3nt3AJXIBbevu6ARTg9SdOkMP6WLbKBiG5hT5rcc=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=

+ 35 - 0
internal/models/api_contract_revision.go

@@ -1,6 +1,9 @@
 package models
 
 import (
+	"database/sql/driver"
+	"encoding/json"
+
 	"github.com/google/uuid"
 	"gorm.io/gorm"
 )
@@ -25,10 +28,42 @@ type APIContractRevision struct {
 
 	// Condition is the status of the apply that happened for this revision.
 	// Condition will contain any failure reasons for a revision, or "SUCCESS" if the revision was applied successfully.
+	// Further details to expand upon this condition are available in ConditionMetadata
 	Condition string `json:"condition"`
+
+	// ConditionMetadata contains all information about the condition of a given revision.
+	// If condition is "SUCCESS", this will likely be empty.
+	// This will follow the error response contract, with the following keys:
+	// {
+	// 	"errors": [
+	// 		{
+	// 			"code": "string",
+	// 			"message": "string",
+	// 			"metadata": {}
+	// 		}
+	// 	]
+	// }
+	ConditionMetadata JSONB `json:"condition_metadata" sql:"type:jsonb" gorm:"type:jsonb"`
 }
 
 // TableName overrides the table name
 func (APIContractRevision) TableName() string {
 	return "api_contract_revisions"
 }
+
+// JSONB implements the jsonb type in postgres for gorm
+type JSONB map[string]any
+
+// Value implements the driver.Valuer interface
+func (j JSONB) Value() (driver.Value, error) {
+	valueString, err := json.Marshal(j)
+	return string(valueString), err
+}
+
+// Scan implements the sql.Scanner interface
+func (j *JSONB) Scan(value interface{}) error {
+	if err := json.Unmarshal(value.([]byte), &j); err != nil {
+		return err
+	}
+	return nil
+}

+ 2 - 2
internal/repository/gorm/api_contract.go

@@ -39,13 +39,13 @@ func (cr APIContractRepository) List(ctx context.Context, projectID uint, cluste
 	var confs []*models.APIContractRevision
 
 	if clusterID == 0 {
-		tx := cr.db.Where("project_id = ?", projectID).Find(&confs)
+		tx := cr.db.Where("project_id = ?", projectID).Find(&confs).Order("created_at desc")
 		if tx.Error != nil {
 			return nil, tx.Error
 		}
 		return confs, nil
 	}
-	tx := cr.db.Where("project_id = ? and cluster_id = ?", projectID, clusterID).Find(&confs)
+	tx := cr.db.Where("project_id = ? and cluster_id = ?", projectID, clusterID).Find(&confs).Order("created_at desc")
 	if tx.Error != nil {
 		return nil, tx.Error
 	}