Skip to content

Commit

Permalink
POR-1836 Domain deletions (#3701)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored Sep 29, 2023
1 parent 7b7aeb8 commit c8690d5
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 34 deletions.
20 changes: 16 additions & 4 deletions api/server/handlers/porter_app/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ func NewValidatePorterAppHandler(

// Deletions are the names of services and env variables to delete
type Deletions struct {
ServiceNames []string `json:"service_names"`
Predeploy []string `json:"predeploy"`
EnvVariableNames []string `json:"env_variable_names"`
EnvGroupNames []string `json:"env_group_names"`
ServiceNames []string `json:"service_names"`
Predeploy []string `json:"predeploy"`
EnvVariableNames []string `json:"env_variable_names"`
EnvGroupNames []string `json:"env_group_names"`
DomainNameDeletions map[string][]string `json:"domain_name_deletions"`
}

// ValidatePorterAppRequest is the request object for the /apps/validate endpoint
Expand Down Expand Up @@ -143,6 +144,16 @@ func (c *ValidatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "validated-with-overrides", Value: true})
}

var ServiceDomainsDeletions map[string]*porterv1.DomainNameList
if request.Deletions.DomainNameDeletions != nil {
ServiceDomainsDeletions = make(map[string]*porterv1.DomainNameList)
for k, v := range request.Deletions.DomainNameDeletions {
ServiceDomainsDeletions[k] = &porterv1.DomainNameList{
DomainNames: v,
}
}
}

validateReq := connect.NewRequest(&porterv1.ValidatePorterAppRequest{
ProjectId: int64(project.ID),
DeploymentTargetId: request.DeploymentTargetId,
Expand All @@ -154,6 +165,7 @@ func (c *ValidatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
PredeployNames: request.Deletions.Predeploy,
EnvVariableNames: request.Deletions.EnvVariableNames,
EnvGroupNames: request.Deletions.EnvGroupNames,
ServiceDomains: ServiceDomainsDeletions,
},
})
ccpResp, err := c.Config().ClusterControlPlaneClient.ValidatePorterApp(ctx, validateReq)
Expand Down
11 changes: 11 additions & 0 deletions dashboard/src/lib/hooks/useAppValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ export const useAppValidation = ({
})
.exhaustive();

const domainDeletions = data.app.services.reduce(
(acc: Record<string, string[]>, svc) => {
if (svc.domainDeletions.length) {
acc[svc.name.value] = svc.domainDeletions.map((d) => d.name);
}
return acc;
},
{}
);

