Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
singleton
Browse files Browse the repository at this point in the history
  • Loading branch information
eladb committed Oct 9, 2023
1 parent decdc2a commit 1f9569b
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 93 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ cluster.

See [Captain's Log](https://winglang.slack.com/archives/C047QFSUL5R/p1696868156845019) in the [Wing Slack](https://t.winglang.io).

- [x] EKS as a singleton
- [ ] Publish the library
- [ ] Implement `start()` and `stop()` and `url()`.
- [ ] Add support for local Dockerfiles (currently only images from Docker Hub are supported), this
includes publishing into an ECR.
includes publishing into an ECR.
- [ ] Add support for sidecar containers
- [ ] Domains
- [ ] SSL
- [ ] Add support for ingress routes (currently all routes go to the container).
- [ ] Use Fargate profiles in EKS instead of managed node groups.
- [ ] What happens if I deploy more than one app into the cluster? Add support for ingress routes
(currently all routes go to the container).
- [ ] Nodes - what should we do there? Use Fargate profiles in EKS instead of managed node groups?
- [ ] Open bugs
- [ ] Allow referencing an existing EKS cluster.

Expand Down
1 change: 1 addition & 0 deletions api.w
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct ContainerOpts {
port: num?;
env: Map<str>?;
readiness: str?; // http get
replicas: num?;
}

struct WorkloadProps extends ContainerOpts {
Expand Down
4 changes: 2 additions & 2 deletions containers.w
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class Workload impl api.IWorkload {
let target = util.env("WING_TARGET");

if target == "sim" {
this.inner = new sim.Workload(props);
this.inner = new sim.Workload(props) as this.node.id;
} elif target == "tf-aws" {
this.inner = new aws.Workload(props);
this.inner = new aws.Workload(props) as this.node.id;
} else {
throw "unsupported target ${target}";
}
Expand Down
16 changes: 15 additions & 1 deletion test/containers.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@ let hello = new containers.Workload(
image: "paulbouwer/hello-kubernetes:1",
port: 8080,
readiness: "/",
replicas: 4,
env: {
"MESSAGE" => message,
}
);
) as "hello";

// new containers.Workload(
// image: "gcr.io/google-samples/gb-frontend:v4",
// env: {
// "GET_HOSTS_FROM" => "dns",
// },
// port: 80,
// ) as "guestbook";

// new containers.Workload(
// image: "registry.k8s.io/redis:e2e",
// port: 6379,
// ) as "redis";

let getBody = inflight (): str? => {
if let url = hello.url() {
Expand Down
22 changes: 7 additions & 15 deletions test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const cdk8s = require('cdk8s');
const fs = require('fs');
const os = require('os');
const path = require('path');
const tfaws = require('@winglang/sdk/lib/target-tf-aws');
const cdktf = require('cdktf');

exports.shell = async function (command, args, cwd) {
return new Promise((resolve, reject) => {
Expand All @@ -24,13 +24,14 @@ exports.entrypointDir = function (scope) {

exports.toHelmChart = function(chart) {
const app = cdk8s.App.of(chart);

app.resolvers = [new cdk8s.LazyResolver(), new cdk8s.ImplicitTokenResolver(), new cdk8s.NumberStringUnionResolver()];
const docs = cdk8s.App._synthChart(chart);
const yaml = cdk8s.Yaml.stringify(...docs);

const workdir = fs.mkdtempSync(path.join(os.tmpdir(), "helm."));
const templates = fs.mkdirSync(path.join(workdir, "templates"), {recursive: true});

const yaml = cdk8s.Yaml.stringify(...docs);
const templates = path.join(workdir, "templates");
fs.mkdirSync(templates, {recursive: true});
fs.writeFileSync(path.join(templates, "all.yaml"), yaml);

const manifest = {
Expand All @@ -47,14 +48,5 @@ exports.toHelmChart = function(chart) {
return workdir;
};

exports.awsRegion = function(scope) {
return tfaws.App.of(scope).region;
}

// exports.awsVpc = function(scope) {
// return tfaws.App.of(scope).vpc;
// }

// exports.toSubnet = function(scope) {
// return scope;
// }
exports.toEksCluster = x => x;
exports.toResource = x => x;
76 changes: 43 additions & 33 deletions tf-aws/eks.w
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
bring aws;
bring cloud;
bring "constructs" as c;
bring "cdktf" as cdktf;
bring "@cdktf/provider-aws" as tfaws;
bring "@cdktf/provider-helm" as helm4;
bring "@cdktf/provider-kubernetes" as kubernetes;
bring "./vpc.w" as v;
bring "./util.w" as util2;

class EksCluster {
/** singleton */
pub static getOrCreate(scope: std.IResource): EksCluster {
let stack = cdktf.TerraformStack.of(scope);
let uid = "WingEksCluster";
return EksCluster.toEksCluster(stack.node.tryFindChild(uid)) ?? new EksCluster() as uid in EksCluster.toResource(stack);
}

pub endpoint: str;
pub certificate: str;
pub name: str;
pub oidcProviderArn: str;

vpc: v.Vpc;

init() {
let clusterName = "wing-eks-${this.node.addr.substring(0, 6)}";

Expand All @@ -23,7 +34,7 @@ class EksCluster {
publicSubnetTags.set("kubernetes.io/role/elb", "1");
publicSubnetTags.set("kubernetes.io/cluster/${clusterName}", "shared");

let vpc = new v.Vpc(
this.vpc = new v.Vpc(
privateSubnetTags: privateSubnetTags.copy(),
publicSubnetTags: publicSubnetTags.copy(),
);
Expand All @@ -34,33 +45,29 @@ class EksCluster {
variables: {
cluster_name: clusterName,

vpc_id: vpc.id,
subnet_ids: vpc.privateSubnets,
vpc_id: this.vpc.id,
subnet_ids: this.vpc.privateSubnets,
cluster_endpoint_public_access: true,
// create_aws_auth_configmap: true,
// manage_aws_auth_configmap: true,
eks_managed_node_group_defaults: {
ami_type: "AL2_x86_64"
},
eks_managed_node_groups: {
one: {
small: {
name: "node-group-1",
instance_types: ["t3.small"],
min_size: 1,
max_size: 3,
desired_size: 2
},
two: {
name: "node-group-2",
instance_types: ["t3.small"],
min_size: 1,
max_size: 2,
desired_size: 1,
max_size: 10,
desired_size: 5
},
}
}
) as "eks";

this.name = clusterName;
this.certificate = eks.get("cluster_certificate_authority_data");
this.endpoint = eks.get("cluster_endpoint");
this.oidcProviderArn = eks.get("oidc_provider_arn");

let ebsCsiPolicy = new tfaws.dataAwsIamPolicy.DataAwsIamPolicy(arn: "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy");

let irsaEbsCsi = new cdktf.TerraformHclModule(
Expand All @@ -86,11 +93,8 @@ class EksCluster {
},
);

this.name = clusterName;
this.certificate = eks.get("cluster_certificate_authority_data");
this.endpoint = eks.get("cluster_endpoint");
this.oidcProviderArn = eks.get("oidc_provider_arn");

// setup the helm and k8s terraform providers
let k8sconfig = {
host: this.endpoint,
clusterCaCertificate: cdktf.Fn.base64decode(this.certificate),
Expand All @@ -104,6 +108,22 @@ class EksCluster {
new helm4.provider.HelmProvider(kubernetes: k8sconfig);
new kubernetes.provider.KubernetesProvider(k8sconfig);

// output the cluster name
new cdktf.TerraformOutput(value: clusterName);

// install the LB controller to support ingress
this.addLoadBalancerController();
}

/**
* Deploys a Helm chart to the cluster.
*/
pub addChart(release: helm4.release.ReleaseConfig) {
new helm4.release.Release(release) as release.name;
}

addLoadBalancerController() {
let region = new tfaws.dataAwsRegion.DataAwsRegion();
let serviceAccountName = "aws-load-balancer-controller";
let lbRole = new cdktf.TerraformHclModule(
source: "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks",
Expand Down Expand Up @@ -134,32 +154,22 @@ class EksCluster {
}
);

new cdktf.TerraformOutput(value: clusterName);

this.addChart(
name: "aws-load-balancer-controller",
repository: "https://aws.github.io/eks-charts",
chart: "aws-load-balancer-controller",
namespace: "kube-system",
dependsOn: [serviceAccount],
set: [
{ name: "region", value: EksUtil.awsRegion(this) },
{ name: "vpcId", value: vpc.id },
{ name: "region", value: region.name },
{ name: "vpcId", value: this.vpc.id },
{ name: "serviceAccount.create", value: "false" },
{ name: "serviceAccount.name", value: serviceAccountName },
{ name: "clusterName", value: this.name },
]
);
}

/**
* Deploys a Helm chart to the cluster.
*/
pub addChart(release: helm4.release.ReleaseConfig) {
new helm4.release.Release(release) as release.name;
}
extern "./util.js" pub static toEksCluster(scope: c.IConstruct?): EksCluster?;
extern "./util.js" pub static toResource(scope: c.IConstruct): EksCluster;
}

class EksUtil {
extern "./util.js" pub static awsRegion(scope: std.IResource): str;
}
5 changes: 5 additions & 0 deletions tf-aws/util.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bring "cdk8s" as k8s9;

class Util {
extern "./util.js" pub static toHelmChart(chart: k8s9.Chart): str;
}
73 changes: 34 additions & 39 deletions tf-aws/workload.w
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,37 @@ bring "../api.w" as api;
bring "./eks.w" as eks;
bring "cdk8s-plus-27" as cdk8s;
bring "cdk8s" as k8s;
bring "cdktf" as cdktf3;
bring "./util.w" as util;

class _Chart extends k8s.Chart {
class Workload impl api.IWorkload {
init(props: api.WorkloadProps) {
let name = "${this.node.id.replace(".", "-")}-${this.node.addr.substring(0, 6)}";
let cluster = eks.EksCluster.getOrCreate(this);
let chart = new _Chart(name, props);
let helmDir = util.toHelmChart(chart);

cluster.addChart(
name: name,
chart: helmDir,
);
}

pub inflight start() {
throw "Not implemented yet";
}

pub inflight stop() {
throw "Not implemented yet";
}

pub inflight url(): str? {
throw "Not implemented yet";
}
}

class _Chart extends k8s.Chart {
init(name: str, props: api.WorkloadProps) {
let env = props.env ?? {};
let envVariables = MutMap<cdk8s.EnvValue>{};

Expand All @@ -27,9 +55,10 @@ class _Chart extends k8s.Chart {
}

let deployment = new cdk8s.Deployment(
replicas: props.replicas,
metadata: {
name: "deployment-${this.node.addr}"
}
name: name
},
);

deployment.addContainer(
Expand All @@ -43,53 +72,19 @@ class _Chart extends k8s.Chart {
);

let service = deployment.exposeViaService(
name: "service-${this.node.addr}",
name: name,
serviceType: cdk8s.ServiceType.NODE_PORT,
);

let ingress = new cdk8s.Ingress(
metadata: {
name: "ingress-${this.node.addr}",
name: name,
annotations: {
"kubernetes.io/ingress.class": "alb",
"alb.ingress.kubernetes.io/scheme": "internet-facing",
}
},
defaultBackend: cdk8s.IngressBackend.fromService(service),
);

// ingress.addRule("/", );
}
}

class Workload impl api.IWorkload {
init(props: api.WorkloadProps) {
let cluster = new eks.EksCluster();

let chart = new _Chart(props);
let helmDir = Util.toHelmChart(chart);

// log(helmDir);

cluster.addChart(
name: "app-${this.node.addr}",
chart: helmDir,
);
}

pub inflight start() {
throw "Not implemented yet";
}

pub inflight stop() {
throw "Not implemented yet";
}

pub inflight url(): str? {
throw "Not implemented yet";
}
}

class Util {
extern "./util.js" pub static toHelmChart(chart: k8s.Chart): str;
}

0 comments on commit 1f9569b

Please sign in to comment.