Skip to content

Commit 5d96b1f

Browse files
authored
Merge pull request #53 from DefangLabs/lio/handle-unresolved
Handle unresolved values in dynamic provider
2 parents b8bfed6 + 1db7f99 commit 5d96b1f

File tree

1 file changed

+87
-72
lines changed

1 file changed

+87
-72
lines changed

index.ts

Lines changed: 87 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,20 @@ function isOptionalIntGt0(x?: number): boolean {
484484
return x === undefined || (Number.isSafeInteger(x) && x > 0);
485485
}
486486

487+
// Unknown values are encoded as a distinguished string value and passed to the Dynamic Providers `check` method.
488+
type UnknownValue = "04da6b54-80e4-46f7-96ec-b56ff0331ba9";
489+
const UNKNOWN: UnknownValue = pulumi.runtime.unknownValue;
490+
type UnknownInputs<T> = T | Record<keyof T, UnknownValue>;
491+
487492
const defangServiceProvider: pulumi.dynamic.ResourceProvider<
488493
DefangServiceInputs,
489494
DefangServiceOutputs
490495
> = {
491496
async check(
492497
olds: DefangServiceInputs,
493-
news: DefangServiceInputs
498+
inputs: DefangServiceInputs
494499
): Promise<pulumi.dynamic.CheckResult<DefangServiceInputs>> {
500+
const news = inputs as UnknownInputs<DefangServiceInputs>;
495501
const failures: pulumi.dynamic.CheckFailure[] = [];
496502

497503
if (!news.fabricDNS) {
@@ -501,7 +507,7 @@ const defangServiceProvider: pulumi.dynamic.ResourceProvider<
501507
if (!news.name) {
502508
failures.push({ property: "name", reason: "name is required" });
503509
}
504-
if (news.deploy) {
510+
if (news.deploy && news.deploy !== UNKNOWN) {
505511
if (!isValidUint(news.deploy.replicas ?? 0)) {
506512
failures.push({
507513
property: "deploy",
@@ -521,54 +527,59 @@ const defangServiceProvider: pulumi.dynamic.ResourceProvider<
521527
});
522528
}
523529
}
524-
if (!news.deploy?.resources?.reservations?.memory) {
530+
if (
531+
news.deploy !== UNKNOWN &&
532+
!news.deploy?.resources?.reservations?.memory
533+
) {
525534
pulumi.log.warn(
526535
"missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors"
527536
);
528537
}
529-
for (const port of news.ports || []) {
530-
// port.protocol = port.protocol || "tcp"; TODO: should we set defaults here?
531-
if (
532-
port.target < 1 ||
533-
port.target > 32767 ||
534-
!Number.isInteger(port.target)
535-
) {
536-
failures.push({
537-
property: "ports",
538-
reason: "target port must be an integer between 1 and 32767",
539-
});
540-
}
541-
if (port.mode === "ingress") {
542-
if (["udp", "tcp"].includes(port.protocol!)) {
538+
if (news.ports !== UNKNOWN) {
539+
for (const port of news.ports ?? []) {
540+
// port.protocol = port.protocol || "tcp"; TODO: should we set defaults here?
541+
if (
542+
port.target < 1 ||
543+
port.target > 32767 ||
544+
!Number.isInteger(port.target)
545+
) {
543546
failures.push({
544547
property: "ports",
545-
reason: "ingress is not support by protocol " + port.protocol,
548+
reason: "target port must be an integer between 1 and 32767",
546549
});
547550
}
548-
if (!news.healthcheck?.test) {
549-
pulumi.log.warn(
550-
"ingress port without healthcheck defaults to GET / HTTP/1.1"
551-
);
552-
}
553-
}
554-
if (news.healthcheck) {
555-
if (!isOptionalIntGt0(news.healthcheck.interval)) {
556-
failures.push({
557-
property: "healthcheck.interval",
558-
reason: "interval must be an integer > 0",
559-
});
560-
}
561-
if (!isOptionalIntGt0(news.healthcheck.timeout)) {
562-
failures.push({
563-
property: "healthcheck.timeout",
564-
reason: "timeout must be an integer > 0",
565-
});
551+
if (port.mode === "ingress") {
552+
if (["udp", "tcp"].includes(port.protocol!)) {
553+
failures.push({
554+
property: "ports",
555+
reason: "ingress is not support by protocol " + port.protocol,
556+
});
557+
}
558+
if (!news.healthcheck?.test) {
559+
pulumi.log.warn(
560+
"ingress port without healthcheck defaults to GET / HTTP/1.1"
561+
);
562+
}
566563
}
567-
if (!isOptionalIntGt0(news.healthcheck.retries)) {
568-
failures.push({
569-
property: "healthcheck.retries",
570-
reason: "retries must be an integer > 0",
571-
});
564+
if (news.healthcheck) {
565+
if (!isOptionalIntGt0(news.healthcheck.interval)) {
566+
failures.push({
567+
property: "healthcheck.interval",
568+
reason: "interval must be an integer > 0",
569+
});
570+
}
571+
if (!isOptionalIntGt0(news.healthcheck.timeout)) {
572+
failures.push({
573+
property: "healthcheck.timeout",
574+
reason: "timeout must be an integer > 0",
575+
});
576+
}
577+
if (!isOptionalIntGt0(news.healthcheck.retries)) {
578+
failures.push({
579+
property: "healthcheck.retries",
580+
reason: "retries must be an integer > 0",
581+
});
582+
}
572583
}
573584
}
574585
}
@@ -578,13 +589,15 @@ const defangServiceProvider: pulumi.dynamic.ResourceProvider<
578589
reason: "only one network can be specified",
579590
});
580591
}
581-
for (const secret of news.secrets || []) {
582-
// TODO: validate source name
583-
if (!secret.source) {
584-
failures.push({
585-
property: "secrets",
586-
reason: "secret source is required",
587-
});
592+
if (news.secrets !== UNKNOWN) {
593+
for (const secret of news.secrets || []) {
594+
// TODO: validate source name
595+
if (!secret.source) {
596+
failures.push({
597+
property: "secrets",
598+
reason: "secret source is required",
599+
});
600+
}
588601
}
589602
}
590603
if (news.build) {
@@ -594,34 +607,36 @@ const defangServiceProvider: pulumi.dynamic.ResourceProvider<
594607
reason: "cannot specify both build and image",
595608
});
596609
}
597-
if (!news.build.context) {
598-
failures.push({
599-
property: "build.context",
600-
reason: "build context is required",
601-
});
602-
}
603-
if (news.build.dockerfile === "") {
604-
failures.push({
605-
property: "build.dockerfile",
606-
reason: "dockerfile cannot be empty string",
607-
});
608-
}
609-
if (!isOptionalFloatGt0(news.build.shmSize)) {
610-
failures.push({
611-
property: "build.shmSize",
612-
reason: "shmSize must be an integer > 0",
613-
});
614-
}
615-
if (news.build.target === "") {
616-
failures.push({
617-
property: "build.target",
618-
reason: "target cannot be empty string",
619-
});
610+
if (news.build !== UNKNOWN) {
611+
if (!news.build.context) {
612+
failures.push({
613+
property: "build.context",
614+
reason: "build context is required",
615+
});
616+
}
617+
if (news.build.dockerfile === "") {
618+
failures.push({
619+
property: "build.dockerfile",
620+
reason: "dockerfile cannot be empty string",
621+
});
622+
}
623+
if (!isOptionalFloatGt0(news.build.shmSize)) {
624+
failures.push({
625+
property: "build.shmSize",
626+
reason: "shmSize must be an integer > 0",
627+
});
628+
}
629+
if (news.build.target === "") {
630+
failures.push({
631+
property: "build.target",
632+
reason: "target cannot be empty string",
633+
});
634+
}
620635
}
621636
} else if (!news.image) {
622637
failures.push({ property: "image", reason: "image is required" });
623638
}
624-
return { inputs: news, failures };
639+
return { inputs, failures };
625640
},
626641
async create(
627642
inputs: DefangServiceInputs

0 commit comments

Comments
 (0)