From 1d5e3ada2c56b6f453c27153b0638962a71aa7e5 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Wed, 18 Oct 2023 15:03:42 +0300 Subject: [PATCH] expose public url on aws --- api.w | 3 -- containers.w | 4 -- test/simple.test.w | 6 ++- tfaws/eks.w | 93 +++++++++++++++++++++++++++------------------- tfaws/workload.w | 35 ++++++++++++----- 5 files changed, 85 insertions(+), 56 deletions(-) diff --git a/api.w b/api.w index d30f72e..a0a7dfe 100644 --- a/api.w +++ b/api.w @@ -5,9 +5,6 @@ interface IWorkload extends std.IResource { /** stops the container */ inflight stop(): void; - /** if `port` is specified, this includes the external url of the container */ - inflight url(): str?; - /** internal url, `nil` if there is no exposed port */ getInternalUrl(): str?; diff --git a/containers.w b/containers.w index ba9c017..193891a 100644 --- a/containers.w +++ b/containers.w @@ -32,10 +32,6 @@ class Workload impl api.IWorkload { this.inner.stop(); } - pub inflight url(): str? { - return this.inner.url(); - } - pub getInternalUrl(): str? { return this.inner.getInternalUrl(); } diff --git a/test/simple.test.w b/test/simple.test.w index d73dfb3..2927edb 100644 --- a/test/simple.test.w +++ b/test/simple.test.w @@ -11,8 +11,12 @@ let app = new containers.Workload( args: ["-text=bang_bang"], ); +new cloud.Function(inflight () => { + log(app.publicUrl ?? "no public url yet"); +}) as "get public url"; + test "http get" { - if let url = app.url() { + if let url = app.publicUrl { let response = http.get(url); log(response.body ?? ""); if let body = response.body { diff --git a/tfaws/eks.w b/tfaws/eks.w index 9d5ed17..83bc7e2 100644 --- a/tfaws/eks.w +++ b/tfaws/eks.w @@ -17,45 +17,68 @@ struct ClusterAttributes { interface ICluster extends std.IResource { attributes(): ClusterAttributes; + kubernetesProvider(): eks_cdktf.TerraformProvider; + helmProvider(): eks_cdktf.TerraformProvider; } -class ClusterRef impl ICluster { - _attributes: ClusterAttributes; +class ClusterBase impl ICluster { + pub attributes(): ClusterAttributes { throw "Not implemented"; } - init(attributes: ClusterAttributes) { - this._attributes = attributes; - } + pub kubernetesProvider(): eks_cdktf.TerraformProvider { + let stack = eks_cdktf.TerraformStack.of(this); + let singletonKey = "WingKubernetesProvider"; + let attributes = this.attributes(); + let existing = stack.node.tryFindChild(singletonKey); + if existing? { + return unsafeCast(existing); + } - pub attributes(): ClusterAttributes { - return this._attributes; + // setup the "kubernetes" terraform provider + return new eks_kubernetes.provider.KubernetesProvider( + host: attributes.endpoint, + clusterCaCertificate: eks_cdktf.Fn.base64decode(attributes.certificate), + exec: { + apiVersion: "client.authentication.k8s.io/v1beta1", + args: ["eks", "get-token", "--cluster-name", attributes.name], + command: "aws", + } + ) as singletonKey in stack; } -} - -class HelmChart { - release: eks_helm.release.Release; - init(cluster: ICluster, release: eks_helm.release.ReleaseConfig) { + pub helmProvider(): eks_cdktf.TerraformProvider { let stack = eks_cdktf.TerraformStack.of(this); let singletonKey = "WingHelmProvider"; - let attributes = cluster.attributes(); + let attributes = this.attributes(); let existing = stack.node.tryFindChild(singletonKey); - if !existing? { - new eks_helm.provider.HelmProvider(kubernetes: { - host: attributes.endpoint, - clusterCaCertificate: eks_cdktf.Fn.base64decode(attributes.certificate), - exec: { - apiVersion: "client.authentication.k8s.io/v1beta1", - args: ["eks", "get-token", "--cluster-name", attributes.name], - command: "aws", - } - }) as singletonKey in stack; + if existing? { + return unsafeCast(existing); } - this.release = new eks_helm.release.Release(release) as release.name; + return new eks_helm.provider.HelmProvider(kubernetes: { + host: attributes.endpoint, + clusterCaCertificate: eks_cdktf.Fn.base64decode(attributes.certificate), + exec: { + apiVersion: "client.authentication.k8s.io/v1beta1", + args: ["eks", "get-token", "--cluster-name", attributes.name], + command: "aws", + } + }) as singletonKey in stack; + } +} + +class ClusterRef extends ClusterBase impl ICluster { + _attributes: ClusterAttributes; + + init(attributes: ClusterAttributes) { + this._attributes = attributes; + } + + pub attributes(): ClusterAttributes { + return this._attributes; } } -class Cluster impl ICluster { +class Cluster extends ClusterBase impl ICluster { /** singleton */ pub static getOrCreate(scope: std.IResource): ICluster { @@ -74,6 +97,7 @@ class Cluster impl ICluster { return existing ?? newCluster(); } + static tryGetClusterAttributes(): ClusterAttributes? { if !eks_values.has("eks.cluster_name") { return nil; @@ -146,17 +170,6 @@ class Cluster impl ICluster { this._oidcProviderArn = cluster.get("oidc_provider_arn"); - // setup the "kubernetes" terraform provider - new eks_kubernetes.provider.KubernetesProvider( - host: this._attributes.endpoint, - clusterCaCertificate: eks_cdktf.Fn.base64decode(this._attributes.certificate), - exec: { - apiVersion: "client.authentication.k8s.io/v1beta1", - args: ["eks", "get-token", "--cluster-name", this._attributes.name], - command: "aws", - } - ); - // output the cluster name new eks_cdktf.TerraformOutput(value: this._attributes.name, description: "eks.cluster_name") as "eks.cluster_name"; new eks_cdktf.TerraformOutput(value: this._attributes.certificate, description: "eks.certificate") as "eks.certificate"; @@ -188,7 +201,10 @@ class Cluster impl ICluster { } ) as "lb_role"; + // install the k8s terraform provider + let serviceAccount = new eks_kubernetes.serviceAccount.ServiceAccount( + provider: this.kubernetesProvider(), metadata: { name: serviceAccountName, namespace: "kube-system", @@ -202,8 +218,9 @@ class Cluster impl ICluster { }, } ); - - new HelmChart(this, + + new eks_helm.release.Release( + provider: this.helmProvider(), name: "aws-load-balancer-controller", repository: "https://aws.github.io/eks-charts", chart: "aws-load-balancer-controller", diff --git a/tfaws/workload.w b/tfaws/workload.w index 91416bd..4a090c9 100644 --- a/tfaws/workload.w +++ b/tfaws/workload.w @@ -5,9 +5,12 @@ bring "cdk8s" as workload_cdk8s; bring "cdktf" as workload_cdktf; bring "./ecr.w" as workload_ecr; bring "../utils.w" as workload_utils; +bring "@cdktf/provider-kubernetes" as workload_k8s; +bring "@cdktf/provider-helm" as workload_helm; class Workload impl workload_api.IWorkload { internalUrl: str?; + publicUrl: str?; init(props: workload_api.WorkloadProps) { let cluster = workload_eks.Cluster.getOrCreate(this); @@ -31,23 +34,39 @@ class Workload impl workload_api.IWorkload { } let chart = new _Chart(props); - let helmDir = chart.toHelm(); - let helm = new workload_eks.HelmChart( - cluster, + let helm = new workload_helm.release.Release( + provider: cluster.helmProvider(), dependsOn: deps.copy(), name: props.name, - chart: helmDir, + chart: chart.toHelm(), values: ["image: ${image}"], ); if let port = props.port { this.internalUrl = "http://${props.name}:${props.port}"; - } else { - this.internalUrl = nil; + } + + // if "public" is set, lookup the address from the ingress resource created by the helm chart + // and assign to `publicUrl`. + if props.public ?? false { + let ingress = new workload_k8s.dataKubernetesIngressV1.DataKubernetesIngressV1( + provider: cluster.kubernetesProvider(), + dependsOn: [helm], + metadata: { + name: props.name + } + ); + + let hostname = ingress.status.get(0).loadBalancer.get(0).ingress.get(0).hostname; + this.publicUrl = "http://${hostname}"; } } + pub getPublicUrl(): str? { + return this.publicUrl; + } + pub getInternalUrl(): str? { return this.internalUrl; } @@ -59,10 +78,6 @@ class Workload impl workload_api.IWorkload { pub inflight stop() { throw "Not implemented yet"; } - - pub inflight url(): str? { - throw "Not implemented yet"; - } } class _Chart extends workload_cdk8s.Chart {