From e77a7cbbfd8911ac6d25f7111fbae47a3f449e5f Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 13 May 2024 13:55:08 +0300 Subject: [PATCH] improve readiness check --- containers/containers-with-readiness.test.w | 32 ++ ...iners-with-readiness.test.w.tf-aws.snap.md | 332 ++++++++++++++++++ containers/containers.test.w | 18 - containers/containers.test.w.tf-aws.snap.md | 45 +-- containers/test.sh | 9 +- containers/workload.sim.w | 24 +- containers/workload.w | 11 + 7 files changed, 404 insertions(+), 67 deletions(-) create mode 100644 containers/containers-with-readiness.test.w create mode 100644 containers/containers-with-readiness.test.w.tf-aws.snap.md diff --git a/containers/containers-with-readiness.test.w b/containers/containers-with-readiness.test.w new file mode 100644 index 00000000..8fc09d0a --- /dev/null +++ b/containers/containers-with-readiness.test.w @@ -0,0 +1,32 @@ +bring "./workload.w" as containers; +bring expect; +bring http; + +let message = "hello, wing change!!"; + +let hello = new containers.Workload( + name: "hello", + image: "paulbouwer/hello-kubernetes:1", + port: 8080, + readiness: "/", + replicas: 2, + env: { + "MESSAGE" => message, + }, + public: true, +) as "hello"; + + +let httpGet = inflight (url: str?): str => { + if let url = url { + return http.get(url).body; + } + + throw "no body"; +}; + +test "access public url" { + let helloBody = httpGet(hello.publicUrl); + log(helloBody); + assert(helloBody.contains(message)); +} diff --git a/containers/containers-with-readiness.test.w.tf-aws.snap.md b/containers/containers-with-readiness.test.w.tf-aws.snap.md new file mode 100644 index 00000000..5efd269d --- /dev/null +++ b/containers/containers-with-readiness.test.w.tf-aws.snap.md @@ -0,0 +1,332 @@ +# `containers-with-readiness.test.w.tf-aws.snap.md` + +## main.tf.json + +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.20.3" + }, + "outputs": { + "root": { + "Default": { + "Default": { + "WingEksCluster": { + "eks.certificate": "WingEksCluster_ekscertificate_183C7367", + "eks.cluster_name": "WingEksCluster_ekscluster_name_E1D79024", + "eks.endpoint": "WingEksCluster_eksendpoint_FD8710BA" + } + } + } + } + } + }, + "data": { + "aws_availability_zones": { + "WingEksCluster_Vpc_DataAwsAvailabilityZones_088D4D6B": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/Vpc/DataAwsAvailabilityZones", + "uniqueId": "WingEksCluster_Vpc_DataAwsAvailabilityZones_088D4D6B" + } + }, + "filter": { + "name": "opt-in-status", + "values": [ + "opt-in-not-required" + ] + } + } + }, + "aws_caller_identity": { + "WingAwsUtil_DataAwsCallerIdentity_E989AAD9": { + "//": { + "metadata": { + "path": "root/Default/Default/WingAwsUtil/DataAwsCallerIdentity", + "uniqueId": "WingAwsUtil_DataAwsCallerIdentity_E989AAD9" + } + } + } + }, + "aws_region": { + "WingAwsUtil_DataAwsRegion_EEBA70AA": { + "//": { + "metadata": { + "path": "root/Default/Default/WingAwsUtil/DataAwsRegion", + "uniqueId": "WingAwsUtil_DataAwsRegion_EEBA70AA" + } + } + } + }, + "kubernetes_ingress_v1": { + "hello_DataKubernetesIngressV1_7138FD92": { + "//": { + "metadata": { + "path": "root/Default/Default/hello/hello/DataKubernetesIngressV1", + "uniqueId": "hello_DataKubernetesIngressV1_7138FD92" + } + }, + "depends_on": [ + "helm_release.hello_Release_00E5935C" + ], + "metadata": { + "name": "hello" + }, + "provider": "kubernetes" + } + } + }, + "module": { + "WingEksCluster_Vpc_TerraformHclModule_708526D4": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/Vpc/TerraformHclModule", + "uniqueId": "WingEksCluster_Vpc_TerraformHclModule_708526D4" + } + }, + "azs": "${slice(data.aws_availability_zones.WingEksCluster_Vpc_DataAwsAvailabilityZones_088D4D6B.names, 0, 3)}", + "cidr": "10.0.0.0/16", + "enable_dns_hostnames": true, + "enable_nat_gateway": true, + "private_subnet_tags": { + "kubernetes.io/cluster/wing-eks-c888f0": "shared", + "kubernetes.io/role/internal-elb": "1" + }, + "private_subnets": [ + "10.0.1.0/24", + "10.0.2.0/24", + "10.0.3.0/24" + ], + "public_subnet_tags": { + "kubernetes.io/cluster/wing-eks-c888f0": "shared", + "kubernetes.io/role/elb": "1" + }, + "public_subnets": [ + "10.0.4.0/24", + "10.0.5.0/24", + "10.0.6.0/24" + ], + "single_nat_gateway": true, + "source": "terraform-aws-modules/vpc/aws", + "version": "4.0.2" + }, + "WingEksCluster_eks_B53CDB45": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/eks", + "uniqueId": "WingEksCluster_eks_B53CDB45" + } + }, + "cluster_addons": { + "coredns": { + "configuration_values": "${jsonencode({\"computeType\" = \"Fargate\"})}" + }, + "kube-proxy": { + }, + "vpc-cni": { + } + }, + "cluster_endpoint_public_access": true, + "cluster_name": "wing-eks-c888f0", + "cluster_version": "1.27", + "create_cluster_security_group": false, + "create_node_security_group": false, + "fargate_profiles": { + "default": { + "name": "default", + "selectors": [ + { + "namespace": "kube-system" + }, + { + "namespace": "default" + } + ] + } + }, + "source": "terraform-aws-modules/eks/aws", + "subnet_ids": "${module.WingEksCluster_Vpc_TerraformHclModule_708526D4.private_subnets}", + "version": "19.17.1", + "vpc_id": "${module.WingEksCluster_Vpc_TerraformHclModule_708526D4.vpc_id}" + }, + "WingEksCluster_lb_role_271BFF6C": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/lb_role", + "uniqueId": "WingEksCluster_lb_role_271BFF6C" + } + }, + "attach_load_balancer_controller_policy": true, + "oidc_providers": { + "main": { + "namespace_service_accounts": [ + "kube-system:aws-load-balancer-controller" + ], + "provider_arn": "${module.WingEksCluster_eks_B53CDB45.oidc_provider_arn}" + } + }, + "role_name": "eks-lb-role-c8c8413959d4dc713edd195c49a192ea57f59d59fd", + "source": "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + } + }, + "output": { + "WingEksCluster_ekscertificate_183C7367": { + "description": "eks.certificate", + "value": "${module.WingEksCluster_eks_B53CDB45.cluster_certificate_authority_data}" + }, + "WingEksCluster_ekscluster_name_E1D79024": { + "description": "eks.cluster_name", + "value": "wing-eks-c888f0" + }, + "WingEksCluster_eksendpoint_FD8710BA": { + "description": "eks.endpoint", + "value": "${module.WingEksCluster_eks_B53CDB45.cluster_endpoint}" + } + }, + "provider": { + "aws": [ + { + } + ], + "helm": [ + { + "kubernetes": { + "cluster_ca_certificate": "${base64decode(module.WingEksCluster_eks_B53CDB45.cluster_certificate_authority_data)}", + "exec": { + "api_version": "client.authentication.k8s.io/v1beta1", + "args": [ + "eks", + "get-token", + "--cluster-name", + "wing-eks-c888f0" + ], + "command": "aws" + }, + "host": "${module.WingEksCluster_eks_B53CDB45.cluster_endpoint}" + } + } + ], + "kubernetes": [ + { + "cluster_ca_certificate": "${base64decode(module.WingEksCluster_eks_B53CDB45.cluster_certificate_authority_data)}", + "exec": { + "api_version": "client.authentication.k8s.io/v1beta1", + "args": [ + "eks", + "get-token", + "--cluster-name", + "wing-eks-c888f0" + ], + "command": "aws" + }, + "host": "${module.WingEksCluster_eks_B53CDB45.cluster_endpoint}" + } + ] + }, + "resource": { + "helm_release": { + "WingEksCluster_Release_AE9D9364": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/Release", + "uniqueId": "WingEksCluster_Release_AE9D9364" + } + }, + "chart": "aws-load-balancer-controller", + "depends_on": [ + "kubernetes_service_account.WingEksCluster_ServiceAccount_580F8592" + ], + "name": "aws-load-balancer-controller", + "namespace": "kube-system", + "provider": "helm", + "repository": "https://aws.github.io/eks-charts", + "set": [ + { + "name": "region", + "value": "${data.aws_region.WingAwsUtil_DataAwsRegion_EEBA70AA.name}" + }, + { + "name": "vpcId", + "value": "${module.WingEksCluster_Vpc_TerraformHclModule_708526D4.vpc_id}" + }, + { + "name": "serviceAccount.create", + "value": "false" + }, + { + "name": "serviceAccount.name", + "value": "aws-load-balancer-controller" + }, + { + "name": "clusterName", + "value": "wing-eks-c888f0" + } + ] + }, + "hello_Release_00E5935C": { + "//": { + "metadata": { + "path": "root/Default/Default/hello/hello/Release", + "uniqueId": "hello_Release_00E5935C" + } + }, + "chart": ".wing/helm/hello-2dc5ca0312851a277e2c2334138c2f4a", + "depends_on": [ + ], + "name": "hello", + "provider": "helm", + "values": [ + "image: paulbouwer/hello-kubernetes:1" + ] + } + }, + "kubernetes_service_account": { + "WingEksCluster_ServiceAccount_580F8592": { + "//": { + "metadata": { + "path": "root/Default/Default/WingEksCluster/ServiceAccount", + "uniqueId": "WingEksCluster_ServiceAccount_580F8592" + } + }, + "metadata": { + "annotations": { + "eks.amazonaws.com/role-arn": "${module.WingEksCluster_lb_role_271BFF6C.iam_role_arn}", + "eks.amazonaws.com/sts-regional-endpoints": "true" + }, + "labels": { + "app.kubernetes.io/component": "controller", + "app.kubernetes.io/name": "aws-load-balancer-controller" + }, + "name": "aws-load-balancer-controller", + "namespace": "kube-system" + }, + "provider": "kubernetes" + } + } + }, + "terraform": { + "backend": { + "local": { + "path": "./terraform.tfstate" + } + }, + "required_providers": { + "aws": { + "source": "aws", + "version": "5.31.0" + }, + "helm": { + "source": "helm", + "version": "2.12.1" + }, + "kubernetes": { + "source": "kubernetes", + "version": "2.27.0" + } + } + } +} +``` diff --git a/containers/containers.test.w b/containers/containers.test.w index 0b01a385..6aaddb36 100644 --- a/containers/containers.test.w +++ b/containers/containers.test.w @@ -2,20 +2,6 @@ bring "./workload.w" as containers; bring expect; bring http; -let message = "hello, wing change!!"; - -let hello = new containers.Workload( - name: "hello", - image: "paulbouwer/hello-kubernetes:1", - port: 8080, - readiness: "/", - replicas: 2, - env: { - "MESSAGE" => message, - }, - public: true, -) as "hello"; - let echo = new containers.Workload( name: "http-echo", image: "hashicorp/http-echo", @@ -34,10 +20,6 @@ let httpGet = inflight (url: str?): str => { }; test "access public url" { - let helloBody = httpGet(hello.publicUrl); - log(helloBody); - assert(helloBody.contains(message)); - let echoBody = httpGet(echo.publicUrl); assert(echoBody.contains("hello1234")); } diff --git a/containers/containers.test.w.tf-aws.snap.md b/containers/containers.test.w.tf-aws.snap.md index 12013359..5ec621f6 100644 --- a/containers/containers.test.w.tf-aws.snap.md +++ b/containers/containers.test.w.tf-aws.snap.md @@ -62,21 +62,6 @@ } }, "kubernetes_ingress_v1": { - "hello_DataKubernetesIngressV1_7138FD92": { - "//": { - "metadata": { - "path": "root/Default/Default/hello/hello/DataKubernetesIngressV1", - "uniqueId": "hello_DataKubernetesIngressV1_7138FD92" - } - }, - "depends_on": [ - "helm_release.hello_Release_00E5935C" - ], - "metadata": { - "name": "hello" - }, - "provider": "kubernetes" - }, "http-echo_DataKubernetesIngressV1_14943683": { "//": { "metadata": { @@ -107,7 +92,7 @@ "enable_dns_hostnames": true, "enable_nat_gateway": true, "private_subnet_tags": { - "kubernetes.io/cluster/wing-eks-c888f0": "shared", + "kubernetes.io/cluster/wing-eks-c81852": "shared", "kubernetes.io/role/internal-elb": "1" }, "private_subnets": [ @@ -116,7 +101,7 @@ "10.0.3.0/24" ], "public_subnet_tags": { - "kubernetes.io/cluster/wing-eks-c888f0": "shared", + "kubernetes.io/cluster/wing-eks-c81852": "shared", "kubernetes.io/role/elb": "1" }, "public_subnets": [ @@ -145,7 +130,7 @@ } }, "cluster_endpoint_public_access": true, - "cluster_name": "wing-eks-c888f0", + "cluster_name": "wing-eks-c81852", "cluster_version": "1.27", "create_cluster_security_group": false, "create_node_security_group": false, @@ -194,7 +179,7 @@ }, "WingEksCluster_ekscluster_name_E1D79024": { "description": "eks.cluster_name", - "value": "wing-eks-c888f0" + "value": "wing-eks-c81852" }, "WingEksCluster_eksendpoint_FD8710BA": { "description": "eks.endpoint", @@ -216,7 +201,7 @@ "eks", "get-token", "--cluster-name", - "wing-eks-c888f0" + "wing-eks-c81852" ], "command": "aws" }, @@ -233,7 +218,7 @@ "eks", "get-token", "--cluster-name", - "wing-eks-c888f0" + "wing-eks-c81852" ], "command": "aws" }, @@ -277,26 +262,10 @@ }, { "name": "clusterName", - "value": "wing-eks-c888f0" + "value": "wing-eks-c81852" } ] }, - "hello_Release_00E5935C": { - "//": { - "metadata": { - "path": "root/Default/Default/hello/hello/Release", - "uniqueId": "hello_Release_00E5935C" - } - }, - "chart": ".wing/helm/hello-2dc5ca0312851a277e2c2334138c2f4a", - "depends_on": [ - ], - "name": "hello", - "provider": "helm", - "values": [ - "image: paulbouwer/hello-kubernetes:1" - ] - }, "http-echo_Release_2E4AC011": { "//": { "metadata": { diff --git a/containers/test.sh b/containers/test.sh index d75ccb9b..7298891c 100755 --- a/containers/test.sh +++ b/containers/test.sh @@ -1,3 +1,10 @@ #!/bin/sh +if [ -n "$CI" ]; then + snapshot_mode="assert" +else + snapshot_mode="update" +fi + DEBUG=1 wing test -wing test -t tf-aws -s assert containers.test.w +wing test -t tf-aws -s $snapshot_mode containers.test.w +wing test -t tf-aws -s $snapshot_mode containers-with-readiness.test.w diff --git a/containers/workload.sim.w b/containers/workload.sim.w index 0a4813c9..2c564e84 100644 --- a/containers/workload.sim.w +++ b/containers/workload.sim.w @@ -36,27 +36,31 @@ pub class Workload_sim { return this.publicUrl!; }); - let s1 = new cloud.Service(inflight () => { - state.set(publicUrlKey, "http://localhost:{c.hostPort!}"); - state.set(internalUrlKey, "http://host.docker.internal:{c.hostPort!}"); - }) as "urls"; + let readiness = new cloud.Service(inflight () => { + let publicUrl = "http://localhost:{c.hostPort!}"; - let s2 = new cloud.Service(inflight () => { if let readiness = props.readiness { - let readinessUrl = "{this.publicUrl!}{readiness}"; - log("waiting for container to be ready: {readinessUrl}..."); + // if we have a readiness check, wait for the container to be ready + let readinessUrl = "{publicUrl}{readiness}"; + util.waitUntil(inflight () => { try { + log("Readiness check: GET {readinessUrl}"); return http.get(readinessUrl).ok; } catch { return false; } - }, interval: 0.1s); + }, interval: 0.5s); + + log("Container is ready!"); } + + // ready! + state.set(publicUrlKey, publicUrl); + state.set(internalUrlKey, "http://host.docker.internal:{c.hostPort!}"); }) as "readiness"; - nodeof(s1).hidden = true; - nodeof(s2).hidden = true; + nodeof(readiness).hidden = true; } } diff --git a/containers/workload.w b/containers/workload.w index b4017723..809a6b8c 100644 --- a/containers/workload.w +++ b/containers/workload.w @@ -5,6 +5,7 @@ bring "./api.w" as api; bring "./helm.w" as helm; bring http; bring fs; +bring ui; pub class Workload { /** internal url, `nil` if there is no exposed port */ @@ -20,6 +21,16 @@ pub class Workload { let w = new sim.Workload_sim(props) as props.name; this.internalUrl = w.internalUrl; this.publicUrl = w.publicUrl; + nodeof(w).hidden = true; + + if let url = w.internalUrl { + new ui.ValueField("Internal URL", url) as "internal_url"; + } + + if let url = w.publicUrl { + new ui.ValueField("Public URL", url) as "public_url"; + } + return this; }