Skip to content

Commit

Permalink
new datastore create form (#4511)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feroze Mohideen authored Apr 9, 2024
1 parent 8ae2c1e commit 9849c97
Show file tree
Hide file tree
Showing 27 changed files with 1,184 additions and 1,050 deletions.
4 changes: 2 additions & 2 deletions api/server/handlers/datastore/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (c *GetDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
}

// TODO: delete this branch once all datastores are on the management cluster
if !datastoreRecord.OnManagementCluster {
if datastoreRecord.IsLegacy() {
awsArn, err := arn.Parse(datastoreRecord.CloudProviderCredentialIdentifier)
if err != nil {
err = telemetry.Error(ctx, span, err, "error parsing aws account id")
Expand All @@ -107,7 +107,7 @@ func (c *GetDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
Status: string(datastoreRecord.Status),
CloudProvider: SupportedDatastoreCloudProvider_AWS,
CloudProviderCredentialIdentifier: datastoreRecord.CloudProviderCredentialIdentifier,
OnManagementCluster: true,
OnManagementCluster: datastoreRecord.OnManagementCluster,
}

// this is done for backwards compatibility; eventually we will just return proto
Expand Down
65 changes: 48 additions & 17 deletions api/server/handlers/datastore/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,8 @@ func (h *UpdateDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
telemetry.AttributeKV{Key: "engine", Value: request.Engine},
)

region, err := h.getClusterRegion(ctx, project.ID, cluster.ID)
if err != nil {
err = telemetry.Error(ctx, span, err, "error getting cluster region")
h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

// assume we are creating for now; will add update support later
datastoreProto := &porterv1.ManagedDatastore{
CloudProvider: porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS,
CloudProviderCredentialIdentifier: cluster.CloudProviderCredentialIdentifier,
Region: region,
ConnectedClusters: &porterv1.ConnectedClusters{
ConnectedClusterIds: []int64{int64(cluster.ID)},
},
Expand All @@ -98,13 +88,15 @@ func (h *UpdateDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques

var datastoreValues struct {
Config struct {
Name string `json:"name"`
DatabaseName string `json:"databaseName"`
MasterUsername string `json:"masterUsername"`
MasterUserPassword string `json:"masterUserPassword"`
AllocatedStorage int64 `json:"allocatedStorage"`
InstanceClass string `json:"instanceClass"`
EngineVersion string `json:"engineVersion"`
Name string `json:"name"`
DatabaseName string `json:"databaseName"`
MasterUsername string `json:"masterUsername"`
MasterUserPassword string `json:"masterUserPassword"`
AllocatedStorage int64 `json:"allocatedStorage"`
InstanceClass string `json:"instanceClass"`
EngineVersion string `json:"engineVersion"`
CpuCores float32 `json:"cpuCores"`
RamMegabytes int `json:"ramMegabytes"`
} `json:"config"`
}
err = json.Unmarshal(marshaledValues, &datastoreValues)
Expand Down Expand Up @@ -134,6 +126,15 @@ func (h *UpdateDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
region, err := h.getClusterRegion(ctx, project.ID, cluster.ID)
if err != nil {
err = telemetry.Error(ctx, span, err, "error getting cluster region")
h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
datastoreProto.Region = region
datastoreProto.CloudProvider = porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS
datastoreProto.CloudProviderCredentialIdentifier = cluster.CloudProviderCredentialIdentifier
datastoreProto.Kind = porterv1.EnumDatastoreKind_ENUM_DATASTORE_KIND_AWS_RDS
datastoreProto.KindValues = &porterv1.ManagedDatastore_AwsRdsKind{
AwsRdsKind: &porterv1.AwsRds{
Expand All @@ -147,6 +148,15 @@ func (h *UpdateDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
},
}
case "ELASTICACHE":
region, err := h.getClusterRegion(ctx, project.ID, cluster.ID)
if err != nil {
err = telemetry.Error(ctx, span, err, "error getting cluster region")
h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
datastoreProto.Region = region
datastoreProto.CloudProvider = porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS
datastoreProto.CloudProviderCredentialIdentifier = cluster.CloudProviderCredentialIdentifier
datastoreProto.Kind = porterv1.EnumDatastoreKind_ENUM_DATASTORE_KIND_AWS_ELASTICACHE
datastoreProto.KindValues = &porterv1.ManagedDatastore_AwsElasticacheKind{
AwsElasticacheKind: &porterv1.AwsElasticache{
Expand All @@ -156,6 +166,27 @@ func (h *UpdateDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
EngineVersion: pointer.String(datastoreValues.Config.EngineVersion),
},
}
case "MANAGED-POSTGRES":
datastoreProto.Kind = porterv1.EnumDatastoreKind_ENUM_DATASTORE_KIND_MANAGED_POSTGRES
datastoreProto.KindValues = &porterv1.ManagedDatastore_ManagedPostgresKind{
ManagedPostgresKind: &porterv1.Postgres{
CpuCores: datastoreValues.Config.CpuCores,
RamMegabytes: int32(datastoreValues.Config.RamMegabytes),
StorageGigabytes: int32(datastoreValues.Config.AllocatedStorage),
MasterUsername: pointer.String(datastoreValues.Config.MasterUsername),
MasterUserPasswordLiteral: pointer.String(datastoreValues.Config.MasterUserPassword),
},
}
case "MANAGED-REDIS":
datastoreProto.Kind = porterv1.EnumDatastoreKind_ENUM_DATASTORE_KIND_MANAGED_REDIS
datastoreProto.KindValues = &porterv1.ManagedDatastore_ManagedRedisKind{
ManagedRedisKind: &porterv1.Redis{
CpuCores: datastoreValues.Config.CpuCores,
RamMegabytes: int32(datastoreValues.Config.RamMegabytes),
StorageGigabytes: int32(datastoreValues.Config.AllocatedStorage),
MasterUserPasswordLiteral: pointer.String(datastoreValues.Config.MasterUserPassword),
},
}
default:
err = telemetry.Error(ctx, span, nil, "invalid datastore type")
h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
Expand Down
142 changes: 142 additions & 0 deletions dashboard/src/components/porter/BlockSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from "react";
import styled from "styled-components";

import Container from "components/porter/Container";
import Icon from "components/porter/Icon";
import Spacer from "components/porter/Spacer";
import Text from "components/porter/Text";
import Tooltip from "components/porter/Tooltip";

export type BlockSelectOption = {
name: string;
displayName: string;
icon: string;
description?: string;
descriptionColor?: "warner";
disabledOpts?: {
tooltipText: string;
};
};
type Props = {
options: BlockSelectOption[];
selectedOption?: BlockSelectOption;
setOption: (option: BlockSelectOption) => void;
};

const BlockSelect: React.FC<Props> = ({
options,
selectedOption,
setOption,
}) => {
return (
<BlockList>
{options.map((option) => {
return option.disabledOpts ? (
<Tooltip content={option.disabledOpts.tooltipText}>
<Block
key={option.name}
selected={selectedOption?.name === option.name}
onClick={() => {}}
disabled
>
<Container row>
<Icon src={option.icon} />
<Spacer inline x={0.5} />
<Text>{option.displayName}</Text>
</Container>
{option.description && (
<>
<Spacer y={0.5} />
<Text
size={12}
color={
option.descriptionColor
? option.descriptionColor
: "helper"
}
>
{option.description}
</Text>
</>
)}
</Block>
</Tooltip>
) : (
<Block
key={option.name}
selected={selectedOption?.name === option.name}
onClick={() => {
setOption(option);
}}
>
<Container row>
<Icon src={option.icon} />
<Spacer inline x={0.5} />
<Text>{option.displayName}</Text>
</Container>
{option.description && (
<>
<Spacer y={0.5} />
<Text
size={12}
color={
option.descriptionColor ? option.descriptionColor : "helper"
}
>
{option.description}
</Text>
</>
)}
</Block>
);
})}
</BlockList>
);
};

export default BlockSelect;

const Block = styled.div<{ selected?: boolean; disabled?: boolean }>`
display: flex;
flex-direction: column;
height: 100%;
align-items: left;
user-select: none;
font-size: 13px;
overflow: hidden;
font-weight: 500;
padding: 10px 10px;
align-item: center;
color: #ffffff;
position: relative;
transition: all 0.2s;
border-radius: 5px;
filter: ${({ disabled }) => (disabled ? "brightness(0.8) grayscale(1)" : "")};
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
background: ${(props) => props.theme.clickable.bg};
border: ${(props) =>
props.selected ? "2px solid #8590ff" : "1px solid #494b4f"};
:hover {
border: ${({ selected, disabled }) =>
!selected && !disabled && "1px solid #7a7b80"};
}
animation: fadeIn 0.3s 0s;
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`;

const BlockList = styled.div`
overflow: visible;
margin-top: 6px;
display: grid;
grid-column-gap: 25px;
grid-row-gap: 25px;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
`;
21 changes: 14 additions & 7 deletions dashboard/src/components/porter/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import React, { useState } from "react";
import styled from "styled-components";

interface TooltipProps {
type TooltipProps = {
children: React.ReactNode;
content: React.ReactNode;
position?: "top" | "right" | "bottom" | "left";
hidden?: boolean;
width?: string;
}
};

const Tooltip: React.FC<TooltipProps> = ({
children,
Expand All @@ -19,8 +19,12 @@ const Tooltip: React.FC<TooltipProps> = ({
}) => {
const [isVisible, setIsVisible] = useState(false);

const showTooltip = () => setIsVisible(true);
const hideTooltip = () => setIsVisible(false);
const showTooltip = () => {
setIsVisible(true);
};
const hideTooltip = () => {
setIsVisible(false);
};

if (hidden) {
return <>{children}</>;
Expand All @@ -29,7 +33,9 @@ const Tooltip: React.FC<TooltipProps> = ({
return (
<TooltipContainer onMouseEnter={showTooltip} onMouseLeave={hideTooltip}>
{isVisible && (
<TooltipContent position={position} width={width}>{content}</TooltipContent>
<TooltipContent position={position} width={width}>
{content}
</TooltipContent>
)}
{children}
</TooltipContainer>
Expand All @@ -40,10 +46,11 @@ export default Tooltip;

const TooltipContainer = styled.div`
position: relative;
display: inline-flex;
width: 100%;
height: 100%;
`;

const TooltipContent = styled.div<{ position: string, width?: string }>`
const TooltipContent = styled.div<{ position: string; width?: string }>`
color: #fff;
padding: 10px;
border-radius: 5px;
Expand Down
Loading

0 comments on commit 9849c97

Please sign in to comment.