const res = await api.validatePorterApp(
"<token>",
{
Expand All @@ -122,6 +132,7 @@ export const useAppValidation = ({
predeploy: data.deletions.predeploy.map((s) => s.name),
env_group_names: data.deletions.envGroupNames.map((eg) => eg.name),
env_variable_names: [],
domain_name_deletions: domainDeletions,
},
},
{
Expand Down
16 changes: 8 additions & 8 deletions dashboard/src/lib/porter-apps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,15 +363,15 @@ export function clientAppFromProto({
const predeployOverrides = serializeService(overrides.predeploy);
const predeploy = proto.predeploy
? [
deserializeService({
service: serializedServiceFromProto({
name: "pre-deploy",
service: proto.predeploy,
isPredeploy: true,
deserializeService({
service: serializedServiceFromProto({
name: "pre-deploy",
service: proto.predeploy,
isPredeploy: true,
}),
override: predeployOverrides,
}),
override: predeployOverrides,
}),
]
]
: undefined;

return {
Expand Down
68 changes: 52 additions & 16 deletions dashboard/src/lib/porter-apps/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,18 @@ export const serviceValidator = z.object({
allowConcurrent: serviceBooleanValidator.optional(),
cron: serviceStringValidator,
suspendCron: serviceBooleanValidator.optional(),
timeoutSeconds: serviceNumberValidator
timeoutSeconds: serviceNumberValidator,
}),
z.object({
type: z.literal("predeploy"),
}),
]),
domainDeletions: z
.object({
name: z.string(),
})
.array()
.default([]),
});

export type ClientService = z.infer<typeof serviceValidator>;
Expand Down Expand Up @@ -273,13 +279,21 @@ export function deserializeService({
service.ramMegabytes,
override?.ramMegabytes
),
domainDeletions: [],
};

return match(service.config)
.with({ type: "web" }, (config) => {
const overrideWebConfig =
override?.config.type == "web" ? override.config : undefined;

const uniqueDomains = Array.from(
new Set([
...config.domains.map((domain) => domain.name),
...(overrideWebConfig?.domains ?? []).map((domain) => domain.name),
])
).map((domain) => ({ name: domain }));

return {
...baseService,
config: {
Expand All @@ -293,9 +307,7 @@ export function deserializeService({
override: overrideWebConfig?.healthCheck,
}),

domains: Array.from(
new Set([...config.domains, ...(overrideWebConfig?.domains ?? [])])
).map((domain) => ({
domains: uniqueDomains.map((domain) => ({
name: ServiceField.string(
domain.name,
overrideWebConfig?.domains.find(
Expand Down Expand Up @@ -337,15 +349,27 @@ export function deserializeService({
allowConcurrent:
typeof config.allowConcurrent === "boolean" ||
typeof overrideJobConfig?.allowConcurrent === "boolean"
? ServiceField.boolean(config.allowConcurrent, overrideJobConfig?.allowConcurrent)
? ServiceField.boolean(
config.allowConcurrent,
overrideJobConfig?.allowConcurrent
)
: ServiceField.boolean(false, undefined),
cron: ServiceField.string(config.cron, overrideJobConfig?.cron),
suspendCron:
typeof config.suspendCron === "boolean" ||
typeof overrideJobConfig?.suspendCron === "boolean"
? ServiceField.boolean(config.suspendCron, overrideJobConfig?.suspendCron)
? ServiceField.boolean(
config.suspendCron,
overrideJobConfig?.suspendCron
)
: ServiceField.boolean(false, undefined),
timeoutSeconds: config.timeoutSeconds == 0 ? ServiceField.number(3600, overrideJobConfig?.timeoutSeconds) : ServiceField.number(config.timeoutSeconds, overrideJobConfig?.timeoutSeconds),
timeoutSeconds:
config.timeoutSeconds == 0
? ServiceField.number(3600, overrideJobConfig?.timeoutSeconds)
: ServiceField.number(
config.timeoutSeconds,
overrideJobConfig?.timeoutSeconds
),
},
};
})
Expand Down Expand Up @@ -410,6 +434,7 @@ export function serviceProto(service: SerializedService): Service {
value: {
...config,
allowConcurrentOptional: config.allowConcurrent,
timeoutSeconds: BigInt(config.timeoutSeconds),
},
case: "jobConfig",
},
Expand Down Expand Up @@ -466,14 +491,25 @@ export function serializedServiceFromProto({
...value,
},
}))
.with({ case: "jobConfig" }, ({ value }) => ({
...service,
name,
config: {
type: isPredeploy ? ("predeploy" as const) : ("job" as const),
...value,
allowConcurrent: value.allowConcurrentOptional
},
}))
.with({ case: "jobConfig" }, ({ value }) =>
isPredeploy
? {
...service,
name,
config: {
type: "predeploy" as const,
},
}
: {
...service,
name,
config: {
type: "job" as const,
...value,
allowConcurrent: value.allowConcurrentOptional,
timeoutSeconds: Number(value.timeoutSeconds),
},
}
)
.exhaustive();
}
13 changes: 9 additions & 4 deletions dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {

const msg =
"An error occurred while deploying your application. Please try again.";
updateAppStep({ step: "stack-launch-failure", errorMessage: msg, appName: name.value });
updateAppStep({
step: "stack-launch-failure",
errorMessage: msg,
appName: name.value,
});
setDeployError(msg);
return false;
} finally {
Expand Down Expand Up @@ -373,7 +377,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
setStep((prev) => Math.max(prev, 5));
} else {
setStep((prev) => Math.min(prev, 2));
};
}
}, [services]);

// todo(ianedwards): it's a bit odd that the button error can be set to either a string or JSX,
Expand Down Expand Up @@ -606,8 +610,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
}
>
{detectedServices.count > 0
? `Detected ${detectedServices.count} service${detectedServices.count > 1 ? "s" : ""
} from porter.yaml.`
? `Detected ${detectedServices.count} service${
detectedServices.count > 1 ? "s" : ""
} from porter.yaml.`
: `Could not detect any services from porter.yaml. Make sure it exists in the root of your repo.`}
</Text>
</AppearingDiv>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ const CustomDomains: React.FC<Props> = ({ index }) => {
control,
name: `app.services.${index}.config.domains`,
});
const { append: appendDomainDeletion } = useFieldArray({
control,
name: `app.services.${index}.domainDeletions`,
});

const onRemove = (i: number, name: string) => {
remove(i);
appendDomainDeletion({
name,
});
};

return (
<CustomDomainsContainer>
Expand All @@ -39,8 +50,9 @@ const CustomDomains: React.FC<Props> = ({ index }) => {
/>
<DeleteButton
onClick={() => {
//remove customDomain at the index
remove(i);
if (!customDomain.name.readOnly) {
onRemove(i, customDomain.name.value);
}
}}
>
<i className="material-icons">cancel</i>
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/shared/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,7 @@ const validatePorterApp = baseApi<
predeploy: string[];
env_variable_names: string[];
env_group_names: string[];
domain_name_deletions: Record<string, string[]>;
};
},
{
Expand Down

0 comments on commit c8690d5

Please sign in to comment.