├── .prettierignore ├── .prettierrc.json ├── cdk.json ├── Dev-EKS.png ├── extras ├── cdk.json ├── release.sh ├── README.md ├── ec2-user-script.sh ├── primehub-ce-starter-cloudformation.yaml └── primehub-starter-cloudformation.yaml ├── docs ├── imgs │ ├── aws-console-login.png │ ├── aws-console-cdk-delete.png │ ├── aws-console-cloudformation.png │ ├── aws-console-eks-cdk-stack.png │ └── aws-console-select-region.png └── destroy-cluster.md ├── .editorconfig ├── FAQ.md ├── package.json ├── .github └── workflows │ ├── deploy-test.yaml │ └── release.yaml ├── lib ├── nvidia-device-plugin.ts ├── aws-efs-csi-driver.ts ├── cert-manager.ts ├── nginx-ingress.ts ├── aws-loadbalancer-controller.ts ├── cluster-autoscaler.ts ├── primehub.ts └── eks-cluster-Stack.ts ├── README.md ├── bin └── index.ts ├── Administration.md ├── .gitignore ├── connect ├── deploy ├── LICENSE └── yarn.lock /.prettierignore: -------------------------------------------------------------------------------- 1 | cdk.out 2 | *.md 3 | **.md 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/index.ts" 3 | } 4 | -------------------------------------------------------------------------------- /Dev-EKS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/Dev-EKS.png -------------------------------------------------------------------------------- /extras/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/index.ts", 3 | "requireApproval": "never" 4 | } 5 | -------------------------------------------------------------------------------- /docs/imgs/aws-console-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/docs/imgs/aws-console-login.png -------------------------------------------------------------------------------- /docs/imgs/aws-console-cdk-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/docs/imgs/aws-console-cdk-delete.png -------------------------------------------------------------------------------- /docs/imgs/aws-console-cloudformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/docs/imgs/aws-console-cloudformation.png -------------------------------------------------------------------------------- /docs/imgs/aws-console-eks-cdk-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/docs/imgs/aws-console-eks-cdk-stack.png -------------------------------------------------------------------------------- /docs/imgs/aws-console-select-region.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myelintek/primehub-aws-cdk/HEAD/docs/imgs/aws-console-select-region.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{Makefile,Makefile.*}] 12 | indent_style = tab 13 | 14 | [*.{py,rst}] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | [*.{md,markdown}] 19 | trim_trailing_whitespace = false 20 | 21 | [*.json] 22 | indent_size = 2 23 | insert_final_newline = ignore 24 | 25 | [**.min.js] 26 | indent_style = ignore 27 | insert_final_newline = ignore 28 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### Q: How much does a license cost? 4 | 5 | We provide a starter plan with 999 USD/month with 3 active users. Please [contact us](https://www.infuseai.io/company/contactus) about your need. 6 | 7 | ### Q: Does the license pricing includes AWS costs? 8 | 9 | No. Check your AWS billing system for the cost of your infrastructure. 10 | 11 | ### Q: What's the minimum resources required by PrimeHub 1-Click 12 | 13 | By default, PrimeHub 1-click requires **1*t3.xlarge EC2 instance** and **1*EKS cluster**. 14 | 15 | ### Q: What's the estimated cost for the minimum require requirement? 16 | 17 | About USD$200 per month. 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eks-cdk", 3 | "version": "1.0.0", 4 | "description": "Basic PrimeHub ready EKS cluster", 5 | "private": true, 6 | "scripts": { 7 | "build": "tsc bin/index.ts", 8 | "watch": "tsc -w bin/index.ts", 9 | "cdk": "cdk" 10 | }, 11 | "author": { 12 | "name": "Kent Huang", 13 | "url": "https://infuseai.io", 14 | "organization": true 15 | }, 16 | "license": "Apache-2.0", 17 | "devDependencies": { 18 | "@types/node": "^10.17.0", 19 | "typescript": "~3.7.2" 20 | }, 21 | "dependencies": { 22 | "@aws-cdk/aws-autoscaling": "1.138.2", 23 | "@aws-cdk/aws-cloudfront": "1.138.2", 24 | "@aws-cdk/aws-cloudfront-origins": "1.138.2", 25 | "@aws-cdk/aws-ec2": "1.138.2", 26 | "@aws-cdk/aws-efs": "1.138.2", 27 | "@aws-cdk/aws-eks": "1.138.2", 28 | "@aws-cdk/aws-iam": "1.138.2", 29 | "@aws-cdk/aws-s3": "1.138.2", 30 | "@aws-cdk/aws-s3-deployment": "1.138.2", 31 | "@aws-cdk/core": "1.138.2", 32 | "aws-cdk": "1.138.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extras/release.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" 4 | GIT_TAG=${1:-'cfTemplate'} 5 | 6 | echo "[Release] primehub-aws-cdk ${GIT_TAG}" 7 | 8 | echo "[Patch] Cloudformation template with tag '${GIT_TAG}'" 9 | extension='' 10 | if [[ "$(uname)" == "Darwin" ]]; then 11 | extension=".bak" 12 | fi 13 | sed -i${extension} "s/cfTemplate/${GIT_TAG}/g" ${DIR}/primehub-starter-cloudformation.yaml 14 | sed -i${extension} "s/cfTemplate/${GIT_TAG}/g" ${DIR}/primehub-ce-starter-cloudformation.yaml 15 | 16 | echo "[S3] Upload cloudformation template" 17 | aws s3 cp ${DIR}/primehub-starter-cloudformation.yaml s3://primehub/cloudformation/${GIT_TAG}/primehub-starter-cloudformation.yaml 18 | aws s3 cp ${DIR}/primehub-ce-starter-cloudformation.yaml s3://primehub/cloudformation/${GIT_TAG}/primehub-ce-starter-cloudformation.yaml 19 | 20 | echo "[S3] Set cloudformation template public" 21 | aws s3api put-object-acl --bucket primehub --key cloudformation/${GIT_TAG}/primehub-ce-starter-cloudformation.yaml --acl public-read 22 | aws s3api put-object-acl --bucket primehub --key cloudformation/${GIT_TAG}/primehub-starter-cloudformation.yaml --acl public-read 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy-test.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: PrimeHub AWS CDK Deploy CI 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | dry-run-deploy: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - name: Install Yarn & aws-cdk 29 | run: npm install -g yarn aws-cdk 30 | - name: Dry-run deploy 31 | run: | 32 | ./deploy aws-cdk-dry-run --dry-run 33 | env: 34 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 35 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 36 | AWS_DEFAULT_REGION: 'us-east-1' 37 | -------------------------------------------------------------------------------- /lib/nvidia-device-plugin.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | 4 | export interface NvidiaDevicePluginProps { 5 | eksCluster: eks.ICluster; 6 | nodeSelector?: { [key: string]: string }; 7 | tolerations?: { [key: string]: string }[]; 8 | } 9 | 10 | interface HelmValues { 11 | [key: string]: any; 12 | } 13 | 14 | export class NvidiaDevicePlugin extends cdk.Construct { 15 | constructor( 16 | scope: cdk.Construct, 17 | id: string, 18 | props: NvidiaDevicePluginProps 19 | ) { 20 | super(scope, id); 21 | 22 | const defaultTolerations = [ 23 | { 24 | key: 'CriticalAddonsOnly', 25 | operator: 'Exists', 26 | }, 27 | { 28 | key: 'nvidia.com/gpu', 29 | operator: 'Exists', 30 | effect: 'NoSchedule', 31 | }, 32 | ] as { [key: string]: string }[]; 33 | 34 | const helmValues = { 35 | nodeSelector: props.nodeSelector, 36 | tolerations: defaultTolerations.concat(props.tolerations || []), 37 | } as HelmValues; 38 | 39 | props.eksCluster.addHelmChart('nvidia-device-plugin', { 40 | chart: 'nvidia-device-plugin', 41 | repository: 'https://nvidia.github.io/k8s-device-plugin', 42 | namespace: 'kube-system', 43 | release: 'nvidia-device-plugin', 44 | values: helmValues, 45 | wait: false, 46 | version: '0.11.0', 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/destroy-cluster.md: -------------------------------------------------------------------------------- 1 | # How to destroy PrimeHub Cluster created by Launch Stack URL 2 | 3 | Once you want to destroy the Cluster created by PrimeHub Launch Stack URL, please follow the following steps to destroy. 4 | 5 | ## By AWS Management Console 6 | 7 | 1. Login AWS Management Console with your own AWS account 8 | 9 | ![aws-console-login](imgs/aws-console-login.png) 10 | 11 | 2. Select region US East Northern Virginia region `us-east-1` 12 | 13 | ![aws-console-select-region](imgs/aws-console-select-region.png) 14 | 15 | 3. Select AWS Services `CloudFormation` 16 | 17 | ![aws-console-cloudformation](imgs/aws-console-cloudformation.png) 18 | 19 | 4. Select your PrimeHub EKS cluster 20 | 21 | The name will be `eks--cdk-stack` 22 | 23 | ![aws-console-eks-cdk-stack](imgs/aws-console-eks-cdk-stack.png) 24 | 25 | 5. Delete the Stack by the delete button 26 | 27 | ![aws-console-cdk-delete](imgs/aws-console-cdk-delete.png) 28 | 29 | ## By Command Line 30 | 31 | 1. Make sure you had installed AWS CLI 32 | 33 | ```bash 34 | $ aws --version 35 | aws-cli/2.2.13 Python/3.9.5 Darwin/20.6.0 source/arm64 prompt/off 36 | ``` 37 | 38 | 2. Setup the Auth configuration of AWS with Access Key ID and Secret Access Key 39 | 40 | ```bash 41 | $ aws configure 42 | ``` 43 | 44 | 3. Destroy the CloudFormation Stack of PrimeHub EKS cluster 45 | 46 | ```bash 47 | $ aws cloudformation --region us-east-1 --stack-name eks--cdk-stack 48 | ``` 49 | -------------------------------------------------------------------------------- /lib/aws-efs-csi-driver.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | 4 | export interface AwsEfsCsiDriverProps { 5 | eksCluster: eks.ICluster; 6 | fileSystemID: string; 7 | username: string; 8 | } 9 | 10 | interface HelmValues { 11 | [key: string]: any; 12 | } 13 | 14 | export class AwsEfsCsiDriver extends cdk.Construct { 15 | constructor(scope: cdk.Construct, id: string, props: AwsEfsCsiDriverProps) { 16 | super(scope, id); 17 | 18 | const helmValues = { 19 | replicaCount: 1, 20 | image: { 21 | repository: 'infuseai/aws-efs-csi-driver', 22 | tag: 'v1.3.3-dirty', 23 | }, 24 | controller: { 25 | tags: { 26 | owner: props.username, 27 | clusterName: props.eksCluster.clusterName, 28 | }, 29 | }, 30 | storageClasses: [ 31 | { 32 | name: 'efs-sc', 33 | parameters: { 34 | provisioningMode: 'efs-ap', 35 | fileSystemId: props.fileSystemID, 36 | directoryPerms: '777', 37 | uid: '1000', 38 | gid: '100', 39 | }, 40 | }, 41 | ], 42 | } as HelmValues; 43 | 44 | props.eksCluster.addHelmChart('aws-efs-csi-driver', { 45 | chart: 'aws-efs-csi-driver', 46 | repository: 'https://kubernetes-sigs.github.io/aws-efs-csi-driver/', 47 | namespace: 'kube-system', 48 | release: 'aws-efs-csi-driver', 49 | values: helmValues, 50 | wait: true, 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /extras/README.md: -------------------------------------------------------------------------------- 1 | # PrimeHub CDK Starter 2 | 3 | ## One Click Apply 4 | 5 | Click it to create PrimeHub EKS by CloudFormation 6 | 7 | - ### PrimeHub EE 8 | 9 | [![Launch Stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?stackName=primehub-starter&templateURL=https://primehub.s3.amazonaws.com/cloudformation/primehub-starter-cloudformation.yaml) 10 | 11 | - ### PrimeHub CE 12 | 13 | [![Launch Stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?stackName=primehub-starter&templateURL=https://primehub.s3.amazonaws.com/cloudformation/primehub-ce-starter-cloudformation.yaml) 14 | 15 | ## Customization 16 | 17 | We use CloudFormation to provision an ec2 instance to run the primehub-aws-cdk. 18 | 19 | ### Parameters 20 | 21 | We could define many parameters in the [primehub-starter-cloudformation.yaml](primehub-starter-cloudformation.yaml). 22 | 23 | 24 | For example, there is a parameter `PrimeHubInstallMode`: 25 | 26 | ```yaml 27 | Parameters: 28 | PrimeHubInstallMode: 29 | Type: String 30 | Default: ce 31 | AllowedValues: 32 | - ce 33 | - ee 34 | Description: PrimeHub installion mode, Please enter ce or ee. Default is ce. 35 | ``` 36 | 37 | In our `user-data-script`, it could be evaluated with `Fn::Sub` function: 38 | 39 | ```bash 40 | UserData: 41 | Fn::Base64: !Sub | 42 | #!/bin/sh 43 | export PRIMEHUB_MODE="${PrimeHubInstallMode}" 44 | ``` 45 | 46 | ## Release Process 47 | 48 | Any new CloudFormation template should upload to our S3 bucket and set it public to download. 49 | -------------------------------------------------------------------------------- /lib/cert-manager.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | 4 | export interface CertManagerProps { 5 | eksCluster: eks.ICluster; 6 | email?: string; 7 | } 8 | 9 | interface HelmValues { 10 | [key: string]: any; 11 | } 12 | 13 | const issuer = { 14 | apiVersion: 'cert-manager.io/v1alpha2', 15 | kind: 'ClusterIssuer', 16 | metadata: { 17 | name: 'letsencrypt-prod', 18 | }, 19 | spec: { 20 | acme: { 21 | email: 'you@example.com', 22 | server: 'https://acme-v02.api.letsencrypt.org/directory', 23 | privateKeySecretRef: { 24 | name: 'letsencrypt', 25 | }, 26 | solvers: [{ http01: { ingress: { class: 'nginx' } } }], 27 | }, 28 | }, 29 | }; 30 | 31 | export class CertManager extends cdk.Construct { 32 | constructor(scope: cdk.Construct, id: string, props: CertManagerProps) { 33 | super(scope, id); 34 | const helmValues = { 35 | installCRDs: true, 36 | ingressShim: { 37 | defaultIssuerName: 'letsencrypt-prod', 38 | defaultIssuerKind: 'ClusterIssuer', 39 | }, 40 | } as HelmValues; 41 | 42 | // Apply Cert Manager helm chart 43 | const certManager = props.eksCluster.addHelmChart('cert-manager', { 44 | chart: 'cert-manager', 45 | repository: 'https://charts.jetstack.io', 46 | namespace: 'kube-system', 47 | release: 'cert-manager', 48 | version: 'v0.15.0', 49 | values: helmValues, 50 | wait: true, 51 | }); 52 | 53 | if (props.email) { 54 | issuer.spec.acme.email = props.email; 55 | } 56 | 57 | const clusterIssuer = new eks.KubernetesManifest(this, id, { 58 | cluster: props.eksCluster, 59 | manifest: [issuer], 60 | overwrite: true, 61 | }); 62 | 63 | clusterIssuer.node.addDependency(certManager); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: 'Release AWS One Click CloudFormation Template' 2 | on: 3 | release: 4 | types: 5 | - created 6 | 7 | jobs: 8 | release-one-click-cloudformation-template: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | - name: Get release 14 | id: get_release 15 | uses: bruceadams/get-release@v1.2.2 16 | env: 17 | GITHUB_TOKEN: ${{github.token}} 18 | - name: Release CloudFormation Template 19 | run: | 20 | ./extras/release.sh ${{ github.event.release.tag_name }} 21 | env: 22 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 23 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 24 | AWS_DEFAULT_REGION: 'us-east-1' 25 | - name: Update Release 26 | id: update_release 27 | uses: tubone24/update_release@v1.0 28 | env: 29 | GITHUB_TOKEN: ${{ github.token }} 30 | with: 31 | body: | 32 | ## One Click Apply 33 | Click it to create PrimeHub EKS by CloudFormation 34 | 35 | - ### PrimeHub EE 36 | 37 | [![Launch Stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?stackName=primehub-starter&templateURL=https://primehub.s3.amazonaws.com/cloudformation/${{ github.event.release.tag_name }}/primehub-starter-cloudformation.yaml) 38 | 39 | - ### PrimeHub CE 40 | 41 | [![Launch Stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?stackName=primehub-starter&templateURL=https://primehub.s3.amazonaws.com/cloudformation/${{ github.event.release.tag_name }}/primehub-ce-starter-cloudformation.yaml) 42 | -------------------------------------------------------------------------------- /lib/nginx-ingress.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | 4 | export interface IngressNginxControllerProps { 5 | eksCluster: eks.ICluster; 6 | } 7 | 8 | interface HelmValues { 9 | [key: string]: any; 10 | } 11 | 12 | export class IngressNginxController extends cdk.Construct { 13 | constructor( 14 | scope: cdk.Construct, 15 | id: string, 16 | props: IngressNginxControllerProps 17 | ) { 18 | super(scope, id); 19 | 20 | // Deploy Ingress Nginx Controller from the Helm chart 21 | const helmValues = { 22 | rbac: { 23 | create: true, 24 | }, 25 | tcp: { 26 | '2222': 'hub/ssh-bastion-server:2222', 27 | }, 28 | defaultBackend: { 29 | enabled: true, 30 | resources: { 31 | limits: { 32 | cpu: '250m', 33 | memory: '100Mi', 34 | }, 35 | requests: { 36 | cpu: '100m', 37 | memory: '64Mi', 38 | }, 39 | }, 40 | }, 41 | controller: { 42 | hostNetwork: true, 43 | admissionWebhooks: { 44 | enabled: false, 45 | }, 46 | config: { 47 | 'ssl-redirect': 'false', 48 | 'use-forwarded-headers': 'true', 49 | }, 50 | containerPort: { 51 | http: 80, 52 | https: 443, 53 | }, 54 | service: { 55 | targetPorts: { 56 | http: 'http', 57 | https: 'https', 58 | }, 59 | annotations: { 60 | 'service.beta.kubernetes.io/aws-load-balancer-backend-protocol': 61 | 'tcp', 62 | 'service.beta.kubernetes.io/aws-load-balancer-type': 'nlb', 63 | }, 64 | }, 65 | resources: { 66 | limits: { 67 | cpu: '250m', 68 | memory: '200Mi', 69 | }, 70 | requests: { 71 | cpu: '100m', 72 | memory: '100Mi', 73 | }, 74 | }, 75 | }, 76 | } as HelmValues; 77 | 78 | // Apply helm chart 79 | props.eksCluster.addHelmChart('ingress-nginx-controller', { 80 | chart: 'ingress-nginx', 81 | repository: 'https://kubernetes.github.io/ingress-nginx', 82 | createNamespace: true, 83 | namespace: 'ingress-nginx', 84 | release: 'nginx-ingress', 85 | values: helmValues, 86 | wait: true, 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CDK with PrimeHub on AWS EKS 2 | 3 | This repo contains code demonstrating how to set up PrimeHub and PrimeHub ready EKS cluster by CDK. 4 | The EKS cluster will include a Managed Node Group for PrimeHub system and 2 Auto Scaling Groups for computing tasks. 5 | 6 | ![image](Dev-EKS.png) 7 | ## Prerequisites 8 | 9 | * [Optional] Setup a public domain served by route 53 10 | * AWS CLI with correct credentials configuration 11 | * Node.js version >= 10.13.0 (We recommend a version in active long-term support, which, at this writing, is the latest 14.x release.) 12 | * CDK version >= 1.115 13 | * yarn 14 | * ts-node 15 | 16 | ## What is being deployed 17 | 18 | * VPC without NAT gatway 19 | * EKS cluster with Managed-nodegroup x1 and Self-managed nodes x 2 20 | * EFS with dynamic provisioner configured on EKS 21 | * S3 buckets, 1 for PrimeHub configuration, 1 for object store 22 | * Cluster-autoscaler on EKS 23 | * Ingress-nginx-controller on EKS 24 | * Cert-manager on EKS 25 | * PrimeHub on EKS 26 | 27 | ## Usage 28 | 29 | ### Deployment 30 | 31 | Way to deploy the EKS stack with AWS CDK 32 | 33 | ```bash 34 | git clone https://github.com/InfuseAI/primehub-aws-cdk.git 35 | cd primehub-aws-cdk 36 | ./deploy 37 | ``` 38 | 39 | ### Login 40 | 41 | The above deployment may take around 20 minutes, depending on your region, the data center workload, and networking status. 42 | 43 | You will receive an email containing login credentials of your cluster. 44 | 45 | Or you can go and check your AWS CloudFormatioin console. By clicking `Stacks`, you should have the deployed stack named after `-cdk-stack` in the `Stack name` column. Click `-cdk-stack`. The console will show you the details of your stack a.k.a. `-cdk-stack`. 46 | 47 | Select the `Outputs` tab of the stack details. Refer to `PrimeHubURL` to get the URL of PrimeHub, and get the account name and password according to `PrimeHubAccount` and `KeycloakPassword`. 48 | 49 | ### Administration 50 | 51 | Please reference [Administration PrimeHub installed by primehub-aws-cdk](Administration.md) 52 | ### Destroy EKS Stack 53 | 54 | Way to destroy the existing EKS stack 55 | 56 | ```bash 57 | cdk destroy 58 | ``` 59 | 60 | Or please reference the following document to destroy the cluster. 61 | 62 | * [How to destroy PrimeHub Cluster created by Launch Stack URL](docs/destroy-cluster.md) 63 | 64 | ### FAQ 65 | 66 | Please see [FAQ.md](FAQ.md) 67 | -------------------------------------------------------------------------------- /bin/index.ts: -------------------------------------------------------------------------------- 1 | import cdk = require('@aws-cdk/core'); 2 | import * as crypto from 'crypto'; 3 | 4 | import { EKSCluster } from '../lib/eks-cluster-Stack'; 5 | 6 | const app = new cdk.App(); 7 | const env = { 8 | account: process.env.CDK_DEFAULT_ACCOUNT || '', 9 | region: 10 | app.node.tryGetContext('region') || process.env.CDK_DEFAULT_REGION || '', 11 | }; 12 | const username = 13 | app.node.tryGetContext('username') || 14 | process.env.USERNAME || 15 | 'dev@infuseai.io'; 16 | const name = app.node.tryGetContext('name') || process.env.NAME || 'cdk'; 17 | const email = app.node.tryGetContext('email') || ''; 18 | const primehubMode = 19 | app.node.tryGetContext('primehubMode') || process.env.PRIMEHUB_MODE || 'ee'; 20 | const basedDomain = 21 | app.node.tryGetContext('basedDomain') || process.env.AWS_BASED_DOMAIN || ''; 22 | const primehubVersion = app.node.tryGetContext('primehubVersion') || null; 23 | const primehubPassword = 24 | app.node.tryGetContext('primehubPassword') || 25 | process.env.PH_PASSWORD || 26 | crypto.randomBytes(32).toString('hex'); 27 | const keycloakPassword = 28 | app.node.tryGetContext('keycloakPassword') || 29 | process.env.KC_PASSWORD || 30 | crypto.randomBytes(32).toString('hex'); 31 | const zone = app.node.tryGetContext('zone') || 'a'; 32 | const cpuInstance = app.node.tryGetContext('cpuInstance') || 't3a.xlarge'; 33 | const gpuInstance = app.node.tryGetContext('gpuInstance') || 'g4dn.xlarge'; 34 | const systemInstance = app.node.tryGetContext('systemInstance') || 't3a.xlarge'; 35 | const k8sInfraOnly = app.node.tryGetContext('k8sInfraOnly') || 'false'; 36 | const enforceUpdatePassword = 37 | (app.node.tryGetContext('enforceUpdatePassword') || 'false') === 'false' 38 | ? false 39 | : true; 40 | const cpuDesiredCapacity = parseInt( 41 | app.node.tryGetContext('cpuDesiredCapacity') || '0', 42 | 10 43 | ); 44 | const gpuDesiredCapacity = parseInt( 45 | app.node.tryGetContext('gpuDesiredCapacity') || '0', 46 | 10 47 | ); 48 | const cpuMaxCapacity = parseInt( 49 | app.node.tryGetContext('cpuMaxCapacity') || '2', 50 | 10 51 | ); 52 | const gpuMaxCapacity = parseInt( 53 | app.node.tryGetContext('gpuMaxCapacity') || '2', 54 | 10 55 | ); 56 | const scaleDownDelay = parseInt( 57 | app.node.tryGetContext('scaleDownDelay') || '10', 58 | 10 59 | ); 60 | 61 | const eksStackName = `eks-${name}-cdk-stack`; 62 | const eksClusterStack = new EKSCluster(app, eksStackName, { 63 | env: env, 64 | name: name, 65 | username: username, 66 | email: email, 67 | primehubMode: primehubMode, 68 | basedDomain: basedDomain, 69 | keycloakPassword: keycloakPassword, 70 | primehubPassword: primehubPassword, 71 | availabilityZone: `${env.region}${zone}`, 72 | cpuInstance: cpuInstance, 73 | gpuInstance: gpuInstance, 74 | systemInstance: systemInstance, 75 | cpuDesiredCapacity: cpuDesiredCapacity, 76 | gpuDesiredCapacity: gpuDesiredCapacity, 77 | cpuMaxCapacity: cpuMaxCapacity, 78 | gpuMaxCapacity: gpuMaxCapacity, 79 | scaleDownDelay: scaleDownDelay, 80 | k8sInfraOnly: k8sInfraOnly, 81 | enforceUpdatePassword: enforceUpdatePassword, 82 | primehubVersion: primehubVersion, 83 | }); 84 | 85 | eksClusterStack.templateOptions.description = `Setup AWS EKS environment with PrimeHub by AWS CDK. 86 | The PrimeHub access information will show in the 'Outputs' tab once the stack status of '${eksStackName}' becomes CREATE_COMPLETE. 87 | For more information, please visit: https://github.com/InfuseAI/primehub-aws-cdk`; 88 | 89 | cdk.Tags.of(eksClusterStack).add('owner', username); 90 | cdk.Tags.of(eksClusterStack).add('clusterType', 'dev-eks'); 91 | 92 | app.synth(); 93 | -------------------------------------------------------------------------------- /extras/ec2-user-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export HOME=/root 4 | whoami 5 | pwd 6 | yum update -y 7 | yum install -y jq 8 | 9 | EMAIL_NOTIFICATION_API="https://ykek6s29ol.execute-api.us-east-1.amazonaws.com/dev/one-click" 10 | 11 | function notification::register() { 12 | local name=$1 13 | local email=$2 14 | local subscribe=${3:-no} 15 | if [[ "${email}" != "" ]]; then 16 | curl -s --location --request POST "${EMAIL_NOTIFICATION_API}" \ 17 | --header 'Content-Type: application/json' \ 18 | --data-raw "{ 19 | \"email\": \"${email}\", 20 | \"name\": \"${name}\", 21 | \"subscribe\": \"${subscribe}\" 22 | }" | jq .id -r 23 | fi 24 | } 25 | 26 | function notification::completed() { 27 | local name=$1 28 | local id=$2 29 | local region=$3 30 | local password=$4 31 | 32 | cf_output=$(aws cloudformation describe-stacks --stack-name eks-${name}-cdk-stack --region ${region} --query "Stacks[0].Outputs[*]" --output text) 33 | stackId=$(aws cloudformation describe-stacks --stack-name eks-${name}-cdk-stack --region ${region} --query Stacks[0].StackId | sed 's/"//g') 34 | PRIMEHUB_URL=$(echo "${cf_output}" | grep "^PrimeHubURL" | awk '{$1 = ""; print $0;}' | sed 's/ //g') 35 | if [[ "${id}" != "" ]]; then 36 | curl -s --location --request PATCH "${EMAIL_NOTIFICATION_API}/${id}" \ 37 | --header 'Content-Type: application/json' \ 38 | --data-raw "{ 39 | \"endpoint\": \"${PRIMEHUB_URL}\", 40 | \"region\": \"${region}\", 41 | \"stackId\": \"${stackId}\", 42 | \"password\": \"${password}\" 43 | }" 44 | fi 45 | } 46 | 47 | echo "Install Node" 48 | cd /root 49 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash 50 | . ~/.nvm/nvm.sh 51 | nvm install v16 52 | node -e "console.log('Running Node.js ' + process.version)" 53 | 54 | echo "Install Yarn and CDK" 55 | npm install -g yarn 56 | npm install -g aws-cdk@v1.134 57 | 58 | echo "Download PrimeHub Starter" 59 | tag=${GIT_TAG:-cfTemplate} 60 | wget https://github.com/InfuseAI/primehub-aws-cdk/archive/refs/tags/${tag}.zip 61 | unzip ${tag}.zip 62 | cd $(unzip -Z -1 ${tag}.zip| head -1) 63 | 64 | # set cdk never asking for approval 65 | cp extras/cdk.json . 66 | yarn install 67 | 68 | echo "Prepare CDK" 69 | AWS_REGION='us-east-1' 70 | AWS_ZONE='a' 71 | SYS_INSTANCE='t3.xlarge' 72 | CPU_INSTANCE="${CPU_INSTANCE:-'t3.xlage'}" 73 | GPU_INSTANCE="${GPU_INSTANCE:-'g4dn.xlarge'}" 74 | PASSWORD="${PRIMEHUB_PASSWORD:-$(openssl rand -hex 16)}" 75 | PRIMEHUB_VERSION='3.12.1' 76 | EMAIL_NOTIFICATION=${EMAIL_NOTIFICATION:-} 77 | EMAIL_NOTIFICATION_ID='' 78 | SCALE_DWON_DELAY=2880 # 48hr 79 | echo "Name: ${AWS_STACK_NAME}" 80 | echo "Mode: ${PRIMEHUB_MODE}" 81 | echo "Region: ${AWS_REGION}" 82 | echo "Zone: ${AWS_ZONE}" 83 | echo "System Instance Type: ${SYS_INSTANCE_TYPE}" 84 | echo "CPU Instance Type: ${CPU_INSTANCE}" 85 | echo "GPU Instance Type: ${GPU_INSTANCE}" 86 | echo "Scale Down Delay: ${SCALE_DWON_DELAY}" 87 | 88 | EMAIL_NOTIFICATION_ID=$(notification::register ${AWS_STACK_NAME} ${EMAIL_NOTIFICATION} ${SUBSCRIBE_NEWSLETTER}) 89 | echo "Deploy CDK ${AWS_STACK_NAME}" 90 | export AWS_REGION 91 | ./deploy ${AWS_STACK_NAME} \ 92 | --region ${AWS_REGION} \ 93 | --zone ${AWS_ZONE} \ 94 | --email ${EMAIL_NOTIFICATION} \ 95 | --enforce-update-password \ 96 | --primehub-version ${PRIMEHUB_VERSION} \ 97 | --system-instance-type ${SYS_INSTANCE} \ 98 | --cpu-instance-type ${CPU_INSTANCE} \ 99 | --gpu-instance-type ${GPU_INSTANCE} \ 100 | --scale-down-delay ${SCALE_DWON_DELAY} \ 101 | --cpu-desired-capacity 1 \ 102 | --mode ${PRIMEHUB_MODE} \ 103 | --keycloak-password ${PASSWORD} \ 104 | --primehub-password ${PASSWORD} || exit 1 105 | 106 | notification::completed ${AWS_STACK_NAME} ${EMAIL_NOTIFICATION_ID} ${AWS_REGION} ${PASSWORD} 107 | echo "Completed" 108 | exit 0 109 | 110 | -------------------------------------------------------------------------------- /Administration.md: -------------------------------------------------------------------------------- 1 | # Administration PrimeHub installed by primehub-aws-cdk 2 | 3 | ## How to upgrade existing EKS Cluster 4 | 5 | Once you use 1-click or the cdk deploy script to deploy the PrimeHub EKS Cluster, you can use the following steps to upgrade your PrimeHub cluster with latest version. 6 | 7 | ### Steps 8 | 9 | 1. Prepare AWS credentials file 10 | 11 | ```bash 12 | mkdir -p ~/.aws 13 | touch ~/.aws/credentials 14 | ``` 15 | 16 | Edit credentials and add the content with the generated access key. 17 | 18 | ```bash 19 | # credentials 20 | [default] 21 | aws_access_key_id = xxx 22 | aws_secret_access_key = xxx 23 | region = us-east-1 24 | ``` 25 | 26 | 1. Install the required tools 27 | 28 | - `awscli` Universal Command Line Interface for Amazon Web Services 29 | 30 | Please reference the [AWS CLI Official Document](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) to install `awscli`. 31 | 32 | - `kubectl` The command line tool lets you control Kubernetes clusters 33 | 34 | Please reference the [Kubernetes Official Document](https://kubernetes.io/docs/tasks/tools/) to install `kubectl` 35 | 36 | - `aws-cdk` [Optional] A framework for defining cloud infrastructure in code 37 | 38 | Please reference the [AWS CDK Developer Guide](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install) to install `aws-cdk` 39 | 40 | 1. Clone the `primehub-aws-cdk` github repo 41 | 42 | ```bash 43 | git clone https://github.com/InfuseAI/primehub-aws-cdk.git 44 | ``` 45 | 46 | 1. Run the `connect` script to fetch the kubeconfig and PrimeHub configuration files. 47 | 48 | ```bash 49 | # List all available cluster in your 50 | ./primehub-aws-cdk/connect --region us-east-1 --list 51 | 52 | # Fetch the kubeconfig and PrimeHub configuration files 53 | ./primehub-aws-cdk/connect --regsion us-east-1 54 | 55 | # Example 56 | ./primehub-aws-cdk/connect --regsion us-east-1 primehub-starter 57 | ``` 58 | 59 | 1. After running the `connect` script, you can use `kubectl` and `helm` to verify the EKS cluster and PrimeHub. 60 | 61 | ```bash 62 | # Clone PrimeHub Repository 63 | $ git clone https://github.com/InfuseAI/primehub.git 64 | 65 | # Install required tools 66 | $ ./primehub/install/primehub-install required-bin 67 | 68 | # Set PATH 69 | $ export PATH=$PATH:$HOME/bin 70 | 71 | # Check k8s node 72 | $ kubectl get node 73 | NAME STATUS ROLES AGE VERSION 74 | ip-10-0-29-171.ec2.internal Ready 7d v1.20.7-eks-135321 75 | 76 | # Check helm 77 | $ helm ls -A 78 | NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION 79 | aws-ecr-credential hub 1 2021-09-28 09:54:56.481498312 +0000 UTC deployed aws-ecr-credential-1.5.0 1.5.0 80 | aws-efs-csi-driver kube-system 1 2021-09-28 09:54:54.991699651 +0000 UTC deployed aws-efs-csi-driver-2.2.0 1.3.4 81 | cert-manager kube-system 1 2021-09-28 09:54:55.372646325 +0000 UTC deployed cert-manager-v0.15.0 v0.15.0 82 | nginx-ingress ingress-nginx 1 2021-09-28 09:54:55.567719036 +0000 UTC deployed ingress-nginx-4.0.3 1.0.2 83 | nvidia-device-plugin kube-system 1 2021-09-28 09:54:55.883676584 +0000 UTC deployed nvidia-device-plugin-0.9.0 0.9.0 84 | primehub hub 1 2021-09-28 09:57:56.158153215 +0000 UTC deployed primehub-3.8.0-aws.0 v3.8.0-aws.0 85 | ``` 86 | 87 | 1. Upgrade PrimeHub to specific version 88 | 89 | ```bash 90 | # List all available 1-click AWS PrimeHub version 91 | ./primehub/install/primehub-install version --aws 92 | 93 | # Upgrade 1-click AWS PrimeHub to specific version 94 | ./primehub/install/primehub-install upgrade primehub --primehub-version v3.8.0-aws.1 95 | 96 | # Upgrade 1-click AWS PrimeHub to latest version 97 | ./primehub/install/primehub-install upgrade primehub --aws 98 | ``` 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,node,yarn,git,vim,linux,macos 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,node,yarn,git,vim,linux,macos 4 | 5 | ### Git ### 6 | # Created by git for backups. To disable backups in Git: 7 | # $ git config --global mergetool.keepBackup false 8 | *.orig 9 | 10 | # Created by git when using merge tools for conflicts 11 | *.BACKUP.* 12 | *.BASE.* 13 | *.LOCAL.* 14 | *.REMOTE.* 15 | *_BACKUP_*.txt 16 | *_BASE_*.txt 17 | *_LOCAL_*.txt 18 | *_REMOTE_*.txt 19 | 20 | ### Linux ### 21 | *~ 22 | 23 | # temporary files which can be created if a process still has a handle open of a deleted file 24 | .fuse_hidden* 25 | 26 | # KDE directory preferences 27 | .directory 28 | 29 | # Linux trash folder which might appear on any partition or disk 30 | .Trash-* 31 | 32 | # .nfs files are created when an open file is removed but is still being accessed 33 | .nfs* 34 | 35 | ### macOS ### 36 | # General 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Icon must end with two \r 42 | Icon 43 | 44 | 45 | # Thumbnails 46 | ._* 47 | 48 | # Files that might appear in the root of a volume 49 | .DocumentRevisions-V100 50 | .fseventsd 51 | .Spotlight-V100 52 | .TemporaryItems 53 | .Trashes 54 | .VolumeIcon.icns 55 | .com.apple.timemachine.donotpresent 56 | 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | 64 | ### Node ### 65 | # Logs 66 | logs 67 | *.log 68 | npm-debug.log* 69 | yarn-debug.log* 70 | yarn-error.log* 71 | lerna-debug.log* 72 | .pnpm-debug.log* 73 | 74 | # Diagnostic reports (https://nodejs.org/api/report.html) 75 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 76 | 77 | # Runtime data 78 | pids 79 | *.pid 80 | *.seed 81 | *.pid.lock 82 | 83 | # Directory for instrumented libs generated by jscoverage/JSCover 84 | lib-cov 85 | 86 | # Coverage directory used by tools like istanbul 87 | coverage 88 | *.lcov 89 | 90 | # nyc test coverage 91 | .nyc_output 92 | 93 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 94 | .grunt 95 | 96 | # Bower dependency directory (https://bower.io/) 97 | bower_components 98 | 99 | # node-waf configuration 100 | .lock-wscript 101 | 102 | # Compiled binary addons (https://nodejs.org/api/addons.html) 103 | build/Release 104 | 105 | # Dependency directories 106 | node_modules/ 107 | jspm_packages/ 108 | 109 | # Snowpack dependency directory (https://snowpack.dev/) 110 | web_modules/ 111 | 112 | # TypeScript cache 113 | *.tsbuildinfo 114 | 115 | # Optional npm cache directory 116 | .npm 117 | 118 | # Optional eslint cache 119 | .eslintcache 120 | 121 | # Microbundle cache 122 | .rpt2_cache/ 123 | .rts2_cache_cjs/ 124 | .rts2_cache_es/ 125 | .rts2_cache_umd/ 126 | 127 | # Optional REPL history 128 | .node_repl_history 129 | 130 | # Output of 'npm pack' 131 | *.tgz 132 | 133 | # Yarn Integrity file 134 | .yarn-integrity 135 | 136 | # dotenv environment variables file 137 | .env 138 | .env.test 139 | .env.production 140 | 141 | # parcel-bundler cache (https://parceljs.org/) 142 | .cache 143 | .parcel-cache 144 | 145 | # Next.js build output 146 | .next 147 | out 148 | 149 | # Nuxt.js build / generate output 150 | .nuxt 151 | dist 152 | 153 | # Gatsby files 154 | .cache/ 155 | # Comment in the public line in if your project uses Gatsby and not Next.js 156 | # https://nextjs.org/blog/next-9-1#public-directory-support 157 | # public 158 | 159 | # vuepress build output 160 | .vuepress/dist 161 | 162 | # Serverless directories 163 | .serverless/ 164 | 165 | # FuseBox cache 166 | .fusebox/ 167 | 168 | # DynamoDB Local files 169 | .dynamodb/ 170 | 171 | # TernJS port file 172 | .tern-port 173 | 174 | # Stores VSCode versions used for testing VSCode extensions 175 | .vscode-test 176 | 177 | # yarn v2 178 | .yarn/cache 179 | .yarn/unplugged 180 | .yarn/build-state.yml 181 | .yarn/install-state.gz 182 | .pnp.* 183 | 184 | ### Vim ### 185 | # Swap 186 | [._]*.s[a-v][a-z] 187 | !*.svg # comment out if you don't need vector files 188 | [._]*.sw[a-p] 189 | [._]s[a-rt-v][a-z] 190 | [._]ss[a-gi-z] 191 | [._]sw[a-p] 192 | 193 | # Session 194 | Session.vim 195 | Sessionx.vim 196 | 197 | # Temporary 198 | .netrwhist 199 | # Auto-generated tag files 200 | tags 201 | # Persistent undo 202 | [._]*.un~ 203 | 204 | ### VisualStudioCode ### 205 | .vscode/* 206 | !.vscode/settings.json 207 | !.vscode/tasks.json 208 | !.vscode/launch.json 209 | !.vscode/extensions.json 210 | *.code-workspace 211 | 212 | # Local History for Visual Studio Code 213 | .history/ 214 | 215 | ### VisualStudioCode Patch ### 216 | # Ignore all local history of files 217 | .history 218 | .ionide 219 | 220 | ### yarn ### 221 | # https://yarnpkg.com/advanced/qa#which-files-should-be-gitignored 222 | 223 | .yarn/* 224 | !.yarn/releases 225 | !.yarn/plugins 226 | !.yarn/sdks 227 | !.yarn/versions 228 | 229 | # if you are NOT using Zero-installs, then: 230 | # comment the following lines 231 | !.yarn/cache 232 | 233 | # and uncomment the following lines 234 | # .pnp.* 235 | 236 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,node,yarn,git,vim,linux,macos 237 | 238 | cdk.out 239 | *.js 240 | 241 | VSCode extension 242 | 243 | # Store launch config in repo but not settings 244 | .vscode/settings.json 245 | /.favorites.json 246 | 247 | # TypeScript incremental build states 248 | *.tsbuildinfo 249 | 250 | # Local state files & OS specifics 251 | .DS_Store 252 | node_modules/ 253 | lerna-debug.log 254 | dist/ 255 | pack/ 256 | .BUILD_COMPLETED 257 | .local-npm/ 258 | .tools/ 259 | coverage/ 260 | .nyc_output 261 | .nycrc 262 | .LAST_BUILD 263 | *.sw[a-z] 264 | *~ 265 | .idea 266 | junit.xml 267 | 268 | # We don't want tsconfig at the root 269 | /tsconfig.json 270 | 271 | # CDK Context & Staging files 272 | cdk.context.json 273 | .cdk.staging/ 274 | cdk.out/ 275 | *.tabl.json 276 | cdk.context.bak 277 | cdk.cloudformation.out 278 | 279 | # Yarn error log 280 | yarn-error.log 281 | 282 | # VSCode history plugin 283 | .vscode/.history/ 284 | 285 | # Cloud9 286 | .c9 287 | .nzm-* 288 | 289 | /.versionrc.json 290 | artifact/ 291 | 292 | # AWS Launch Stack URL Release 293 | extras/*.bak 294 | -------------------------------------------------------------------------------- /connect: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | IFS=$'\t' 3 | set -eo pipefail 4 | 5 | TASK='connect' 6 | NAME='' 7 | AWS_REGION=${AWS_REGION} 8 | 9 | info() { 10 | echo -e "\033[0;32m$1\033[0m" 11 | } 12 | 13 | warn() { 14 | echo -e "\033[0;93m$1\033[0m" 15 | } 16 | 17 | error() { 18 | 19 | echo -e "\033[0;91m$1\033[0m" >&2 20 | } 21 | 22 | function usage() { 23 | local SELF=`basename $0` 24 | cat <] 26 | 27 | Options: 28 | --region : Set the AWS region 29 | -h, --help : Show this message 30 | EOF 31 | } 32 | 33 | function no_eks_cluster_found() { 34 | if [ "${AWS_REGION}" == "" ]; then 35 | error "[Not Found] EKS Cluster" 36 | else 37 | error "[Not Found] EKS Cluster in region ${AWS_REGION}" 38 | fi 39 | warn "[Available] EKS Cluster running" 40 | list_available_eks_clsuter 41 | } 42 | 43 | function list_available_eks_clsuter() { 44 | local region_opt 45 | if [[ ${AWS_REGION} != '' ]]; then 46 | region_opt="--region ${AWS_REGION}" 47 | fi 48 | eval "aws ${region_opt} cloudformation list-stacks --stack-status-filter CREATE_COMPLETE" | grep StackName | grep -e 'eks-.*-cdk-stack"' | sed 's/.*: "\(.*\)",/\1/' 49 | } 50 | 51 | function verifyCfStackName() { 52 | IFS=$'\t\n' 53 | local name=$1 54 | local stackName 55 | 56 | if command -v cdk > /dev/null && [[ $name == '' ]]; then 57 | name=$(cdk context -j | grep '"name":' | awk '{print $2}' | sed 's/"\(.*\)",/\1/g' || '') 58 | fi 59 | 60 | if [[ $name =~ eks-.*-cdk-stack$ ]]; then 61 | stackName=$name 62 | else 63 | stackName="eks-$name-cdk-stack" 64 | fi 65 | 66 | for i in $(list_available_eks_clsuter) 67 | do 68 | if [[ $i == $stackName ]]; then 69 | echo $stackName 70 | return 0 71 | fi 72 | done 73 | 74 | return 1 75 | } 76 | 77 | 78 | function replace_domain() { 79 | local primehub_domain=$1 80 | local elb_domain=${2:-} 81 | 82 | local extension='' 83 | if [[ "$(uname)" == "Darwin" ]]; then 84 | extension=".bak" 85 | fi 86 | 87 | info "[Patch] ELB Domain -> ${primehub_domain}" 88 | k8s_context=$(kubectl config current-context) 89 | if [ "${elb_domain}" != '' ]; then 90 | sed -i ${extension} "s/customHostname: \${Token.*}/customHostname: ${elb_domain}/g" ~/.primehub/config/${k8s_context}/helm_override/primehub.yaml 91 | fi 92 | sed -i ${extension} "s/\${Token.*}/${primehub_domain}/g" ~/.primehub/config/${k8s_context}/.env 93 | sed -i ${extension} "s/\${Token.*}/${primehub_domain}/g" ~/.primehub/config/${k8s_context}/helm_override/primehub.yaml 94 | } 95 | 96 | function connect() { 97 | IFS=$'\t' 98 | local stackName=$1 99 | 100 | if command -v cdk > /dev/null && [ "${AWS_REGION}" == "" ]; then 101 | AWS_REGION=$(cdk context -j | grep '"region":' | awk '{print $2}' | sed 's/"\(.*\)",/\1/g' || echo "") 102 | fi 103 | 104 | if [ "${AWS_REGION}" != "" ]; then 105 | cf_output=$(aws cloudformation describe-stacks --stack-name ${stackName} --region ${AWS_REGION} --query "Stacks[0].Outputs[*]" --output text) 106 | else 107 | cf_output=$(aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[0].Outputs[*]" --output text) 108 | fi 109 | 110 | cluster_name=$(echo ${cf_output} | grep ^ClusterClusterName | awk '{print $2}') 111 | k8s_context_cmd="$(echo ${cf_output} | grep ^ClusterConfigCommand | awk '{$1 = ""; print $0;}') --alias ${EKS_CLUSTER_ALIAS:-$cluster_name}" 112 | config_bucket=$(echo ${cf_output} | grep ^PrimeHubConfigS3Bucket | awk '{$1 = ""; print $0;}' | sed 's/ //g') 113 | 114 | info "[Connect] EKS Cluster ${cluster_name}" 115 | eval "$k8s_context_cmd" 116 | 117 | info "[Fetch] PrimeHub Config from S3 bucket ${config_bucket}" 118 | k8s_context=$(kubectl config current-context) 119 | mkdir -p ~/.primehub/config/${k8s_context}/helm_override 120 | aws s3 cp s3://${config_bucket}/.env ~/.primehub/config/${k8s_context}/ 121 | aws s3 cp s3://${config_bucket}/helm_override/primehub.yaml ~/.primehub/config/${k8s_context}/helm_override/ 122 | 123 | info "[PrimeHub] Config" 124 | AWS_ELB_DOMAIN=$(echo ${cf_output} | grep ^AWSELBDomain | awk '{$1 = ""; print $0;}' | sed 's/ //g') 125 | AWS_CLOUDFRONT_DOMAIN=$(echo ${cf_output} | grep ^AWSCloudFrontDomain | awk '{$1 = ""; print $0;}' | sed 's/ //g' || TRUE) 126 | PRIMEHUB_URL=$(echo ${cf_output} | grep ^PrimeHubURL | awk '{$1 = ""; print $0;}' | sed 's/ //g') 127 | KC_URL="${PRIMEHUB_URL}/auth" 128 | PH_USER=$(echo ${cf_output} | grep ^PrimeHubAccount | awk '{$1 = ""; print $0;}' | sed 's/ //g') 129 | PH_PASSWORD=$(echo ${cf_output} | grep ^PrimeHubPassword | awk '{$1 = ""; print $0;}' | sed 's/ //g') 130 | KC_USER=$(echo ${cf_output} | grep ^KeycloakAccount | awk '{$1 = ""; print $0;}' | sed 's/ //g') 131 | KC_PASSWORD=$(echo ${cf_output} | grep ^KeycloakPassword | awk '{$1 = ""; print $0;}' | sed 's/ //g') 132 | 133 | echo "" 134 | echo " AWS ELB Domain: ${AWS_ELB_DOMAIN}" 135 | echo " PrimeHub: ${PRIMEHUB_URL} ( ${PH_USER} / ${PH_PASSWORD} )" 136 | echo " Id Server: ${KC_URL}/admin/ ( ${KC_USER} / ${KC_PASSWORD} )" 137 | echo "" 138 | 139 | if [ "${PRIMEHUB_URL}" == "https://${AWS_ELB_DOMAIN}" ]; then 140 | replace_domain ${AWS_ELB_DOMAIN} 141 | elif [ "${PRIMEHUB_URL}" == "https://${AWS_CLOUDFRONT_DOMAIN}" ]; then 142 | replace_domain ${AWS_CLOUDFRONT_DOMAIN} ${AWS_ELB_DOMAIN} 143 | fi 144 | } 145 | 146 | function main() { 147 | while (( "$#" )); do 148 | case "${1:-}" in 149 | --list) 150 | TASK='list' 151 | ;; 152 | --region) 153 | shift 154 | AWS_REGION=${1} 155 | ;; 156 | --alias) 157 | shift 158 | EKS_CLUSTER_ALIAS=${1} 159 | ;; 160 | -h|--help) 161 | usage 162 | exit 0 163 | ;; 164 | *) 165 | if [ "${NAME}" == "" ]; then 166 | NAME=$1 167 | else 168 | error "[Syntax error] Should only provide one cluster name" 169 | usage 170 | exit 1 171 | fi 172 | ;; 173 | esac 174 | shift || (usage; exit 1) 175 | done 176 | 177 | if ! command -v kubectl > /dev/null; then 178 | error '[Error] No kubectl installed' 179 | echo ' Please install kubectl' 180 | fi 181 | if ! command -v aws > /dev/null; then 182 | error '[Error] No awscli installed' 183 | echo ' Please install awscli' 184 | fi 185 | 186 | if [ "${TASK}" == 'list' ]; then 187 | info "[Available] EKS Cluster" 188 | list_available_eks_clsuter 189 | else 190 | stackName=$(verifyCfStackName $NAME) || (no_eks_cluster_found; exit 1) 191 | connect $stackName 192 | fi 193 | } 194 | 195 | main "$@" 196 | -------------------------------------------------------------------------------- /extras/primehub-ce-starter-cloudformation.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: | 3 | The PrimeHub Starter Stack. 4 | A new stack `eks--cdk-stack` will be created later in your AWS. 5 | The access URL and default account/password will be show in the output of `eks--cdk-stack`. 6 | Once the deployment is completed, this stack will be deleted automatically. 7 | For more information, please visit: https://github.com/InfuseAI/primehub-aws-cdk 8 | 9 | Resources: 10 | PrimeHubCDKBootstrapEC2: 11 | Type: AWS::EC2::Instance 12 | CreationPolicy: 13 | ResourceSignal: 14 | Timeout: PT1H 15 | Properties: 16 | ImageId: 17 | !FindInMap [AmazonLinux2RegionMap202107212, !Ref 'AWS::Region', AMI] 18 | InstanceType: 't3.micro' 19 | IamInstanceProfile: !Ref InstanceProfile 20 | UserData: 21 | Fn::Base64: !Sub | 22 | #!/bin/sh 23 | curl -o run-cdk.sh https://raw.githubusercontent.com/InfuseAI/primehub-aws-cdk/cfTemplate/extras/ec2-user-script.sh 24 | chmod a+x run-cdk.sh 25 | 26 | # export vars 27 | export PRIMEHUB_MODE="${PrimeHubInstallMode}" 28 | export AWS_STACK_NAME="${AWS::StackName}" 29 | export AWS_REGION="${AWS::Region}" 30 | export GIT_TAG='cfTemplate' 31 | export CPU_INSTANCE="${CPUInstanceType}" 32 | export GPU_INSTANCE="${GPUInstanceType}" 33 | export EMAIL_NOTIFICATION="${EmailNotification}" 34 | export SUBSCRIBE_NEWSLETTER="${SubscribeNewsLetter}" 35 | 36 | ./run-cdk.sh > /var/log/primehub-starter.log 2>&1 37 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --region ${AWS::Region} --resource PrimeHubCDKBootstrapEC2 38 | 39 | # delete the starter stack 40 | aws cloudformation delete-stack --stack-name ${AWS::StackName} --region ${AWS::Region} 41 | SecurityGroups: 42 | - !Ref PrimeHubCDKBootstrapEC2SecurityGroup 43 | AwsIamRole: 44 | Type: AWS::IAM::Role 45 | Properties: 46 | AssumeRolePolicyDocument: 47 | Version: '2012-10-17' 48 | Statement: 49 | - Effect: Allow 50 | Principal: 51 | Service: 52 | - ec2.amazonaws.com 53 | Action: 54 | - 'sts:AssumeRole' 55 | ManagedPolicyArns: 56 | - arn:aws:iam::aws:policy/AmazonEC2FullAccess 57 | - arn:aws:iam::aws:policy/AmazonRoute53FullAccess 58 | - arn:aws:iam::aws:policy/IAMReadOnlyAccess 59 | Path: / 60 | Policies: 61 | - PolicyName: CdkPolicy 62 | PolicyDocument: 63 | Version: '2012-10-17' 64 | Statement: 65 | - Action: 66 | - cloudformation:* 67 | Resource: '*' 68 | Effect: Allow 69 | - Condition: 70 | ForAnyValue:StringEquals: 71 | aws:CalledVia: 72 | - cloudformation.amazonaws.com 73 | Action: '*' 74 | Resource: '*' 75 | Effect: Allow 76 | - Action: s3:* 77 | Resource: arn:aws:s3:::cdktoolkit-stagingbucket-* 78 | Effect: Allow 79 | - Action: 80 | - sts:AssumeRole 81 | Effect: Allow 82 | Resource: 83 | - arn:aws:iam::*:role/cdk-* 84 | InstanceProfile: 85 | Type: 'AWS::IAM::InstanceProfile' 86 | Properties: 87 | Path: / 88 | Roles: 89 | - !Ref AwsIamRole 90 | PrimeHubCDKBootstrapEC2SecurityGroup: 91 | Type: AWS::EC2::SecurityGroup 92 | Properties: 93 | GroupDescription: PrimeHub CDK Bootstrap Security Group 94 | SecurityGroupIngress: 95 | - IpProtocol: tcp 96 | FromPort: 22 97 | ToPort: 22 98 | CidrIp: 0.0.0.0/0 99 | Mappings: 100 | AmazonLinux2RegionMap202107212: 101 | 'af-south-1': 102 | AMI: 'ami-0edec67949fd25461' 103 | 'eu-north-1': 104 | AMI: 'ami-0d441f5643da997cb' 105 | 'ap-south-1': 106 | AMI: 'ami-04db49c0fb2215364' 107 | 'eu-west-3': 108 | AMI: 'ami-0d49cec198762b78c' 109 | 'eu-west-2': 110 | AMI: 'ami-0d26eb3972b7f8c96' 111 | 'eu-south-1': 112 | AMI: 'ami-0fce326033a239d55' 113 | 'eu-west-1': 114 | AMI: 'ami-02b4e72b17337d6c1' 115 | 'ap-northeast-3': 116 | AMI: 'ami-0e787554e61105680' 117 | 'ap-northeast-2': 118 | AMI: 'ami-0a0de518b1fc4524c' 119 | 'me-south-1': 120 | AMI: 'ami-004b77593bd476317' 121 | 'ap-northeast-1': 122 | AMI: 'ami-09ebacdc178ae23b7' 123 | 'sa-east-1': 124 | AMI: 'ami-0f8243a5175208e08' 125 | 'ca-central-1': 126 | AMI: 'ami-02f84cf47c23f1769' 127 | 'ap-east-1': 128 | AMI: 'ami-0a2115f8cc0a3956b' 129 | 'ap-southeast-1': 130 | AMI: 'ami-0f511ead81ccde020' 131 | 'ap-southeast-2': 132 | AMI: 'ami-0aab712d6363da7f9' 133 | 'eu-central-1': 134 | AMI: 'ami-0453cb7b5f2b7fca2' 135 | 'us-east-1': 136 | AMI: 'ami-0c2b8ca1dad447f8a' 137 | 'us-east-2': 138 | AMI: 'ami-0443305dabd4be2bc' 139 | 'us-west-1': 140 | AMI: 'ami-04b6c97b14c54de18' 141 | 'us-west-2': 142 | AMI: 'ami-083ac7c7ecf9bb9b0' 143 | Parameters: 144 | EmailNotification: 145 | Type: String 146 | AllowedPattern: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" 147 | ConstraintDescription: Must be a valid email address. 148 | Description: Please provide an email for notifying the cluster installation status. If you don't subscribe our news latter, this will send credential only, no marketing material, and no spam 149 | SubscribeNewsLetter: 150 | Type: String 151 | AllowedValues: 152 | - "yes" 153 | - "no" 154 | Default: "no" 155 | Description: Subscribe our newsletter, stay up to date on MLOps insights and news 156 | PrimeHubInstallMode: 157 | Type: String 158 | Default: ce 159 | AllowedValues: 160 | - ce 161 | Description: PrimeHub Community Edition Mode 162 | CPUInstanceType: 163 | Type: String 164 | Default: t3.xlarge 165 | AllowedValues: 166 | - t3.xlarge 167 | - t3.2xlarge 168 | - t3a.xlarge 169 | - t3a.2xlarge 170 | - m5.xlarge 171 | - m5.2xlarge 172 | - m5.4xlarge 173 | - m5.8xlarge 174 | - m5.12xlarge 175 | - c5.xlarge 176 | - c5.2xlarge 177 | - c5.4xlarge 178 | - c5.12xlarge 179 | Description: The Instance Type of auto scaling CPU node group 180 | GPUInstanceType: 181 | Type: String 182 | Default: g4dn.xlarge 183 | AllowedValues: 184 | - g4dn.xlarge 185 | - g4dn.2xlarge 186 | - g4dn.12xlarge 187 | Description: The Instance Type of auto scaling GPU node group 188 | -------------------------------------------------------------------------------- /extras/primehub-starter-cloudformation.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: | 3 | The PrimeHub Starter Stack. 4 | A new stack `eks--cdk-stack` will be created later in your AWS. 5 | The access URL and default account/password will be show in the output of `eks--cdk-stack`. 6 | Once the deployment is completed, this stack will be deleted automatically. 7 | For more information, please visit: https://github.com/InfuseAI/primehub-aws-cdk 8 | 9 | Resources: 10 | PrimeHubCDKBootstrapEC2: 11 | Type: AWS::EC2::Instance 12 | CreationPolicy: 13 | ResourceSignal: 14 | Timeout: PT1H 15 | Properties: 16 | ImageId: 17 | !FindInMap [AmazonLinux2RegionMap202107212, !Ref 'AWS::Region', AMI] 18 | InstanceType: 't3.micro' 19 | IamInstanceProfile: !Ref InstanceProfile 20 | UserData: 21 | Fn::Base64: !Sub | 22 | #!/bin/sh 23 | curl -o run-cdk.sh https://raw.githubusercontent.com/InfuseAI/primehub-aws-cdk/cfTemplate/extras/ec2-user-script.sh 24 | chmod a+x run-cdk.sh 25 | 26 | # export vars 27 | export PRIMEHUB_MODE="${PrimeHubInstallMode}" 28 | export AWS_STACK_NAME="${AWS::StackName}" 29 | export AWS_REGION="${AWS::Region}" 30 | export GIT_TAG='cfTemplate' 31 | export CPU_INSTANCE="${CPUInstanceType}" 32 | export GPU_INSTANCE="${GPUInstanceType}" 33 | export EMAIL_NOTIFICATION="${EmailNotification}" 34 | export SUBSCRIBE_NEWSLETTER="${SubscribeNewsLetter}" 35 | 36 | ./run-cdk.sh > /var/log/primehub-starter.log 2>&1 37 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --region ${AWS::Region} --resource PrimeHubCDKBootstrapEC2 38 | 39 | # delete the starter stack 40 | aws cloudformation delete-stack --stack-name ${AWS::StackName} --region ${AWS::Region} 41 | SecurityGroups: 42 | - !Ref PrimeHubCDKBootstrapEC2SecurityGroup 43 | AwsIamRole: 44 | Type: AWS::IAM::Role 45 | Properties: 46 | AssumeRolePolicyDocument: 47 | Version: '2012-10-17' 48 | Statement: 49 | - Effect: Allow 50 | Principal: 51 | Service: 52 | - ec2.amazonaws.com 53 | Action: 54 | - 'sts:AssumeRole' 55 | ManagedPolicyArns: 56 | - arn:aws:iam::aws:policy/AmazonEC2FullAccess 57 | - arn:aws:iam::aws:policy/AmazonRoute53FullAccess 58 | - arn:aws:iam::aws:policy/IAMReadOnlyAccess 59 | Path: / 60 | Policies: 61 | - PolicyName: CdkPolicy 62 | PolicyDocument: 63 | Version: '2012-10-17' 64 | Statement: 65 | - Action: 66 | - cloudformation:* 67 | Resource: '*' 68 | Effect: Allow 69 | - Condition: 70 | ForAnyValue:StringEquals: 71 | aws:CalledVia: 72 | - cloudformation.amazonaws.com 73 | Action: '*' 74 | Resource: '*' 75 | Effect: Allow 76 | - Action: s3:* 77 | Resource: arn:aws:s3:::cdktoolkit-stagingbucket-* 78 | Effect: Allow 79 | - Action: 80 | - sts:AssumeRole 81 | Effect: Allow 82 | Resource: 83 | - arn:aws:iam::*:role/cdk-* 84 | InstanceProfile: 85 | Type: 'AWS::IAM::InstanceProfile' 86 | Properties: 87 | Path: / 88 | Roles: 89 | - !Ref AwsIamRole 90 | PrimeHubCDKBootstrapEC2SecurityGroup: 91 | Type: AWS::EC2::SecurityGroup 92 | Properties: 93 | GroupDescription: PrimeHub CDK Bootstrap Security Group 94 | SecurityGroupIngress: 95 | - IpProtocol: tcp 96 | FromPort: 22 97 | ToPort: 22 98 | CidrIp: 0.0.0.0/0 99 | Mappings: 100 | AmazonLinux2RegionMap202107212: 101 | 'af-south-1': 102 | AMI: 'ami-0edec67949fd25461' 103 | 'eu-north-1': 104 | AMI: 'ami-0d441f5643da997cb' 105 | 'ap-south-1': 106 | AMI: 'ami-04db49c0fb2215364' 107 | 'eu-west-3': 108 | AMI: 'ami-0d49cec198762b78c' 109 | 'eu-west-2': 110 | AMI: 'ami-0d26eb3972b7f8c96' 111 | 'eu-south-1': 112 | AMI: 'ami-0fce326033a239d55' 113 | 'eu-west-1': 114 | AMI: 'ami-02b4e72b17337d6c1' 115 | 'ap-northeast-3': 116 | AMI: 'ami-0e787554e61105680' 117 | 'ap-northeast-2': 118 | AMI: 'ami-0a0de518b1fc4524c' 119 | 'me-south-1': 120 | AMI: 'ami-004b77593bd476317' 121 | 'ap-northeast-1': 122 | AMI: 'ami-09ebacdc178ae23b7' 123 | 'sa-east-1': 124 | AMI: 'ami-0f8243a5175208e08' 125 | 'ca-central-1': 126 | AMI: 'ami-02f84cf47c23f1769' 127 | 'ap-east-1': 128 | AMI: 'ami-0a2115f8cc0a3956b' 129 | 'ap-southeast-1': 130 | AMI: 'ami-0f511ead81ccde020' 131 | 'ap-southeast-2': 132 | AMI: 'ami-0aab712d6363da7f9' 133 | 'eu-central-1': 134 | AMI: 'ami-0453cb7b5f2b7fca2' 135 | 'us-east-1': 136 | AMI: 'ami-0c2b8ca1dad447f8a' 137 | 'us-east-2': 138 | AMI: 'ami-0443305dabd4be2bc' 139 | 'us-west-1': 140 | AMI: 'ami-04b6c97b14c54de18' 141 | 'us-west-2': 142 | AMI: 'ami-083ac7c7ecf9bb9b0' 143 | Parameters: 144 | EmailNotification: 145 | Type: String 146 | AllowedPattern: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" 147 | ConstraintDescription: Must be a valid email address. 148 | Description: Please provide an email for notifying the cluster installation status. If you don't subscribe our news latter, this will send credential only, no marketing material, and no spam 149 | SubscribeNewsLetter: 150 | Type: String 151 | AllowedValues: 152 | - "yes" 153 | - "no" 154 | Default: "no" 155 | Description: Subscribe our newsletter, stay up to date on MLOps insights and news 156 | PrimeHubInstallMode: 157 | Type: String 158 | Default: ee 159 | AllowedValues: 160 | - ee 161 | Description: PrimeHub Enterprise Edition Mode 162 | CPUInstanceType: 163 | Type: String 164 | Default: t3.xlarge 165 | AllowedValues: 166 | - t3.xlarge 167 | - t3.2xlarge 168 | - t3a.xlarge 169 | - t3a.2xlarge 170 | - m5.xlarge 171 | - m5.2xlarge 172 | - m5.4xlarge 173 | - m5.8xlarge 174 | - m5.12xlarge 175 | - c5.xlarge 176 | - c5.2xlarge 177 | - c5.4xlarge 178 | - c5.12xlarge 179 | Description: The Instance Type of auto scaling CPU node group 180 | GPUInstanceType: 181 | Type: String 182 | Default: g4dn.xlarge 183 | AllowedValues: 184 | - g4dn.xlarge 185 | - g4dn.2xlarge 186 | - g4dn.12xlarge 187 | Description: The Instance Type of auto scaling GPU node group 188 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -eo pipefail 3 | 4 | NAME= 5 | DRY_MODE=false 6 | PRIMEHUB_MODE=ee 7 | PRIMEHUB_VERSION='' 8 | K8S_INFRA_ONLY=false 9 | ENFORCE_UPDATE_PASSWORD=false 10 | AWS_ACCOUNT=${AWS_ACCOUNT:-} 11 | AWS_REGION=${AWS_REGION:-$(aws configure get region || echo 'ap-northeast-1')} 12 | AWS_ZONE=${AWS_ZONE:-a} 13 | CPU_INSTANCE='t3a.xlarge' 14 | GPU_INSTANCE='g4dn.xlarge' 15 | CPU_DESIRED_SIZE=0 16 | GPU_DESIRED_SIZE=0 17 | CPU_MAX_SIZE=2 18 | GPU_MAX_SIZE=2 19 | SCALE_DWON_DELAY=10 20 | SYS_INSTANCE='t3a.xlarge' 21 | PH_PASSWORD='' 22 | KC_PASSWORD='' 23 | PH_USER_EMAIL='' 24 | 25 | info() { 26 | echo -e "\033[0;32m$1\033[0m" 27 | } 28 | 29 | warn() { 30 | echo -e "\033[0;93m$1\033[0m" 31 | } 32 | 33 | error() { 34 | echo -e "\033[0;91m$1\033[0m" >&2 35 | } 36 | 37 | usage() { 38 | local SELF=`basename $0` 39 | cat < 41 | 42 | Options: 43 | --mode : Set the PrimeHub mode ( Default: ${PRIMEHUB_MODE}, Support: ee, ce, deploy ) 44 | --region : Set the AWS region ( Default: ${AWS_REGION} ) 45 | --zone : Set the AWS availability zones ( Default: ${AWS_ZONE} ) 46 | --dry-run : Dry run AWS CDK deploy 47 | --k8s-infra : Only setup AWS ESK without installing PrimeHub 48 | --enforce-update-password : Enforce default user reset password after first login 49 | --domain : Provide the base domain managed by AWS Route 53 50 | --email : Set the email of default user 51 | --primehub-version : Set the specific version number of PrimeHub 52 | --system-instance-type : Set the EKS default system node group instance type ( Default: ${SYS_INSTANCE} ) 53 | --cpu-instance-type : Set the EKS default CPU node group instance type ( Default: ${CPU_INSTANCE} ) 54 | --gpu-instance-type : Set the EKS default GPU node group instance type ( Default: ${GPU_INSTANCE} ) 55 | --cpu-desired-capacity : Set the EKS default CPU node group desired capacity ( Default: 0 ) 56 | --gpu-desired-capactiy : Set the EKS default GPU node group desired capacity ( Default: 0 ) 57 | --cpu-max-capacity : Set the EKS default CPU node group max capacity ( Default: 2 ) 58 | --gpu-max-capactiy : Set the EKS default GPU node group max capacity ( Default: 2 ) 59 | --scale-down-delay : Set the EKS autoscale scale down delay in minutes ( Default: 10 ) 60 | --keycloak-password : Set the password of Keycloak admin account 61 | --primehub-password : Set the password of PrimeHub default account 62 | -h, --help : Show this message 63 | EOF 64 | } 65 | 66 | node::version() { 67 | info "[Node] Version" 68 | node -v 69 | } 70 | 71 | node::packages() { 72 | info "\n[Fetch] Packages" 73 | yarn install 74 | echo "" 75 | } 76 | 77 | cdk::context() { 78 | if aws sts get-caller-identity | grep "Arn" | grep "assumed-role"; then 79 | export USERNAME=$(aws sts get-caller-identity | grep "Arn" | sed 's/.*:assumed-role\/\(.*\)\/.*/\1/') 80 | else 81 | export USERNAME=$(aws iam get-user | jq ".User.UserName" | sed -e 's/"//g') 82 | fi 83 | export NAME=${NAME:-'cdk'} 84 | export AWS_ACCOUNT=$(aws sts get-caller-identity | grep "Account" | sed 's/.*Account": "\(.*\)",/\1/' | sed 's/ //g') 85 | 86 | if [ -f "./cdk.context.json" ]; then 87 | warn "[Detect] The CDK context already exist. Backup it by cdk.context.bak" 88 | mv cdk.context.json cdk.context.bak 89 | fi 90 | 91 | if [ ! -f "./cdk.context.json" ] || ! (cat ./cdk.context.json | grep '"clusterType": "dev-eks"' > /dev/null ); then 92 | info "[Create] CDK context" 93 | AWS_BASED_DOMAIN=${AWS_BASED_DOMAIN:-''} 94 | PRIMEHUB_VERSION=${PRIMEHUB_VERSION:-''} 95 | PH_PASSWORD=${PH_PASSWORD:-"$(openssl rand -hex 16)"} 96 | KC_PASSWORD=${KC_PASSWORD:-"$(openssl rand -hex 16)"} 97 | ADMIN_UI_GRAPHQL_SECRET_KEY=$(openssl rand -hex 32) 98 | HUB_PROXY_SECRET_TOKEN=$(openssl rand -hex 32) 99 | 100 | cdk context \ 101 | -c clusterType=dev-eks \ 102 | -c username=$USERNAME \ 103 | -c name=$NAME \ 104 | -c region=$AWS_REGION \ 105 | -c zone=$AWS_ZONE \ 106 | -c email=$PH_USER_EMAIL \ 107 | -c primehubVersion=$PRIMEHUB_VERSION \ 108 | -c cpuInstance=$CPU_INSTANCE \ 109 | -c gpuInstance=$GPU_INSTANCE \ 110 | -c cpuDesiredCapacity=$CPU_DESIRED_SIZE \ 111 | -c gpuDesiredCapacity=$GPU_DESIRED_SIZE \ 112 | -c cpuMaxCapacity=$CPU_MAX_SIZE \ 113 | -c gpuMaxCapacity=$GPU_MAX_SIZE \ 114 | -c scaleDownDelay=$SCALE_DWON_DELAY \ 115 | -c systemInstance=$SYS_INSTANCE \ 116 | -c basedDomain=$AWS_BASED_DOMAIN \ 117 | -c primehubMode=$PRIMEHUB_MODE \ 118 | -c primehubPassword=$PH_PASSWORD \ 119 | -c keycloakPassword=$KC_PASSWORD \ 120 | -c GraphqlSecretKey=$ADMIN_UI_GRAPHQL_SECRET_KEY \ 121 | -c HubProxySecretKey=$HUB_PROXY_SECRET_TOKEN \ 122 | -c k8sInfraOnly=$K8S_INFRA_ONLY \ 123 | -c enforceUpdatePassword=$ENFORCE_UPDATE_PASSWORD \ 124 | --json --strict > cdk.context.tmp 125 | mv cdk.context.tmp cdk.context.json 126 | cdk context 127 | else 128 | info "[Found] CDK context" 129 | cdk context 130 | fi 131 | } 132 | 133 | cdk::deploy() { 134 | info "[Bootstrap] aws://${AWS_ACCOUNT}/${AWS_REGION}" 135 | cdk bootstrap --force aws://${AWS_ACCOUNT}/${AWS_REGION} 136 | 137 | info "[Deploy] eks-$NAME by $USERNAME" 138 | cdk deploy 139 | } 140 | 141 | cdk::dry() { 142 | info "[Dry Run] eks-$NAME by $USERNAME" 143 | cdk synth > cdk.cloudformation.out 144 | info "[Output] Please check the output from 'cdk.cloudformation.out'" 145 | } 146 | 147 | args::parse() { 148 | while (( "$#" )); do 149 | case "${1:-}" in 150 | --mode) 151 | shift 152 | if [[ "${1}" != 'ee' && "${1}" != 'ce' && "${1}" != 'deploy' ]]; then 153 | error "[Not Support] PrimeHub Mode '${1}' not support" 154 | usage 155 | exit 2 156 | fi 157 | PRIMEHUB_MODE=${1} 158 | ;; 159 | --region) 160 | shift 161 | AWS_REGION=${1} 162 | ;; 163 | --zone) 164 | shift 165 | AWS_ZONE=${1} 166 | ;; 167 | --email) 168 | shift 169 | PH_USER_EMAIL=${1} 170 | ;; 171 | --primehub-version) 172 | shift 173 | PRIMEHUB_VERSION=${1} 174 | ;; 175 | --cpu-instance-type) 176 | shift 177 | CPU_INSTANCE=${1} 178 | ;; 179 | --gpu-instance-type) 180 | shift 181 | GPU_INSTANCE=${1} 182 | ;; 183 | --system-instance-type) 184 | shift 185 | SYS_INSTANCE=${1} 186 | ;; 187 | --cpu-desired-capacity) 188 | shift 189 | CPU_DESIRED_SIZE=${1} 190 | ;; 191 | --gpu-desired-capacity) 192 | shift 193 | GPU_DESIRED_SIZE=${1} 194 | ;; 195 | --cpu-max-capacity) 196 | shift 197 | CPU_MAX_SIZE=${1} 198 | ;; 199 | --gpu-max-capacity) 200 | shift 201 | GPU_MAX_SIZE=${1} 202 | ;; 203 | --scale-down-delay) 204 | shift 205 | SCALE_DWON_DELAY=${1} 206 | ;; 207 | --dry-run) 208 | DRY_MODE=true 209 | ;; 210 | --domain) 211 | shift 212 | AWS_BASED_DOMAIN=${1} 213 | ;; 214 | --keycloak-password) 215 | shift 216 | KC_PASSWORD=${1} 217 | ;; 218 | --primehub-password) 219 | shift 220 | PH_PASSWORD=${1} 221 | ;; 222 | --k8s-infra) 223 | K8S_INFRA_ONLY=true 224 | ;; 225 | --enforce-update-password) 226 | ENFORCE_UPDATE_PASSWORD=true 227 | ;; 228 | -h|--help) 229 | usage 230 | exit 0 231 | ;; 232 | --debug) 233 | set -x 234 | ;; 235 | *) 236 | if [ "${NAME}" == "" ]; then 237 | NAME=$1 238 | else 239 | error "[Syntax error] Should only provide one cluster name" 240 | usage 241 | exit 1 242 | fi 243 | ;; 244 | esac 245 | shift || (usage; exit 1) 246 | done 247 | } 248 | 249 | main() { 250 | args::parse "$@" 251 | node::version 252 | node::packages 253 | cdk::context "$@" 254 | if [ "${DRY_MODE}" == "true" ]; then 255 | cdk::dry 256 | else 257 | cdk::deploy 258 | fi 259 | } 260 | 261 | main "$@" 262 | -------------------------------------------------------------------------------- /lib/aws-loadbalancer-controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | */ 17 | import * as cdk from '@aws-cdk/core'; 18 | import * as eks from '@aws-cdk/aws-eks'; 19 | import * as iam from '@aws-cdk/aws-iam'; 20 | 21 | export interface AwsLoadBalancerControllerProps { 22 | eksCluster: eks.ICluster; 23 | } 24 | 25 | interface HelmValues { 26 | [key: string]: unknown; 27 | } 28 | 29 | export class AwsLoadBalancerController extends cdk.Construct { 30 | constructor( 31 | scope: cdk.Construct, 32 | id: string, 33 | props: AwsLoadBalancerControllerProps 34 | ) { 35 | super(scope, id); 36 | 37 | // Create an K8S Service Account for AWS Load Balancer Controller on EKS cluster. 38 | // @aws-cdk/aws-eks module will also automatically create the corresponding IAM Role mapped via IRSA 39 | const awsLbControllerServiceAccount = props.eksCluster.addServiceAccount( 40 | 'aws-load-balancer-controller', 41 | { 42 | name: 'aws-load-balancer-controller', 43 | namespace: 'kube-system', 44 | } 45 | ); 46 | 47 | const lbAcmPolicyStatements = new iam.PolicyStatement({ 48 | effect: iam.Effect.ALLOW, 49 | actions: [ 50 | 'acm:DescribeCertificate', 51 | 'acm:ListCertificates', 52 | 'acm:GetCertificate', 53 | ], 54 | resources: ['*'], 55 | }); 56 | 57 | const lbEc2PolicyStatements = new iam.PolicyStatement({ 58 | effect: iam.Effect.ALLOW, 59 | actions: [ 60 | 'ec2:AuthorizeSecurityGroupIngress', 61 | 'ec2:CreateSecurityGroup', 62 | 'ec2:CreateTags', 63 | 'ec2:DeleteTags', 64 | 'ec2:DeleteSecurityGroup', 65 | 'ec2:DescribeAccountAttributes', 66 | 'ec2:DescribeAddresses', 67 | 'ec2:DescribeInstances', 68 | 'ec2:DescribeInstanceStatus', 69 | 'ec2:DescribeInternetGateways', 70 | 'ec2:DescribeNetworkInterfaces', 71 | 'ec2:DescribeSecurityGroups', 72 | 'ec2:DescribeSubnets', 73 | 'ec2:DescribeTags', 74 | 'ec2:DescribeVpcs', 75 | 'ec2:ModifyInstanceAttribute', 76 | 'ec2:ModifyNetworkInterfaceAttribute', 77 | 'ec2:RevokeSecurityGroupIngress', 78 | ], 79 | resources: ['*'], 80 | }); 81 | 82 | const lbElbPolicyStatements = new iam.PolicyStatement({ 83 | effect: iam.Effect.ALLOW, 84 | actions: [ 85 | 'elasticloadbalancing:AddListenerCertificates', 86 | 'elasticloadbalancing:AddTags', 87 | 'elasticloadbalancing:CreateListener', 88 | 'elasticloadbalancing:CreateLoadBalancer', 89 | 'elasticloadbalancing:CreateRule', 90 | 'elasticloadbalancing:CreateTargetGroup', 91 | 'elasticloadbalancing:DeleteListener', 92 | 'elasticloadbalancing:DeleteLoadBalancer', 93 | 'elasticloadbalancing:DeleteRule', 94 | 'elasticloadbalancing:DeleteTargetGroup', 95 | 'elasticloadbalancing:DeregisterTargets', 96 | 'elasticloadbalancing:DescribeListenerCertificates', 97 | 'elasticloadbalancing:DescribeListeners', 98 | 'elasticloadbalancing:DescribeLoadBalancers', 99 | 'elasticloadbalancing:DescribeLoadBalancerAttributes', 100 | 'elasticloadbalancing:DescribeRules', 101 | 'elasticloadbalancing:DescribeSSLPolicies', 102 | 'elasticloadbalancing:DescribeTags', 103 | 'elasticloadbalancing:DescribeTargetGroups', 104 | 'elasticloadbalancing:DescribeTargetGroupAttributes', 105 | 'elasticloadbalancing:DescribeTargetHealth', 106 | 'elasticloadbalancing:ModifyListener', 107 | 'elasticloadbalancing:ModifyLoadBalancerAttributes', 108 | 'elasticloadbalancing:ModifyRule', 109 | 'elasticloadbalancing:ModifyTargetGroup', 110 | 'elasticloadbalancing:ModifyTargetGroupAttributes', 111 | 'elasticloadbalancing:RegisterTargets', 112 | 'elasticloadbalancing:RemoveListenerCertificates', 113 | 'elasticloadbalancing:RemoveTags', 114 | 'elasticloadbalancing:SetIpAddressType', 115 | 'elasticloadbalancing:SetSecurityGroups', 116 | 'elasticloadbalancing:SetSubnets', 117 | 'elasticloadbalancing:SetWebAcl', 118 | ], 119 | resources: ['*'], 120 | }); 121 | 122 | const lbIamPolicyStatements = new iam.PolicyStatement({ 123 | effect: iam.Effect.ALLOW, 124 | actions: [ 125 | 'iam:CreateServiceLinkedRole', 126 | 'iam:GetServerCertificate', 127 | 'iam:ListServerCertificates', 128 | ], 129 | resources: ['*'], 130 | }); 131 | 132 | const lbCognitoPolicyStatements = new iam.PolicyStatement({ 133 | effect: iam.Effect.ALLOW, 134 | actions: ['cognito-idp:DescribeUserPoolClient'], 135 | resources: ['*'], 136 | }); 137 | 138 | const lbWafRegPolicyStatements = new iam.PolicyStatement({ 139 | effect: iam.Effect.ALLOW, 140 | actions: [ 141 | 'waf-regional:GetWebACLForResource', 142 | 'waf-regional:GetWebACL', 143 | 'waf-regional:AssociateWebACL', 144 | 'waf-regional:DisassociateWebACL', 145 | ], 146 | resources: ['*'], 147 | }); 148 | 149 | const lbTagPolicyStatements = new iam.PolicyStatement({ 150 | effect: iam.Effect.ALLOW, 151 | actions: ['tag:GetResources', 'tag:TagResources'], 152 | resources: ['*'], 153 | }); 154 | 155 | const lbWafPolicyStatements = new iam.PolicyStatement({ 156 | effect: iam.Effect.ALLOW, 157 | actions: ['waf:GetWebACL'], 158 | resources: ['*'], 159 | }); 160 | 161 | const lbWafv2PolicyStatements = new iam.PolicyStatement({ 162 | effect: iam.Effect.ALLOW, 163 | actions: [ 164 | 'wafv2:GetWebACL', 165 | 'wafv2:GetWebACLForResource', 166 | 'wafv2:AssociateWebACL', 167 | 'wafv2:DisassociateWebACL', 168 | ], 169 | resources: ['*'], 170 | }); 171 | 172 | const lbShieldPolicyStatements = new iam.PolicyStatement({ 173 | effect: iam.Effect.ALLOW, 174 | actions: [ 175 | 'shield:DescribeProtection', 176 | 'shield:GetSubscriptionState', 177 | 'shield:DeleteProtection', 178 | 'shield:CreateProtection', 179 | 'shield:DescribeSubscription', 180 | 'shield:ListProtections', 181 | ], 182 | resources: ['*'], 183 | }); 184 | 185 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbAcmPolicyStatements); 186 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbEc2PolicyStatements); 187 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbElbPolicyStatements); 188 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbIamPolicyStatements); 189 | awsLbControllerServiceAccount.addToPrincipalPolicy( 190 | lbCognitoPolicyStatements 191 | ); 192 | awsLbControllerServiceAccount.addToPrincipalPolicy( 193 | lbWafRegPolicyStatements 194 | ); 195 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbTagPolicyStatements); 196 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbWafPolicyStatements); 197 | awsLbControllerServiceAccount.addToPrincipalPolicy(lbWafv2PolicyStatements); 198 | awsLbControllerServiceAccount.addToPrincipalPolicy( 199 | lbShieldPolicyStatements 200 | ); 201 | 202 | // Deploy AWS LoadBalancer Controller from the Helm chart 203 | const stack = cdk.Stack.of(this); 204 | const lbHelmValues = {} as HelmValues; 205 | lbHelmValues.clusterName = props.eksCluster.clusterName; 206 | lbHelmValues.region = stack.region; 207 | lbHelmValues.vpcId = props.eksCluster.vpc.vpcId; 208 | lbHelmValues.serviceAccount = { 209 | create: false, 210 | name: 'aws-load-balancer-controller', 211 | }; 212 | props.eksCluster.addHelmChart('aws-load-balancer-controller', { 213 | chart: 'aws-load-balancer-controller', 214 | repository: 'https://aws.github.io/eks-charts', 215 | namespace: 'kube-system', 216 | values: lbHelmValues, 217 | }); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /lib/cluster-autoscaler.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | 4 | export interface ClusterAutoScalerProps { 5 | eksCluster: eks.ICluster; 6 | version: string; 7 | scaleDownDelay?: number; 8 | } 9 | 10 | export class ClusterAutoScaler extends cdk.Construct { 11 | constructor(scope: cdk.Construct, id: string, props: ClusterAutoScalerProps) { 12 | super(scope, id); 13 | const scaleDownDelayOpt = `${props.scaleDownDelay || 10}m`; 14 | 15 | new eks.KubernetesManifest(this, id, { 16 | cluster: props.eksCluster, 17 | manifest: [ 18 | { 19 | apiVersion: 'v1', 20 | kind: 'ServiceAccount', 21 | metadata: { 22 | name: 'cluster-autoscaler', 23 | namespace: 'kube-system', 24 | labels: { 25 | 'k8s-addon': 'cluster-autoscaler.addons.k8s.io', 26 | 'k8s-app': 'cluster-autoscaler', 27 | }, 28 | }, 29 | }, 30 | { 31 | apiVersion: 'rbac.authorization.k8s.io/v1', 32 | kind: 'ClusterRole', 33 | metadata: { 34 | name: 'cluster-autoscaler', 35 | namespace: 'kube-system', 36 | labels: { 37 | 'k8s-addon': 'cluster-autoscaler.addons.k8s.io', 38 | 'k8s-app': 'cluster-autoscaler', 39 | }, 40 | }, 41 | rules: [ 42 | { 43 | apiGroups: [''], 44 | resources: ['events', 'endpoints'], 45 | verbs: ['create', 'patch'], 46 | }, 47 | { 48 | apiGroups: [''], 49 | resources: ['pods/eviction'], 50 | verbs: ['create'], 51 | }, 52 | { 53 | apiGroups: [''], 54 | resources: ['pods/status'], 55 | verbs: ['update'], 56 | }, 57 | { 58 | apiGroups: [''], 59 | resources: ['endpoints'], 60 | resourceNames: ['cluster-autoscaler'], 61 | verbs: ['get', 'update'], 62 | }, 63 | { 64 | apiGroups: ['coordination.k8s.io'], 65 | resources: ['leases'], 66 | verbs: ['watch', 'list', 'get', 'patch', 'create', 'update'], 67 | }, 68 | { 69 | apiGroups: [''], 70 | resources: ['nodes'], 71 | verbs: ['watch', 'list', 'get', 'update'], 72 | }, 73 | { 74 | apiGroups: [''], 75 | resources: [ 76 | 'pods', 77 | 'services', 78 | 'replicationcontrollers', 79 | 'persistentvolumeclaims', 80 | 'persistentvolumes', 81 | ], 82 | verbs: ['watch', 'list', 'get'], 83 | }, 84 | { 85 | apiGroups: ['extensions'], 86 | resources: ['replicasets', 'daemonsets'], 87 | verbs: ['watch', 'list', 'get'], 88 | }, 89 | { 90 | apiGroups: ['policy'], 91 | resources: ['poddisruptionbudgets'], 92 | verbs: ['watch', 'list'], 93 | }, 94 | { 95 | apiGroups: ['apps'], 96 | resources: ['statefulsets', 'replicasets', 'daemonsets'], 97 | verbs: ['watch', 'list', 'get'], 98 | }, 99 | { 100 | apiGroups: ['storage.k8s.io'], 101 | resources: [ 102 | 'storageclasses', 103 | 'csinodes', 104 | 'csistoragecapacities', 105 | 'csidrivers', 106 | ], 107 | verbs: ['watch', 'list', 'get'], 108 | }, 109 | { 110 | apiGroups: ['batch', 'extensions'], 111 | resources: ['jobs'], 112 | verbs: ['get', 'list', 'watch', 'patch'], 113 | }, 114 | ], 115 | }, 116 | { 117 | apiVersion: 'rbac.authorization.k8s.io/v1', 118 | kind: 'Role', 119 | metadata: { 120 | name: 'cluster-autoscaler', 121 | namespace: 'kube-system', 122 | labels: { 123 | 'k8s-addon': 'cluster-autoscaler.addons.k8s.io', 124 | 'k8s-app': 'cluster-autoscaler', 125 | }, 126 | }, 127 | rules: [ 128 | { 129 | apiGroups: [''], 130 | resources: ['configmaps'], 131 | verbs: ['create', 'list', 'watch'], 132 | }, 133 | { 134 | apiGroups: [''], 135 | resources: ['configmaps'], 136 | resourceNames: [ 137 | 'cluster-autoscaler-status', 138 | 'cluster-autoscaler-priority-expander', 139 | ], 140 | verbs: ['delete', 'get', 'update', 'watch'], 141 | }, 142 | ], 143 | }, 144 | { 145 | apiVersion: 'rbac.authorization.k8s.io/v1', 146 | kind: 'ClusterRoleBinding', 147 | metadata: { 148 | name: 'cluster-autoscaler', 149 | namespace: 'kube-system', 150 | labels: { 151 | 'k8s-addon': 'cluster-autoscaler.addons.k8s.io', 152 | 'k8s-app': 'cluster-autoscaler', 153 | }, 154 | }, 155 | roleRef: { 156 | apiGroup: 'rbac.authorization.k8s.io', 157 | kind: 'ClusterRole', 158 | name: 'cluster-autoscaler', 159 | }, 160 | subjects: [ 161 | { 162 | kind: 'ServiceAccount', 163 | name: 'cluster-autoscaler', 164 | namespace: 'kube-system', 165 | }, 166 | ], 167 | }, 168 | { 169 | apiVersion: 'rbac.authorization.k8s.io/v1', 170 | kind: 'RoleBinding', 171 | metadata: { 172 | name: 'cluster-autoscaler', 173 | namespace: 'kube-system', 174 | labels: { 175 | 'k8s-addon': 'cluster-autoscaler.addons.k8s.io', 176 | 'k8s-app': 'cluster-autoscaler', 177 | }, 178 | }, 179 | roleRef: { 180 | apiGroup: 'rbac.authorization.k8s.io', 181 | kind: 'Role', 182 | name: 'cluster-autoscaler', 183 | }, 184 | subjects: [ 185 | { 186 | kind: 'ServiceAccount', 187 | name: 'cluster-autoscaler', 188 | namespace: 'kube-system', 189 | }, 190 | ], 191 | }, 192 | { 193 | apiVersion: 'apps/v1', 194 | kind: 'Deployment', 195 | metadata: { 196 | name: 'cluster-autoscaler', 197 | namespace: 'kube-system', 198 | labels: { 199 | app: 'cluster-autoscaler', 200 | }, 201 | annotations: { 202 | 'cluster-autoscaler.kubernetes.io/safe-to-evict': 'false', 203 | }, 204 | }, 205 | spec: { 206 | replicas: 1, 207 | selector: { 208 | matchLabels: { 209 | app: 'cluster-autoscaler', 210 | }, 211 | }, 212 | template: { 213 | metadata: { 214 | labels: { 215 | app: 'cluster-autoscaler', 216 | }, 217 | annotations: { 218 | 'prometheus.io/scrape': 'true', 219 | 'prometheus.io/port': '8085', 220 | }, 221 | }, 222 | spec: { 223 | serviceAccountName: 'cluster-autoscaler', 224 | containers: [ 225 | { 226 | image: 227 | 'k8s.gcr.io/autoscaling/cluster-autoscaler:' + 228 | props.version, 229 | name: 'cluster-autoscaler', 230 | resources: { 231 | limits: { 232 | cpu: '100m', 233 | memory: '500Mi', 234 | }, 235 | requests: { 236 | cpu: '100m', 237 | memory: '300Mi', 238 | }, 239 | }, 240 | command: [ 241 | './cluster-autoscaler', 242 | '--v=4', 243 | '--stderrthreshold=info', 244 | '--cloud-provider=aws', 245 | '--skip-nodes-with-local-storage=false', 246 | '--expander=least-waste', 247 | '--node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/' + 248 | props.eksCluster.clusterName, 249 | '--balance-similar-node-groups', 250 | '--skip-nodes-with-system-pods=false', 251 | `--scale-down-delay-after-add=${scaleDownDelayOpt}`, 252 | ], 253 | volumeMounts: [ 254 | { 255 | name: 'ssl-certs', 256 | mountPath: '/etc/ssl/certs/ca-certificates.crt', 257 | readOnly: true, 258 | }, 259 | ], 260 | imagePullPolicy: 'Always', 261 | }, 262 | ], 263 | volumes: [ 264 | { 265 | name: 'ssl-certs', 266 | hostPath: { 267 | path: '/etc/ssl/certs/ca-bundle.crt', 268 | }, 269 | }, 270 | ], 271 | }, 272 | }, 273 | }, 274 | }, 275 | ], 276 | }); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /lib/primehub.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as eks from '@aws-cdk/aws-eks'; 3 | import * as s3 from '@aws-cdk/aws-s3'; 4 | import * as s3deploy from '@aws-cdk/aws-s3-deployment'; 5 | import * as crypto from 'crypto'; 6 | import * as YAML from 'yaml'; 7 | import { writeFileSync, mkdirSync } from 'fs'; 8 | 9 | export interface PrimeHubProps { 10 | eksCluster: eks.ICluster; 11 | clusterName: string; 12 | ecrRepoName: string; 13 | primehubMode: string; 14 | primehubDomain: string; 15 | primehubUserEmail: string; 16 | acmeEnabled: boolean; 17 | sshCustomHostname?: string; 18 | primehubVersion?: string; 19 | primehubPassword: string; 20 | keycloakPassword: string; 21 | account: string; 22 | region: string; 23 | primehubConfigBucket: string; 24 | sharedVolumeStorageClass?: string; 25 | primehubStoreBucket?: string; 26 | dryRunMode?: boolean; 27 | cpuInstance: string; 28 | gpuInstance: string; 29 | enforceUpdatePassword?: boolean; 30 | } 31 | 32 | interface HelmValues { 33 | [key: string]: any; 34 | } 35 | 36 | interface Toleration { 37 | key: string; 38 | operator: string; 39 | effect: string; 40 | value?: string; 41 | } 42 | 43 | export class PrimeHub extends cdk.Construct { 44 | constructor(scope: cdk.Construct, id: string, props: PrimeHubProps) { 45 | super(scope, id); 46 | 47 | const graphqlSecretKey = 48 | scope.node.tryGetContext('GraphqlSecretKey') || 49 | process.env.ADMIN_UI_GRAPHQL_SECRET_KEY || 50 | crypto.randomBytes(32).toString('hex'); 51 | const hubProxySecretKey = 52 | scope.node.tryGetContext('HubProxySecretKey') || 53 | process.env.HUB_PROXY_SECRET_TOKEN || 54 | crypto.randomBytes(32).toString('hex'); 55 | 56 | const enabledPrimeHubStore = props.primehubStoreBucket ? true : false; 57 | const instanceTypes = [ 58 | PrimeHub.generateInstanceTypeConfig(props.cpuInstance), 59 | PrimeHub.generateInstanceTypeConfig(props.gpuInstance), 60 | ].flat(); 61 | 62 | const helmValues = { 63 | customImage: { 64 | registryEndpoint: `https://${props.account}.dkr.ecr.${props.region}.amazonaws.com`, 65 | pushRepo: `${props.account}.dkr.ecr.${props.region}.amazonaws.com/${props.ecrRepoName}`, 66 | pushSecretName: 'aws-registry', 67 | }, 68 | primehub: { 69 | domain: props.primehubDomain, 70 | mode: props.primehubMode, 71 | scheme: 'https', 72 | sharedVolumeStorageClass: props.sharedVolumeStorageClass, 73 | }, 74 | keycloak: { 75 | password: props.keycloakPassword, 76 | postgresql: { 77 | persistence: { 78 | storageClass: 'gp2', 79 | }, 80 | }, 81 | eula: { 82 | enabled: true, 83 | }, 84 | }, 85 | ingress: { 86 | enabled: true, 87 | annotations: { 88 | 'ingress.kubernetes.io/affinity': 'cookie', 89 | 'kubernetes.io/ingress.class': 'nginx', 90 | 'kubernetes.io/tls-acme': props.acmeEnabled ? 'true' : 'false', 91 | }, 92 | hosts: [props.primehubDomain], 93 | tls: [{ hosts: [props.primehubDomain], secretName: 'hub-tls' }], 94 | }, 95 | groupvolume: { 96 | storageClass: 'gp2', 97 | }, 98 | console: { 99 | npsSurvey: { 100 | enabled: true, 101 | }, 102 | }, 103 | bootstrap: { 104 | email: props.primehubUserEmail, 105 | password: props.primehubPassword, 106 | enforceUpdatePassword: props.enforceUpdatePassword || false, 107 | instanceTypes: instanceTypes, 108 | }, 109 | graphql: { 110 | sharedGraphqlSecret: graphqlSecretKey, 111 | }, 112 | usage: { 113 | dbStorageClass: 'gp2', 114 | }, 115 | store: { 116 | enabled: enabledPrimeHubStore, 117 | bucket: props.primehubStoreBucket, 118 | createBucket: { 119 | enabled: false, 120 | }, 121 | }, 122 | minio: { 123 | s3gateway: { 124 | enabled: enabledPrimeHubStore, 125 | serviceEndpoint: `https://s3.${props.region}.amazonaws.com/`, 126 | accessKey: null, 127 | secretKey: null, 128 | }, 129 | }, 130 | jupyterhub: { 131 | auth: { 132 | state: { 133 | cryptoKey: graphqlSecretKey, 134 | }, 135 | }, 136 | hub: { 137 | db: { 138 | pvc: { 139 | storageClassName: 'gp2', 140 | }, 141 | }, 142 | extraEnv: [ 143 | { 144 | name: 'KC_CLIENT_SECRET', 145 | valueFrom: { 146 | secretKeyRef: { 147 | name: 'primehub-client-jupyterhub', 148 | key: 'client_secret', 149 | }, 150 | }, 151 | }, 152 | { 153 | name: 'GRAPHQL_SHARED_SECRET', 154 | valueFrom: { 155 | secretKeyRef: { 156 | name: 'primehub-graphql-shared-secret', 157 | key: 'sharedSecret', 158 | }, 159 | }, 160 | }, 161 | { 162 | name: 'OAUTH_CALLBACK_URL', 163 | value: `https://${props.primehubDomain}/hub/oauth_callback`, 164 | }, 165 | ], 166 | }, 167 | proxy: { 168 | secretToken: hubProxySecretKey, 169 | }, 170 | scheduling: { 171 | userScheduler: { 172 | enabled: true, 173 | replicas: 1, 174 | image: { 175 | tag: 'v1.19.8', 176 | }, 177 | }, 178 | podPriority: { 179 | enabled: true, 180 | }, 181 | userPlaceholder: { 182 | enabled: false, 183 | }, 184 | userPods: { 185 | nodeAffinity: { 186 | matchNodePurpose: 'require', 187 | }, 188 | }, 189 | }, 190 | }, 191 | sshBastionServer: { 192 | enabled: true, 193 | customHostname: props.sshCustomHostname ? props.sshCustomHostname : '', 194 | }, 195 | } as HelmValues; 196 | 197 | if (!props.dryRunMode) { 198 | // Apply PrimeHub helm chart 199 | props.eksCluster.addHelmChart('primehub', { 200 | chart: 'primehub', 201 | repository: 'https://charts.infuseai.io', 202 | createNamespace: true, 203 | namespace: 'hub', 204 | release: 'primehub', 205 | values: helmValues, 206 | version: props.primehubVersion || '', 207 | timeout: cdk.Duration.minutes(15), 208 | wait: false, 209 | }); 210 | } 211 | 212 | const primehubEnv = `PRIMEHUB_MODE=${props.primehubMode} 213 | PRIMEHUB_NAMESPACE=hub 214 | PRIMEHUB_DOMAIN=${props.primehubDomain} 215 | PRIMEHUB_SCHEME=https 216 | PRIMEHUB_STORAGE_CLASS=gp2 217 | GROUP_VOLUME_STORAGE_CLASS=${props.sharedVolumeStorageClass} 218 | KC_DOMAIN=${props.primehubDomain} 219 | KC_SCHEME=https 220 | KC_USER=keycloak 221 | KC_PASSWORD=${props.keycloakPassword} 222 | KC_REALM=primehub 223 | ADMIN_UI_GRAPHQL_SECRET_KEY=${graphqlSecretKey} 224 | HUB_AUTH_STATE_CRYPTO_KEY=${graphqlSecretKey} 225 | HUB_PROXY_SECRET_TOKEN=${hubProxySecretKey} 226 | PH_PASSWORD=${props.primehubPassword} 227 | METACONTROLLER_DEPLOY=true 228 | KEYCLOAK_DEPLOY=true`; 229 | const primehubHelmOverride = new YAML.Document(); 230 | primehubHelmOverride.contents = helmValues; 231 | mkdirSync(`./artifact/${props.clusterName}/helm_override`, { 232 | recursive: true, 233 | }); 234 | writeFileSync( 235 | `./artifact/${props.clusterName}/helm_override/primehub.yaml`, 236 | primehubHelmOverride.toString() 237 | ); 238 | writeFileSync(`./artifact/${props.clusterName}/.env`, primehubEnv); 239 | 240 | // Create S3 bucket to store primehub.yaml 241 | const bucket = new s3.Bucket( 242 | this, 243 | `${props.primehubConfigBucket}-s3-bucket`, 244 | { 245 | autoDeleteObjects: true, 246 | removalPolicy: cdk.RemovalPolicy.DESTROY, 247 | bucketName: props.primehubConfigBucket, 248 | } 249 | ); 250 | 251 | new s3deploy.BucketDeployment(this, 'primehub-yaml', { 252 | sources: [s3deploy.Source.asset(`./artifact/${props.clusterName}`)], 253 | destinationBucket: bucket, 254 | }); 255 | } 256 | 257 | static calculateInstanceSize( 258 | instanceClass: string, 259 | instanceSize: string 260 | ): number[] { 261 | let cpu: number = 2; 262 | let memory: number = 8; 263 | let gpu: number = 0; 264 | 265 | const instanceSizeMap: HelmValues = { 266 | xlarge: { 267 | cpu: 4, 268 | memory: 16, 269 | }, 270 | '2xlarge': { 271 | cpu: 8, 272 | memory: 32, 273 | }, 274 | '4xlarge': { 275 | cpu: 16, 276 | memory: 64, 277 | }, 278 | '8xlarge': { 279 | cpu: 32, 280 | memory: 128, 281 | }, 282 | '12xlarge': { 283 | cpu: 48, 284 | memory: 192, 285 | }, 286 | '16xlarge': { 287 | cpu: 64, 288 | memory: 256, 289 | }, 290 | '24xlarge': { 291 | cpu: 96, 292 | memory: 384, 293 | }, 294 | metal: { 295 | cpu: 96, 296 | memory: 384, 297 | }, 298 | }; 299 | 300 | if (instanceSizeMap[instanceSize]) { 301 | cpu = instanceSizeMap[instanceSize].cpu; 302 | memory = instanceSizeMap[instanceSize].memory; 303 | } 304 | 305 | // AWS EC2 Compute Optimized Instance has less memory 306 | // Ref: https://aws.amazon.com/ec2/instance-types/ 307 | if (instanceClass.startsWith('c')) { 308 | memory = memory / 2; 309 | } 310 | 311 | // AWS EC2 Compute Optimized 312 | if (instanceClass === 'g4dn') { 313 | switch (instanceSize) { 314 | case '12xlarge': 315 | gpu = 4; 316 | break; 317 | case 'metal': 318 | gpu = 8; 319 | default: 320 | gpu = 1; 321 | break; 322 | } 323 | } else if (instanceClass === 'p3' || instanceClass === 'p3dn') { 324 | switch (instanceSize) { 325 | case '2xlarge': 326 | gpu = 1; 327 | break; 328 | case '8xlarge': 329 | gpu = 4; 330 | break; 331 | case '16xlarge': 332 | gpu = 8; 333 | break; 334 | case '24xlarge': 335 | gpu = 8; 336 | break; 337 | } 338 | } 339 | 340 | return [cpu, memory, gpu]; 341 | } 342 | 343 | static generateInstanceTypeConfig(instanceType: string): HelmValues[] { 344 | const instanceTypesConfig: HelmValues[] = []; 345 | const [instanceClass, instanceSize] = instanceType.split('.'); 346 | const [cpu, memory, gpu] = this.calculateInstanceSize( 347 | instanceClass, 348 | instanceSize 349 | ); 350 | const groupType = gpu > 0 ? 'GPU' : 'CPU'; 351 | const gpuToleration = { 352 | key: 'nvidia.com/gpu', 353 | operator: 'Exists', 354 | effect: 'NoSchedule', 355 | }; 356 | 357 | // Quarter Instance 358 | if (cpu / 4 >= 1) { 359 | const quarterGpu = gpu / 4 >= 1 ? gpu / 4 : gpu; 360 | const quarterCpu = cpu / 4; 361 | const quarterMemory = memory / 4; 362 | const quarterInstance = { 363 | metadata: { 364 | name: `${instanceType}-quarter`, 365 | }, 366 | spec: { 367 | description: `CPU: ${quarterCpu} / Memory: ${quarterMemory}G / GPU: ${quarterGpu}`, 368 | displayName: `[${groupType}] ${instanceType} -> Quarter`, 369 | 'limits.cpu': quarterCpu, 370 | 'limits.memory': `${quarterMemory}G`, 371 | 'limits.nvidia.com/gpu': quarterGpu, 372 | 'requests.cpu': (quarterCpu * 8) / 10, 373 | 'requests.memory': `${(quarterMemory * 8) / 10}G`, 374 | nodeSelector: { 375 | 'instance-type': instanceType, 376 | component: 'singleuser-server', 377 | }, 378 | tolerations: [ 379 | { 380 | key: 'hub.jupyter.org/dedicated', 381 | operator: 'Equal', 382 | value: 'user', 383 | effect: 'NoSchedule', 384 | } as Toleration, 385 | ], 386 | }, 387 | }; 388 | if (quarterGpu > 0) { 389 | quarterInstance.spec.tolerations.push({ ...gpuToleration }); 390 | } 391 | instanceTypesConfig.push(quarterInstance); 392 | } 393 | 394 | // Half Instance 395 | const halfGpu = gpu / 2 >= 1 ? gpu / 2 : gpu; 396 | const halfCpu = cpu / 2; 397 | const halfMemory = memory / 2; 398 | const halfInstance = { 399 | metadata: { 400 | name: `${instanceType}-half`, 401 | }, 402 | spec: { 403 | description: `CPU: ${halfCpu} / Memory: ${halfMemory}G / GPU: ${halfGpu}`, 404 | displayName: `[${groupType}] ${instanceType} -> Half`, 405 | 'limits.cpu': halfCpu, 406 | 'limits.memory': `${halfMemory}G`, 407 | 'limits.nvidia.com/gpu': halfGpu, 408 | 'requests.cpu': (halfCpu * 8) / 10, 409 | 'requests.memory': `${(halfMemory * 8) / 10}G`, 410 | nodeSelector: { 411 | 'instance-type': instanceType, 412 | component: 'singleuser-server', 413 | }, 414 | tolerations: [ 415 | { 416 | key: 'hub.jupyter.org/dedicated', 417 | operator: 'Equal', 418 | value: 'user', 419 | effect: 'NoSchedule', 420 | } as Toleration, 421 | ], 422 | }, 423 | }; 424 | if (gpu > 0) { 425 | halfInstance.spec.tolerations.push({ ...gpuToleration }); 426 | } 427 | instanceTypesConfig.push(halfInstance); 428 | 429 | // Full Instance 430 | const fullInstance = { 431 | metadata: { 432 | name: `${instanceType}-full`, 433 | }, 434 | spec: { 435 | description: `CPU: ${cpu} / Memory: ${memory}G / GPU: ${gpu}`, 436 | displayName: `[${groupType}] ${instanceType} -> Full`, 437 | 'limits.cpu': cpu, 438 | 'limits.memory': `${memory}G`, 439 | 'limits.nvidia.com/gpu': gpu, 440 | 'requests.cpu': (cpu * 8) / 10, 441 | 'requests.memory': `${(memory * 8) / 10}G`, 442 | nodeSelector: { 443 | 'instance-type': instanceType, 444 | component: 'singleuser-server', 445 | }, 446 | tolerations: [ 447 | { 448 | key: 'hub.jupyter.org/dedicated', 449 | operator: 'Equal', 450 | value: 'user', 451 | effect: 'NoSchedule', 452 | } as Toleration, 453 | ], 454 | }, 455 | }; 456 | if (gpu > 0) { 457 | fullInstance.spec.tolerations.push({ ...gpuToleration }); 458 | } 459 | instanceTypesConfig.push(fullInstance); 460 | 461 | return instanceTypesConfig; 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /lib/eks-cluster-Stack.ts: -------------------------------------------------------------------------------- 1 | import s3 = require('@aws-cdk/aws-s3'); 2 | import iam = require('@aws-cdk/aws-iam'); 3 | import ec2 = require('@aws-cdk/aws-ec2'); 4 | import efs = require('@aws-cdk/aws-efs'); 5 | import eks = require('@aws-cdk/aws-eks'); 6 | import cdk = require('@aws-cdk/core'); 7 | import route53 = require('@aws-cdk/aws-route53'); 8 | import ecr = require('@aws-cdk/aws-ecr'); 9 | import crypto = require('crypto'); 10 | import cf = require('@aws-cdk/aws-cloudfront'); 11 | import cfo = require('@aws-cdk/aws-cloudfront-origins'); 12 | 13 | import { InstanceType } from '@aws-cdk/aws-ec2'; 14 | import { ClusterAutoScaler } from './cluster-autoscaler'; 15 | import { IngressNginxController } from './nginx-ingress'; 16 | import { CertManager } from './cert-manager'; 17 | import { PrimeHub } from './primehub'; 18 | import { NvidiaDevicePlugin } from './nvidia-device-plugin'; 19 | import { AwsEfsCsiDriver } from './aws-efs-csi-driver'; 20 | import { BlockDeviceVolume } from '@aws-cdk/aws-autoscaling'; 21 | 22 | export interface EksStackProps extends cdk.StackProps { 23 | name: string; 24 | username: string; 25 | email: string; 26 | primehubMode: string; 27 | basedDomain: string; 28 | primehubPassword: string; 29 | keycloakPassword: string; 30 | availabilityZone: string; 31 | cpuInstance: string; 32 | gpuInstance: string; 33 | systemInstance: string; 34 | cpuDesiredCapacity: number; 35 | gpuDesiredCapacity: number; 36 | cpuMaxCapacity: number; 37 | gpuMaxCapacity: number; 38 | scaleDownDelay: number; 39 | masterRole?: string; 40 | k8sInfraOnly?: string; 41 | primehubVersion?: string; 42 | enforceUpdatePassword?: boolean; 43 | } 44 | 45 | export class EKSCluster extends cdk.Stack { 46 | constructor(scope: cdk.App, id: string, props: EksStackProps) { 47 | super(scope, id, props); 48 | let masterRole; 49 | let primehubDomain; 50 | const env: cdk.Environment = props.env || {}; 51 | const account: string = env.account || ''; 52 | const region: string = env.region || ''; 53 | const clusterName = `eks-${props.name}`; 54 | const randomHash = crypto.randomBytes(4).toString('hex'); 55 | 56 | const vpc = new ec2.Vpc(this, 'vpc', { 57 | enableDnsHostnames: true, 58 | enableDnsSupport: true, 59 | natGateways: 0, 60 | }); // Create a new VPC for our cluster 61 | 62 | // cluster master role 63 | if (props.masterRole) { 64 | masterRole = iam.Role.fromRoleArn( 65 | this, 66 | 'imported-master-rold', 67 | props.masterRole 68 | ); 69 | } else { 70 | masterRole = new iam.Role(this, 'eks-master-role', { 71 | roleName: `${clusterName}-master-role`, 72 | assumedBy: new iam.AnyPrincipal(), 73 | }); 74 | } 75 | 76 | const eksCluster = new eks.Cluster(this, 'Cluster', { 77 | version: eks.KubernetesVersion.V1_21, 78 | mastersRole: masterRole, 79 | clusterName: clusterName, 80 | outputClusterName: true, 81 | 82 | // Networking related settings listed below - important in enterprise context. 83 | endpointAccess: eks.EndpointAccess.PUBLIC, // In Enterprise context, you may want to set it to PRIVATE. 84 | vpc: vpc, 85 | vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }], // you can also specify the subnets by other attributes 86 | defaultCapacity: 0, 87 | }); 88 | 89 | const defaultNodeGroup = eksCluster.addNodegroupCapacity( 90 | 'Default-Node-Group', 91 | { 92 | nodegroupName: 'default-node-group', 93 | desiredSize: 1, 94 | minSize: 1, 95 | maxSize: 3, 96 | diskSize: 60, 97 | instanceTypes: [new InstanceType(props.systemInstance)], 98 | subnets: { 99 | subnetType: ec2.SubnetType.PUBLIC, 100 | availabilityZones: [props.availabilityZone], 101 | }, 102 | tags: { 103 | Name: `${clusterName}-default-node-group`, 104 | cluster: clusterName, 105 | owner: props.username, 106 | InstanceType: props.systemInstance, 107 | clusterType: 'dev-eks', 108 | }, 109 | } 110 | ); 111 | 112 | const cpuASG = eksCluster.addAutoScalingGroupCapacity('OnDemandCpuASG', { 113 | autoScalingGroupName: `${clusterName}-scaled-cpu-pool`, 114 | desiredCapacity: props.cpuDesiredCapacity, 115 | minCapacity: 0, 116 | maxCapacity: props.cpuMaxCapacity, 117 | instanceType: new InstanceType(props.cpuInstance), 118 | blockDevices: [ 119 | { deviceName: '/dev/xvda', volume: BlockDeviceVolume.ebs(80) }, 120 | ], 121 | vpcSubnets: { 122 | subnetType: ec2.SubnetType.PUBLIC, 123 | availabilityZones: [props.availabilityZone], 124 | }, 125 | bootstrapOptions: { 126 | kubeletExtraArgs: `--node-labels=component=singleuser-server,hub.jupyter.org/node-purpose=user,instance-type=${props.cpuInstance} --register-with-taints=hub.jupyter.org/dedicated=user:NoSchedule`, 127 | }, 128 | }); 129 | cdk.Tags.of(cpuASG).add('Name', `${clusterName}-scaled-cpu-pool`); 130 | cdk.Tags.of(cpuASG).add('cluster', clusterName); 131 | cdk.Tags.of(cpuASG).add('owner', props.username); 132 | cdk.Tags.of(cpuASG).add('instanceType', props.cpuInstance); 133 | cdk.Tags.of(cpuASG).add('clusterType', 'dev-eks'); 134 | cdk.Tags.of(cpuASG).add( 135 | `k8s.io/cluster-autoscaler/${clusterName}`, 136 | 'owned' 137 | ); 138 | cdk.Tags.of(cpuASG).add('k8s.io/cluster-autoscaler/enabled', 'TRUE'); 139 | cdk.Tags.of(cpuASG).add( 140 | 'k8s.io/cluster-autoscaler/node-template/label/auto-scaler', 141 | 'enabled' 142 | ); 143 | cdk.Tags.of(cpuASG).add( 144 | 'k8s.io/cluster-autoscaler/node-template/label/instance-type', 145 | props.cpuInstance 146 | ); 147 | cdk.Tags.of(cpuASG).add( 148 | 'k8s.io/cluster-autoscaler/node-template/label/component', 149 | 'singleuser-server' 150 | ); 151 | cdk.Tags.of(cpuASG).add( 152 | 'k8s.io/cluster-autoscaler/node-template/label/hub.jupyter.org/node-purpose', 153 | 'user' 154 | ); 155 | cdk.Tags.of(cpuASG).add( 156 | 'k8s.io/cluster-autoscaler/node-template/taint/hub.jupyter.org/dedicated', 157 | 'user:NoSchedule' 158 | ); 159 | 160 | const gpuASG = eksCluster.addAutoScalingGroupCapacity('OnDemandGpuASG', { 161 | autoScalingGroupName: `${clusterName}-scaled-gpu-pool`, 162 | desiredCapacity: props.gpuDesiredCapacity, 163 | minCapacity: 0, 164 | maxCapacity: props.gpuMaxCapacity, 165 | instanceType: new InstanceType(props.gpuInstance), 166 | blockDevices: [ 167 | { deviceName: '/dev/xvda', volume: BlockDeviceVolume.ebs(80) }, 168 | ], 169 | vpcSubnets: { 170 | subnetType: ec2.SubnetType.PUBLIC, 171 | availabilityZones: [props.availabilityZone], 172 | }, 173 | bootstrapOptions: { 174 | kubeletExtraArgs: `--node-labels=component=singleuser-server,hub.jupyter.org/node-purpose=user,instance-type=${props.gpuInstance},nvidia.com/gpu=true --register-with-taints=nvidia.com/gpu=true:NoSchedule`, 175 | dockerConfigJson: '{ "exec-opts": ["native.cgroupdriver=systemd"] }', 176 | }, 177 | }); 178 | cdk.Tags.of(gpuASG).add('Name', `${clusterName}-scaled-gpu-pool`); 179 | cdk.Tags.of(gpuASG).add('cluster', clusterName); 180 | cdk.Tags.of(gpuASG).add('owner', props.username); 181 | cdk.Tags.of(gpuASG).add('instanceType', props.gpuInstance); 182 | cdk.Tags.of(gpuASG).add('clusterType', 'dev-eks'); 183 | cdk.Tags.of(gpuASG).add( 184 | `k8s.io/cluster-autoscaler/${clusterName}`, 185 | 'owned' 186 | ); 187 | cdk.Tags.of(gpuASG).add('k8s.io/cluster-autoscaler/enabled', 'TRUE'); 188 | cdk.Tags.of(gpuASG).add( 189 | 'k8s.io/cluster-autoscaler/node-template/label/auto-scaler', 190 | 'enabled' 191 | ); 192 | cdk.Tags.of(gpuASG).add( 193 | 'k8s.io/cluster-autoscaler/node-template/label/instance-type', 194 | props.gpuInstance 195 | ); 196 | cdk.Tags.of(gpuASG).add( 197 | 'k8s.io/cluster-autoscaler/node-template/label/component', 198 | 'singleuser-server' 199 | ); 200 | cdk.Tags.of(gpuASG).add( 201 | 'k8s.io/cluster-autoscaler/node-template/label/hub.jupyter.org/node-purpose', 202 | 'user' 203 | ); 204 | cdk.Tags.of(gpuASG).add( 205 | 'k8s.io/cluster-autoscaler/node-template/taint/nvidia.com/gpu', 206 | 'true:NoSchedule' 207 | ); 208 | 209 | // Nvidia device Plugin 210 | new NvidiaDevicePlugin(this, 'NvidiaDevicePlugin', { 211 | eksCluster: eksCluster, 212 | nodeSelector: { 'nvidia.com/gpu': 'true' }, 213 | tolerations: [{ operator: 'Exists', effect: 'NoSchedule' }], 214 | }); 215 | 216 | // Auto Scale 217 | const autoscalerStmt = new iam.PolicyStatement(); 218 | autoscalerStmt.addResources('*'); 219 | autoscalerStmt.addActions( 220 | 'autoscaling:DescribeAutoScalingGroups', 221 | 'autoscaling:DescribeAutoScalingInstances', 222 | 'autoscaling:DescribeLaunchConfigurations', 223 | 'autoscaling:DescribeTags', 224 | 'autoscaling:SetDesiredCapacity', 225 | 'autoscaling:TerminateInstanceInAutoScalingGroup', 226 | 'ec2:DescribeLaunchTemplateVersions' 227 | ); 228 | const autoscalerPolicy = new iam.Policy(this, 'cluster-autoscaler-policy', { 229 | policyName: 'ClusterAutoscalerPolicy', 230 | statements: [autoscalerStmt], 231 | }); 232 | autoscalerPolicy.attachToRole(defaultNodeGroup.role); 233 | autoscalerPolicy.attachToRole(cpuASG.role); 234 | autoscalerPolicy.attachToRole(gpuASG.role); 235 | 236 | new ClusterAutoScaler(this, 'cluster-autoscaler', { 237 | eksCluster: eksCluster, 238 | version: 'v1.21.0', 239 | scaleDownDelay: props.scaleDownDelay, 240 | }); 241 | 242 | // AWS ECR 243 | const ecrStamt = new iam.PolicyStatement(); 244 | ecrStamt.addResources('*'); 245 | ecrStamt.addActions('ecr:*', 'sts:GetServiceBearerToken'); 246 | const ecrPolicy = new iam.Policy(this, 'ecr-full-access-policy', { 247 | policyName: 'ECRFullAccessPolicy', 248 | statements: [ecrStamt], 249 | }); 250 | ecrPolicy.attachToRole(defaultNodeGroup.role); 251 | ecrPolicy.attachToRole(cpuASG.role); 252 | ecrPolicy.attachToRole(gpuASG.role); 253 | eksCluster.addHelmChart('aws-ecr-credential', { 254 | chart: 'aws-ecr-credential', 255 | release: 'aws-ecr-credential', 256 | repository: 'https://charts.infuseai.io', 257 | createNamespace: true, 258 | namespace: 'hub', 259 | values: { 260 | aws: { 261 | account: account, 262 | region: region, 263 | }, 264 | targetNamespace: 'hub', 265 | }, 266 | wait: false, 267 | }); 268 | const ecrRepoName = `${clusterName}-repo-${randomHash}`; 269 | new ecr.Repository(this, `ecr-${clusterName}`, { 270 | repositoryName: ecrRepoName, 271 | }); 272 | 273 | // AWS EFS 274 | const efsFileSystem = new efs.FileSystem(this, 'efs-file-system', { 275 | fileSystemName: `efs-${clusterName}`, 276 | vpc: vpc, 277 | vpcSubnets: { 278 | subnetType: ec2.SubnetType.PUBLIC, 279 | availabilityZones: [props.availabilityZone], 280 | }, 281 | securityGroup: eksCluster.clusterSecurityGroup, 282 | removalPolicy: cdk.RemovalPolicy.DESTROY, 283 | }); 284 | const efsPolicy = new iam.Policy(this, 'efs-full-access-policy', { 285 | policyName: 'EFSFullAccessPolicy', 286 | statements: [ 287 | new iam.PolicyStatement({ 288 | resources: ['*'], 289 | effect: iam.Effect.ALLOW, 290 | actions: [ 291 | 'elasticfilesystem:DescribeAccessPoints', 292 | 'elasticfilesystem:DescribeFileSystems', 293 | 'elasticfilesystem:Client*', 294 | ], 295 | }), 296 | new iam.PolicyStatement({ 297 | resources: ['*'], 298 | effect: iam.Effect.ALLOW, 299 | actions: ['elasticfilesystem:CreateAccessPoint'], 300 | conditions: { 301 | StringLike: { 302 | 'aws:RequestTag/efs.csi.aws.com/cluster': 'true', 303 | }, 304 | }, 305 | }), 306 | new iam.PolicyStatement({ 307 | resources: ['*'], 308 | effect: iam.Effect.ALLOW, 309 | actions: ['elasticfilesystem:DeleteAccessPoint'], 310 | conditions: { 311 | StringLike: { 312 | 'aws:RequestTag/efs.csi.aws.com/cluster': 'true', 313 | }, 314 | }, 315 | }), 316 | ], 317 | }); 318 | efsPolicy.attachToRole(defaultNodeGroup.role); 319 | efsPolicy.attachToRole(cpuASG.role); 320 | efsPolicy.attachToRole(gpuASG.role); 321 | const csiDriver = new AwsEfsCsiDriver(this, 'aws-efs-csi-driver', { 322 | eksCluster: eksCluster, 323 | fileSystemID: efsFileSystem.fileSystemId, 324 | username: props.username, 325 | }); 326 | 327 | // AWS S3 328 | const primehubStoreBucket = `${clusterName}-store-${randomHash}`; 329 | const primehubConfigBucket = `${clusterName}-${randomHash}`; 330 | // Create S3 bucket to store primehub.yaml 331 | const s3StoreBucket = new s3.Bucket( 332 | this, 333 | `${primehubStoreBucket}-s3-bucket`, 334 | { 335 | autoDeleteObjects: true, 336 | removalPolicy: cdk.RemovalPolicy.DESTROY, 337 | bucketName: primehubStoreBucket, 338 | } 339 | ); 340 | const s3Policy = new iam.Policy(this, 's3-store-bucket-policy', { 341 | policyName: 'S3StoreBucketPolicy', 342 | statements: [ 343 | new iam.PolicyStatement({ 344 | effect: iam.Effect.ALLOW, 345 | actions: ['s3:*'], 346 | resources: [ 347 | `arn:aws:s3:::${primehubStoreBucket}/*`, 348 | `arn:aws:s3:::${primehubStoreBucket}`, 349 | ], 350 | }), 351 | ], 352 | }); 353 | s3Policy.attachToRole(defaultNodeGroup.role); 354 | s3Policy.attachToRole(cpuASG.role); 355 | s3Policy.attachToRole(gpuASG.role); 356 | 357 | const ingressNginx = new IngressNginxController( 358 | this, 359 | 'ingress-nginx-controller', 360 | { 361 | eksCluster: eksCluster, 362 | } 363 | ); 364 | 365 | const certManager = new CertManager(this, 'cert-manager', { 366 | eksCluster: eksCluster, 367 | email: props.email || props.username, 368 | }); 369 | 370 | const awsElbAddress = new eks.KubernetesObjectValue(this, 'AWS-ELB', { 371 | cluster: eksCluster, 372 | objectType: 'service', 373 | objectName: 'nginx-ingress-ingress-nginx-controller', 374 | objectNamespace: 'ingress-nginx', 375 | jsonPath: '.status.loadBalancer.ingress[0].hostname', 376 | }); 377 | new cdk.CfnOutput(this, 'AWS ELB Domain', { value: awsElbAddress.value }); 378 | 379 | let acmeEnabled; 380 | let sshCustomHostname = ''; 381 | if (props.basedDomain != '') { 382 | acmeEnabled = true; 383 | // Setup DNS record by AWS ELB 384 | const hostedZone = route53.HostedZone.fromLookup(this, 'Domain', { 385 | domainName: props.basedDomain, 386 | }); 387 | 388 | let hostedZoneID = EKSCluster.getELBHostedZoneID(region); 389 | 390 | new route53.ARecord(this, 'ARecord', { 391 | zone: hostedZone, 392 | recordName: `*.${props.name}.${props.basedDomain}.`, 393 | target: route53.RecordTarget.fromAlias({ 394 | bind() { 395 | return { 396 | dnsName: awsElbAddress.value, 397 | hostedZoneId: hostedZoneID, 398 | }; 399 | }, 400 | }), 401 | }); 402 | primehubDomain = `hub.${props.name}.${props.basedDomain}`; 403 | } else { 404 | // Create cloudfront distribution 405 | const cfd = new cf.Distribution(this, 'myDist', { 406 | comment: clusterName, 407 | defaultBehavior: { 408 | origin: new cfo.HttpOrigin(awsElbAddress.value, { 409 | protocolPolicy: cf.OriginProtocolPolicy.HTTP_ONLY, 410 | customHeaders: { 'X-Forwarded-Proto': 'https' }, 411 | }), 412 | allowedMethods: cf.AllowedMethods.ALLOW_ALL, 413 | cachePolicy: cf.CachePolicy.CACHING_DISABLED, 414 | originRequestPolicy: cf.OriginRequestPolicy.ALL_VIEWER, 415 | viewerProtocolPolicy: cf.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 416 | }, 417 | }); 418 | acmeEnabled = false; 419 | primehubDomain = cfd.domainName; 420 | sshCustomHostname = awsElbAddress.value; 421 | new cdk.CfnOutput(this, 'AWS CloudFront Domain', { 422 | value: cfd.domainName, 423 | }); 424 | } 425 | 426 | const dryRunMode = props.k8sInfraOnly == 'true'; 427 | const primehub = new PrimeHub(this, 'primehub', { 428 | eksCluster: eksCluster, 429 | clusterName: clusterName, 430 | ecrRepoName: ecrRepoName, 431 | primehubMode: props.primehubMode, 432 | primehubDomain: primehubDomain, 433 | primehubUserEmail: props.email, 434 | acmeEnabled: acmeEnabled, 435 | sshCustomHostname: sshCustomHostname, 436 | primehubVersion: props.primehubVersion, 437 | primehubPassword: props.primehubPassword, 438 | keycloakPassword: props.keycloakPassword, 439 | account: account, 440 | region: region, 441 | sharedVolumeStorageClass: 'efs-sc', 442 | primehubStoreBucket: primehubStoreBucket, 443 | primehubConfigBucket: primehubConfigBucket, 444 | dryRunMode: dryRunMode, 445 | cpuInstance: props.cpuInstance, 446 | gpuInstance: props.gpuInstance, 447 | enforceUpdatePassword: props.enforceUpdatePassword, 448 | }); 449 | 450 | const primehubReadyHelmCharts = new cdk.ConcreteDependable(); 451 | primehubReadyHelmCharts.add(ingressNginx); 452 | primehubReadyHelmCharts.add(certManager); 453 | primehubReadyHelmCharts.add(csiDriver); 454 | primehubReadyHelmCharts.add(s3StoreBucket); 455 | primehub.node.addDependency(primehubReadyHelmCharts); 456 | 457 | new cdk.CfnOutput(this, 'PrimeHub Store S3 Bucket', { 458 | value: primehubStoreBucket, 459 | }); 460 | new cdk.CfnOutput(this, 'PrimeHub Config S3 Bucket', { 461 | value: primehubConfigBucket, 462 | }); 463 | new cdk.CfnOutput(this, 'PrimeHub URL', { 464 | value: `https://${primehubDomain}`, 465 | }); 466 | new cdk.CfnOutput(this, 'PrimeHub Account', { value: 'phadmin' }); 467 | new cdk.CfnOutput(this, 'PrimeHub Password', { 468 | value: props.primehubPassword, 469 | }); 470 | new cdk.CfnOutput(this, 'Keycloak Account', { value: 'keycloak' }); 471 | new cdk.CfnOutput(this, 'Keycloak Password', { 472 | value: props.keycloakPassword, 473 | }); 474 | if (props.primehubVersion) { 475 | new cdk.CfnOutput(this, 'PrimeHub Version', { 476 | value: props.primehubVersion, 477 | }); 478 | } 479 | 480 | cdk.Tags.of(eksCluster).add('owner', props.username); 481 | cdk.Tags.of(eksCluster).add('clusterName', clusterName); 482 | cdk.Tags.of(eksCluster).add('clusterType', 'dev-eks'); 483 | } 484 | 485 | static getELBHostedZoneID(region: string): string { 486 | interface RegionMap { 487 | [name: string]: string; 488 | } 489 | 490 | const nlbServiceEndpoints: RegionMap = { 491 | 'us-east-2': 'ZLMOA37VPKANP', 492 | 'us-east-1': 'Z26RNL4JYFTOTI', 493 | 'us-west-1': 'Z24FKFUX50B4VW', 494 | 'us-west-2': 'Z18D5FSROUN65G', 495 | 'af-south-1': 'Z203XCE67M25HM', 496 | 'ap-east-1': 'Z12Y7K3UBGUAD1', 497 | 'ap-south-1': 'ZVDDRBQ08TROA', 498 | 'ap-northeast-3': 'Z1GWIQ4HH19I5X', 499 | 'ap-northeast-2': 'ZIBE1TIR4HY56', 500 | 'ap-southeast-1': 'ZKVM4W9LS7TM', 501 | 'ap-southeast-2': 'ZCT6FZBF4DROD', 502 | 'ap-northeast-1': 'Z31USIVHYNEOWT', 503 | 'ca-central-1': 'Z2EPGBW3API2WT', 504 | 'cn-north-1': 'Z3QFB96KMJ7ED6', 505 | 'cn-northwest-1': 'ZQEIKTCZ8352D', 506 | 'eu-central-1': 'Z3F0SRJ5LGBH90', 507 | 'eu-west-1': 'Z2IFOLAFXWLO4F', 508 | 'eu-west-2': 'ZD4D7Y8KGAS4G', 509 | 'eu-south-1': 'Z23146JA1KNAFP', 510 | 'eu-west-3': 'Z1CMS0P5QUZ6D5', 511 | 'eu-north-1': 'Z1UDT6IFJ4EJM', 512 | 'me-south-1': 'Z3QSRYVP46NYYV', 513 | 'sa-east-1': 'ZTK26PT1VY4CU', 514 | 'us-gov-east-1': 'Z1ZSMQQ6Q24QQ8', 515 | 'us-gov-west-1': 'ZMG1MZ2THAWF1', 516 | }; 517 | 518 | return nlbServiceEndpoints[region] || ''; 519 | } 520 | } 521 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aws-cdk/assets@1.134.0": 6 | version "1.134.0" 7 | resolved "https://registry.yarnpkg.com/@aws-cdk/assets/-/assets-1.134.0.tgz#506955c0c08486edee0315586d2c5963be9fd03b" 8 | integrity sha512-0UG+5iHQiok+TzfrZubsLrmyBuPtunAm9u6aXyG3yQ6RAJTfsvRRuM3g92BYvWWJEVAZxj3Zly/YT5jJJ+AS4g== 9 | dependencies: 10 | "@aws-cdk/core" "1.134.0" 11 | "@aws-cdk/cx-api" "1.134.0" 12 | constructs "^3.3.69" 13 | 14 | "@aws-cdk/aws-acmpca@1.134.0": 15 | version "1.134.0" 16 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-acmpca/-/aws-acmpca-1.134.0.tgz#4a698161df12e201f187fd7bfe17c6215675b556" 17 | integrity sha512-cOd7VTRAL4PKCpYEdzIbxAustSiT6ek9n7h1lMkAtGEInRh3PN1HNs8sK9bxHXK3mOo5Io6EFoCl5KjSepvQCw== 18 | dependencies: 19 | "@aws-cdk/core" "1.134.0" 20 | constructs "^3.3.69" 21 | 22 | "@aws-cdk/aws-applicationautoscaling@1.134.0": 23 | version "1.134.0" 24 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-applicationautoscaling/-/aws-applicationautoscaling-1.134.0.tgz#0fe81ed64bc3d5faa31bf4bfabdcaede272b9267" 25 | integrity sha512-1LvSK5WWTaW2id+KQgfFfHHo7uoBXzuyfGWgv2YwUpKu1JUQwdTcTi0Bljbfzk4tG0gYenNxOFnzxhhV1Fe+Ww== 26 | dependencies: 27 | "@aws-cdk/aws-autoscaling-common" "1.134.0" 28 | "@aws-cdk/aws-cloudwatch" "1.134.0" 29 | "@aws-cdk/aws-iam" "1.134.0" 30 | "@aws-cdk/core" "1.134.0" 31 | constructs "^3.3.69" 32 | 33 | "@aws-cdk/aws-autoscaling-common@1.134.0": 34 | version "1.134.0" 35 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-autoscaling-common/-/aws-autoscaling-common-1.134.0.tgz#2816485e98bef293c561901ff7f45b570b591f28" 36 | integrity sha512-Ret8dHpdPko5OZSJk4d4ejVEKRdabHHPpnxR4QFaJJC+V+88kshQ6SB764HDKm6+14/uQnruubspKmlq3XAhuQ== 37 | dependencies: 38 | "@aws-cdk/aws-iam" "1.134.0" 39 | "@aws-cdk/core" "1.134.0" 40 | constructs "^3.3.69" 41 | 42 | "@aws-cdk/aws-autoscaling@1.134.0": 43 | version "1.134.0" 44 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-autoscaling/-/aws-autoscaling-1.134.0.tgz#2570ae9f0819deacf91dd543fbcbbe6c5ce530d6" 45 | integrity sha512-2ZmvnWEdprgeFnG9o8Ck62RvUwLLNUD89h8NzaftRTqGKicdRIwli7RQSs70A6QjoxyKOQAhenBA4DAo2nK4Zw== 46 | dependencies: 47 | "@aws-cdk/aws-autoscaling-common" "1.134.0" 48 | "@aws-cdk/aws-cloudwatch" "1.134.0" 49 | "@aws-cdk/aws-ec2" "1.134.0" 50 | "@aws-cdk/aws-elasticloadbalancing" "1.134.0" 51 | "@aws-cdk/aws-elasticloadbalancingv2" "1.134.0" 52 | "@aws-cdk/aws-iam" "1.134.0" 53 | "@aws-cdk/aws-sns" "1.134.0" 54 | "@aws-cdk/core" "1.134.0" 55 | constructs "^3.3.69" 56 | 57 | "@aws-cdk/aws-certificatemanager@1.134.0": 58 | version "1.134.0" 59 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-1.134.0.tgz#c7d0e1c72220f040f119d6c1c27d17be42d5a3be" 60 | integrity sha512-Bjx8/7OhtmVQmnLSE9lq/7veakoPe3vy5lNXkABtQRowk0c2G30kWe5mjDhazSCd+RbUWfaTnnS84RcK0vwTKg== 61 | dependencies: 62 | "@aws-cdk/aws-acmpca" "1.134.0" 63 | "@aws-cdk/aws-cloudwatch" "1.134.0" 64 | "@aws-cdk/aws-iam" "1.134.0" 65 | "@aws-cdk/aws-lambda" "1.134.0" 66 | "@aws-cdk/aws-route53" "1.134.0" 67 | "@aws-cdk/core" "1.134.0" 68 | constructs "^3.3.69" 69 | 70 | "@aws-cdk/aws-cloudformation@1.134.0": 71 | version "1.134.0" 72 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-cloudformation/-/aws-cloudformation-1.134.0.tgz#6835b09e03f86f834b7709a445ed05e7aae5d3f6" 73 | integrity sha512-+9WNeJH4cKiZ5WOLJyeIM7ugADHwDIhWgptzH03EozAG4FuqujDnN5QtHpTLBd/GE3ItOpuD465zPJsRYqLlrQ== 74 | dependencies: 75 | "@aws-cdk/aws-iam" "1.134.0" 76 | "@aws-cdk/aws-lambda" "1.134.0" 77 | "@aws-cdk/aws-s3" "1.134.0" 78 | "@aws-cdk/aws-sns" "1.134.0" 79 | "@aws-cdk/core" "1.134.0" 80 | "@aws-cdk/cx-api" "1.134.0" 81 | constructs "^3.3.69" 82 | 83 | "@aws-cdk/aws-cloudfront-origins@1.134.0": 84 | version "1.134.0" 85 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-cloudfront-origins/-/aws-cloudfront-origins-1.134.0.tgz#8849b7f476b44e71b62e26374b6cff27b1857a4c" 86 | integrity sha512-xkqHRvlCZahP3bTPcHgFEhzQW+4EG5H1GK8BwQA3kW+LRT9UHniE/d0Ecg0czH4vo0xghuAS77HCwFCN0tvBew== 87 | dependencies: 88 | "@aws-cdk/aws-cloudfront" "1.134.0" 89 | "@aws-cdk/aws-elasticloadbalancingv2" "1.134.0" 90 | "@aws-cdk/aws-iam" "1.134.0" 91 | "@aws-cdk/aws-s3" "1.134.0" 92 | "@aws-cdk/core" "1.134.0" 93 | constructs "^3.3.69" 94 | 95 | "@aws-cdk/aws-cloudfront@1.134.0": 96 | version "1.134.0" 97 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-cloudfront/-/aws-cloudfront-1.134.0.tgz#e3493c87bd795520e9596c8c80589edb6a484743" 98 | integrity sha512-N20WzDePC0JJwBSBvja5CjMUFby245743KoKVZ6TneAXPM933G4jCp6jurrZ9S1SSC25KhfBK/pKrNrt+PbMcQ== 99 | dependencies: 100 | "@aws-cdk/aws-certificatemanager" "1.134.0" 101 | "@aws-cdk/aws-cloudwatch" "1.134.0" 102 | "@aws-cdk/aws-ec2" "1.134.0" 103 | "@aws-cdk/aws-iam" "1.134.0" 104 | "@aws-cdk/aws-kms" "1.134.0" 105 | "@aws-cdk/aws-lambda" "1.134.0" 106 | "@aws-cdk/aws-s3" "1.134.0" 107 | "@aws-cdk/aws-ssm" "1.134.0" 108 | "@aws-cdk/core" "1.134.0" 109 | "@aws-cdk/cx-api" "1.134.0" 110 | constructs "^3.3.69" 111 | 112 | "@aws-cdk/aws-cloudwatch@1.134.0": 113 | version "1.134.0" 114 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-cloudwatch/-/aws-cloudwatch-1.134.0.tgz#d454069d1288fbe11f5801b9607d29150106399c" 115 | integrity sha512-LdnRXLHVHBYDa2CSCZc4jzB+89hUjt5xI1SWmhY9vIqXvNLp7Xt2wq3gYzCJG1Gq+jwpTG69S0B3TzFLKL988A== 116 | dependencies: 117 | "@aws-cdk/aws-iam" "1.134.0" 118 | "@aws-cdk/core" "1.134.0" 119 | constructs "^3.3.69" 120 | 121 | "@aws-cdk/aws-codeguruprofiler@1.134.0": 122 | version "1.134.0" 123 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-codeguruprofiler/-/aws-codeguruprofiler-1.134.0.tgz#3fb718e8b447f7bfff33ed06fece16db400af540" 124 | integrity sha512-Uv17un0PkPqOLJq17IJelDJ40+SaBIk+GEBqmASIix7R/SB0FB62g9r1vh3SwDnBWAmebNIV6HFZyVIjh4LJWA== 125 | dependencies: 126 | "@aws-cdk/aws-iam" "1.134.0" 127 | "@aws-cdk/core" "1.134.0" 128 | constructs "^3.3.69" 129 | 130 | "@aws-cdk/aws-codestarnotifications@1.134.0": 131 | version "1.134.0" 132 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-codestarnotifications/-/aws-codestarnotifications-1.134.0.tgz#ef8be9132ff9dba1fdb523c5665412ee4038590f" 133 | integrity sha512-cUgiT1tscqgXmAOA7WK4eCvs4kTa9wf1s1jfhPnipcjXq9/KaU+kuvmLeOc0UMRyfeXcdRYXaPTymKAtdHhllQ== 134 | dependencies: 135 | "@aws-cdk/core" "1.134.0" 136 | constructs "^3.3.69" 137 | 138 | "@aws-cdk/aws-ec2@1.134.0": 139 | version "1.134.0" 140 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-ec2/-/aws-ec2-1.134.0.tgz#25d38243aa04fd2498c0a7682ce7cd375760d41d" 141 | integrity sha512-4HPDl0tzkkKhEn4ja8E9r8WgRIyzd2suZCdRYVONC9A5KO8Rhgld8NIf/+WpDTZvlh01o/vYH/zONodJS2rkjw== 142 | dependencies: 143 | "@aws-cdk/aws-cloudwatch" "1.134.0" 144 | "@aws-cdk/aws-iam" "1.134.0" 145 | "@aws-cdk/aws-kms" "1.134.0" 146 | "@aws-cdk/aws-logs" "1.134.0" 147 | "@aws-cdk/aws-s3" "1.134.0" 148 | "@aws-cdk/aws-s3-assets" "1.134.0" 149 | "@aws-cdk/aws-ssm" "1.134.0" 150 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 151 | "@aws-cdk/core" "1.134.0" 152 | "@aws-cdk/cx-api" "1.134.0" 153 | "@aws-cdk/region-info" "1.134.0" 154 | constructs "^3.3.69" 155 | 156 | "@aws-cdk/aws-ecr-assets@1.134.0": 157 | version "1.134.0" 158 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-ecr-assets/-/aws-ecr-assets-1.134.0.tgz#8b1d5d423f5fda49883f691701a6c4c177ffe92a" 159 | integrity sha512-BqxwQB6QKU2hH3IvwazBYvfhBk6eUh3ZRfOCD9nagQqI/oIcvxr3bHmfaZQoIERsiZ3qLP5XMz8lBaWJWBMYTA== 160 | dependencies: 161 | "@aws-cdk/assets" "1.134.0" 162 | "@aws-cdk/aws-ecr" "1.134.0" 163 | "@aws-cdk/aws-iam" "1.134.0" 164 | "@aws-cdk/aws-s3" "1.134.0" 165 | "@aws-cdk/core" "1.134.0" 166 | "@aws-cdk/cx-api" "1.134.0" 167 | constructs "^3.3.69" 168 | minimatch "^3.0.4" 169 | 170 | "@aws-cdk/aws-ecr@1.134.0": 171 | version "1.134.0" 172 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-ecr/-/aws-ecr-1.134.0.tgz#b181626b8030ee2421af98d758dca23e206f05ef" 173 | integrity sha512-NOMyucbtEWyljlXWQgL49AJy13YLsW4k45rjrZKB34Oiz+bigpeAirTxyOUvz+1cXjIndmzCD3AuQflTvCUxkw== 174 | dependencies: 175 | "@aws-cdk/aws-events" "1.134.0" 176 | "@aws-cdk/aws-iam" "1.134.0" 177 | "@aws-cdk/core" "1.134.0" 178 | constructs "^3.3.69" 179 | 180 | "@aws-cdk/aws-efs@1.134.0": 181 | version "1.134.0" 182 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-efs/-/aws-efs-1.134.0.tgz#8c9dc00b378a9922bf23545ffb207966e65500da" 183 | integrity sha512-gh3dGFjeyG5jVOMQURCjTlqDP22U1Y2eo1P9cROW4b009Drk2NmofVplkrLX6INFgVcXNyikbcJ4l1PfN81T2A== 184 | dependencies: 185 | "@aws-cdk/aws-ec2" "1.134.0" 186 | "@aws-cdk/aws-iam" "1.134.0" 187 | "@aws-cdk/aws-kms" "1.134.0" 188 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 189 | "@aws-cdk/core" "1.134.0" 190 | "@aws-cdk/cx-api" "1.134.0" 191 | constructs "^3.3.69" 192 | 193 | "@aws-cdk/aws-eks@1.134.0": 194 | version "1.134.0" 195 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-eks/-/aws-eks-1.134.0.tgz#b8ddf920975cf311ce08c3095d12364128280079" 196 | integrity sha512-kfyAKRM9mmFO8tEcsTqkdsMduAuAGhPSR79cQeWM+BOT/+BpUWteoJr0Q/zol/yTowCZxBemjQE9J9LD9YrmsQ== 197 | dependencies: 198 | "@aws-cdk/aws-autoscaling" "1.134.0" 199 | "@aws-cdk/aws-ec2" "1.134.0" 200 | "@aws-cdk/aws-iam" "1.134.0" 201 | "@aws-cdk/aws-kms" "1.134.0" 202 | "@aws-cdk/aws-lambda" "1.134.0" 203 | "@aws-cdk/aws-ssm" "1.134.0" 204 | "@aws-cdk/core" "1.134.0" 205 | "@aws-cdk/custom-resources" "1.134.0" 206 | "@aws-cdk/lambda-layer-awscli" "1.134.0" 207 | "@aws-cdk/lambda-layer-kubectl" "1.134.0" 208 | "@aws-cdk/lambda-layer-node-proxy-agent" "1.134.0" 209 | constructs "^3.3.69" 210 | yaml "1.10.2" 211 | 212 | "@aws-cdk/aws-elasticloadbalancing@1.134.0": 213 | version "1.134.0" 214 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-elasticloadbalancing/-/aws-elasticloadbalancing-1.134.0.tgz#0a2806389b166085a38eebfc6816485c4dc90eaf" 215 | integrity sha512-eVQmn39enzFT5n6e/DOcZnRXLbBxPWw0q0DhXJrOY1DAIXUO6Ba7X64al7YdQ+aDs/RZ9Rao9L4Ok9lWxJgWdw== 216 | dependencies: 217 | "@aws-cdk/aws-ec2" "1.134.0" 218 | "@aws-cdk/core" "1.134.0" 219 | constructs "^3.3.69" 220 | 221 | "@aws-cdk/aws-elasticloadbalancingv2@1.134.0": 222 | version "1.134.0" 223 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-elasticloadbalancingv2/-/aws-elasticloadbalancingv2-1.134.0.tgz#9d986e7170d7884ed872a041ced007146a8d357a" 224 | integrity sha512-+IrGm5SGjibKPaLb/7+s28caOrRRkEQMlyk5bqWd1quVwZpepN1929UpgFD7NQwlDpUwFM0QvuB0uOLeNwNzBw== 225 | dependencies: 226 | "@aws-cdk/aws-certificatemanager" "1.134.0" 227 | "@aws-cdk/aws-cloudwatch" "1.134.0" 228 | "@aws-cdk/aws-ec2" "1.134.0" 229 | "@aws-cdk/aws-iam" "1.134.0" 230 | "@aws-cdk/aws-lambda" "1.134.0" 231 | "@aws-cdk/aws-s3" "1.134.0" 232 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 233 | "@aws-cdk/core" "1.134.0" 234 | "@aws-cdk/cx-api" "1.134.0" 235 | "@aws-cdk/region-info" "1.134.0" 236 | constructs "^3.3.69" 237 | 238 | "@aws-cdk/aws-events@1.134.0": 239 | version "1.134.0" 240 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-events/-/aws-events-1.134.0.tgz#10c4034c1d6ba9cf9ea3923aba837458f4399952" 241 | integrity sha512-M0jmIwgIssB4Gi1m4h3Jeuu+KETmjijHHFGI0Xffxmu9myMvfKjfgyFLytQuz4/7UfaPH6vqkHCCp9/bJNLo2Q== 242 | dependencies: 243 | "@aws-cdk/aws-iam" "1.134.0" 244 | "@aws-cdk/core" "1.134.0" 245 | constructs "^3.3.69" 246 | 247 | "@aws-cdk/aws-iam@1.134.0": 248 | version "1.134.0" 249 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-iam/-/aws-iam-1.134.0.tgz#53a45dd0fd3c1327952dd10209c95a5e8366488d" 250 | integrity sha512-EZy+oyIiAl2WvAfKpn2zhSZ9Wtcu06RUTIbkrourietnQoQhqhiwpp/H84VosgUgwpHsx7BNmZ8EJJXJXs7XfA== 251 | dependencies: 252 | "@aws-cdk/core" "1.134.0" 253 | "@aws-cdk/region-info" "1.134.0" 254 | constructs "^3.3.69" 255 | 256 | "@aws-cdk/aws-kms@1.134.0": 257 | version "1.134.0" 258 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-kms/-/aws-kms-1.134.0.tgz#fd929d02ad0da1cd7626f2c886bae34acf531e0e" 259 | integrity sha512-nuXWE/lHnR9PzZdjCN9cbhT5IAHS593JVrS889v+O/9l3jLCgdSaElQ6EAjmr7WHne2npRbTo/Qw7ebT31BIRQ== 260 | dependencies: 261 | "@aws-cdk/aws-iam" "1.134.0" 262 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 263 | "@aws-cdk/core" "1.134.0" 264 | "@aws-cdk/cx-api" "1.134.0" 265 | constructs "^3.3.69" 266 | 267 | "@aws-cdk/aws-lambda@1.134.0": 268 | version "1.134.0" 269 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-lambda/-/aws-lambda-1.134.0.tgz#891714695fc1d7c2666adec16414ddab72f5b5c4" 270 | integrity sha512-RGxPEPw2twJzcllNSNkHSLjgntuT8JQ5Sphiuyk3EZayPDPYUCbkEVWhmkHoLP5KnZprn2GIPaknSGnOVB7TBQ== 271 | dependencies: 272 | "@aws-cdk/aws-applicationautoscaling" "1.134.0" 273 | "@aws-cdk/aws-cloudwatch" "1.134.0" 274 | "@aws-cdk/aws-codeguruprofiler" "1.134.0" 275 | "@aws-cdk/aws-ec2" "1.134.0" 276 | "@aws-cdk/aws-ecr" "1.134.0" 277 | "@aws-cdk/aws-ecr-assets" "1.134.0" 278 | "@aws-cdk/aws-efs" "1.134.0" 279 | "@aws-cdk/aws-events" "1.134.0" 280 | "@aws-cdk/aws-iam" "1.134.0" 281 | "@aws-cdk/aws-kms" "1.134.0" 282 | "@aws-cdk/aws-logs" "1.134.0" 283 | "@aws-cdk/aws-s3" "1.134.0" 284 | "@aws-cdk/aws-s3-assets" "1.134.0" 285 | "@aws-cdk/aws-signer" "1.134.0" 286 | "@aws-cdk/aws-sqs" "1.134.0" 287 | "@aws-cdk/core" "1.134.0" 288 | "@aws-cdk/cx-api" "1.134.0" 289 | "@aws-cdk/region-info" "1.134.0" 290 | constructs "^3.3.69" 291 | 292 | "@aws-cdk/aws-logs@1.134.0": 293 | version "1.134.0" 294 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-logs/-/aws-logs-1.134.0.tgz#629df12cf49e29ec98ed6afc0026c7f21ede77e0" 295 | integrity sha512-XolgIfT8vamdlVy7TAtHvhX7BFeJrg37pEur0CAiuzROipnBGwHc63K4QqJYlA0QDEsLXn8BupUV76jBHr3wvQ== 296 | dependencies: 297 | "@aws-cdk/aws-cloudwatch" "1.134.0" 298 | "@aws-cdk/aws-iam" "1.134.0" 299 | "@aws-cdk/aws-kms" "1.134.0" 300 | "@aws-cdk/aws-s3-assets" "1.134.0" 301 | "@aws-cdk/core" "1.134.0" 302 | "@aws-cdk/cx-api" "1.134.0" 303 | constructs "^3.3.69" 304 | 305 | "@aws-cdk/aws-route53@1.134.0": 306 | version "1.134.0" 307 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-route53/-/aws-route53-1.134.0.tgz#f679182fa21accd79fef749b453b495e5ad8bb19" 308 | integrity sha512-VodtS60oCfrxtBjriwytmYVkRSAc4m6oxCz6w2nvF9IwAxPWHga66KDdEnMpRKExZtpBx0Pw1EHgxG03bhVx+g== 309 | dependencies: 310 | "@aws-cdk/aws-ec2" "1.134.0" 311 | "@aws-cdk/aws-iam" "1.134.0" 312 | "@aws-cdk/aws-logs" "1.134.0" 313 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 314 | "@aws-cdk/core" "1.134.0" 315 | "@aws-cdk/custom-resources" "1.134.0" 316 | constructs "^3.3.69" 317 | 318 | "@aws-cdk/aws-s3-assets@1.134.0": 319 | version "1.134.0" 320 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-s3-assets/-/aws-s3-assets-1.134.0.tgz#7177b1ba9d7f51399ad82594bf55061b1d3fff77" 321 | integrity sha512-1geRa/eAkGpWekTpf6QJiLH3UtN4PNrpK+2WtK3hW1fsuj0F8GC9gB+AQSruMAI6SC195tsEMTLO5zqdypDknQ== 322 | dependencies: 323 | "@aws-cdk/assets" "1.134.0" 324 | "@aws-cdk/aws-iam" "1.134.0" 325 | "@aws-cdk/aws-kms" "1.134.0" 326 | "@aws-cdk/aws-s3" "1.134.0" 327 | "@aws-cdk/core" "1.134.0" 328 | "@aws-cdk/cx-api" "1.134.0" 329 | constructs "^3.3.69" 330 | 331 | "@aws-cdk/aws-s3-deployment@1.134.0": 332 | version "1.134.0" 333 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-s3-deployment/-/aws-s3-deployment-1.134.0.tgz#3b67f3c80c7f89d0889f0d2b52917e58c64fac0a" 334 | integrity sha512-MYheBRzrrcfHr8NvotTg62/cOQqnB/atdAyr85KpRQzVrhaZRJOOw8hAqYg+TIsbj9IJkuPiXPVi0Lwf2R93ig== 335 | dependencies: 336 | "@aws-cdk/aws-cloudfront" "1.134.0" 337 | "@aws-cdk/aws-ec2" "1.134.0" 338 | "@aws-cdk/aws-efs" "1.134.0" 339 | "@aws-cdk/aws-iam" "1.134.0" 340 | "@aws-cdk/aws-lambda" "1.134.0" 341 | "@aws-cdk/aws-s3" "1.134.0" 342 | "@aws-cdk/aws-s3-assets" "1.134.0" 343 | "@aws-cdk/core" "1.134.0" 344 | "@aws-cdk/lambda-layer-awscli" "1.134.0" 345 | case "1.6.3" 346 | constructs "^3.3.69" 347 | 348 | "@aws-cdk/aws-s3@1.134.0": 349 | version "1.134.0" 350 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-s3/-/aws-s3-1.134.0.tgz#70374da0fa5fbd799f9270c857c94c8a6d042c4d" 351 | integrity sha512-HhVITjecHzJSf1eH5JqphvQBAaSXSDdbNQMNSX/qmMnhorsvOuU/vSmn4mmPTOUnbR6NP4k51WotVZ9qmMCaow== 352 | dependencies: 353 | "@aws-cdk/aws-events" "1.134.0" 354 | "@aws-cdk/aws-iam" "1.134.0" 355 | "@aws-cdk/aws-kms" "1.134.0" 356 | "@aws-cdk/core" "1.134.0" 357 | "@aws-cdk/cx-api" "1.134.0" 358 | constructs "^3.3.69" 359 | 360 | "@aws-cdk/aws-signer@1.134.0": 361 | version "1.134.0" 362 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-signer/-/aws-signer-1.134.0.tgz#74b88b5a3b3abedb277aeb27c24b86542ba1ce1f" 363 | integrity sha512-L9A6FXJrcxIfGLMKdOweikGJjePFy0FegYEqK3w26+3I8ZmQreeDf9GOCEpFY6f1D8t4m2jxJK5/hjMuwFpV6w== 364 | dependencies: 365 | "@aws-cdk/core" "1.134.0" 366 | constructs "^3.3.69" 367 | 368 | "@aws-cdk/aws-sns@1.134.0": 369 | version "1.134.0" 370 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-sns/-/aws-sns-1.134.0.tgz#8592725ec12000aad70ede1c084996d5776f5a51" 371 | integrity sha512-Lf+JOASA5r3YganhTtC7FKgSgp4DacH0zGCzLsv1GF7nLfWEbdVDBpiwTj2l42I9IQEEcYsIMhmbx1RB0AO2vQ== 372 | dependencies: 373 | "@aws-cdk/aws-cloudwatch" "1.134.0" 374 | "@aws-cdk/aws-codestarnotifications" "1.134.0" 375 | "@aws-cdk/aws-events" "1.134.0" 376 | "@aws-cdk/aws-iam" "1.134.0" 377 | "@aws-cdk/aws-kms" "1.134.0" 378 | "@aws-cdk/aws-sqs" "1.134.0" 379 | "@aws-cdk/core" "1.134.0" 380 | constructs "^3.3.69" 381 | 382 | "@aws-cdk/aws-sqs@1.134.0": 383 | version "1.134.0" 384 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-sqs/-/aws-sqs-1.134.0.tgz#2f69ca40670f44a37562e06c5918901428eb84e3" 385 | integrity sha512-idzqP90kvIsm15STScg90BvBuxNiXvg0AEfJ/tz9Z7b339VPTOzkJwMmY0DCSjvR8d3gnZZd6Xd6X4u6XUqR3Q== 386 | dependencies: 387 | "@aws-cdk/aws-cloudwatch" "1.134.0" 388 | "@aws-cdk/aws-iam" "1.134.0" 389 | "@aws-cdk/aws-kms" "1.134.0" 390 | "@aws-cdk/core" "1.134.0" 391 | constructs "^3.3.69" 392 | 393 | "@aws-cdk/aws-ssm@1.134.0": 394 | version "1.134.0" 395 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-ssm/-/aws-ssm-1.134.0.tgz#596c8bc679c3c872ebf4011317de01903d9b2041" 396 | integrity sha512-cKubHdfexeiuXLPTk77FMTFgehpuYyD72e6rd42Oc79KNGAoytWWGzDc+tieIpz8caBOt5U4QJ3KNF/ARF1wGA== 397 | dependencies: 398 | "@aws-cdk/aws-iam" "1.134.0" 399 | "@aws-cdk/aws-kms" "1.134.0" 400 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 401 | "@aws-cdk/core" "1.134.0" 402 | constructs "^3.3.69" 403 | 404 | "@aws-cdk/cfnspec@1.134.0": 405 | version "1.134.0" 406 | resolved "https://registry.yarnpkg.com/@aws-cdk/cfnspec/-/cfnspec-1.134.0.tgz#9d04f9bfcad3035dac507a6787d95b5c4f3fac59" 407 | integrity sha512-0R25VRg8R2aj9K8y2UzU60JmgNomYBuIBo41MeRnHMEmZcjXCAuPAvYm6F1+Htd6QG4Y9XUYHX96zfK40IV0rw== 408 | dependencies: 409 | fs-extra "^9.1.0" 410 | md5 "^2.3.0" 411 | 412 | "@aws-cdk/cloud-assembly-schema@1.134.0": 413 | version "1.134.0" 414 | resolved "https://registry.yarnpkg.com/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.134.0.tgz#a2d563d17b09f5bb12697edba43d9ec742d6a502" 415 | integrity sha512-P7HTOJ/e7MM+RfMQKpzP4cie0hbf/st/l6AmbYx0t/aayhnL1MXrnWPZd1t89TEYiekJkwsXX6x6N7S180uQvw== 416 | dependencies: 417 | jsonschema "^1.4.0" 418 | semver "^7.3.5" 419 | 420 | "@aws-cdk/cloudformation-diff@1.134.0": 421 | version "1.134.0" 422 | resolved "https://registry.yarnpkg.com/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.134.0.tgz#9bd61e5c87ba2b9c94a214c543c61c2cbf06c031" 423 | integrity sha512-0Dle04uZGG2og1Lcv9GCHi9NozX7ukiFt0C1AbsgRuEvbK+zIo4beKrX9Yqtt3pfUCU20zlTqibw4mS7+y72CQ== 424 | dependencies: 425 | "@aws-cdk/cfnspec" "1.134.0" 426 | "@types/node" "^10.17.60" 427 | colors "^1.4.0" 428 | diff "^5.0.0" 429 | fast-deep-equal "^3.1.3" 430 | string-width "^4.2.3" 431 | table "^6.7.3" 432 | 433 | "@aws-cdk/core@1.134.0": 434 | version "1.134.0" 435 | resolved "https://registry.yarnpkg.com/@aws-cdk/core/-/core-1.134.0.tgz#cdcb94ffcfbfa04cdb9c585f008c995e1f8bad5c" 436 | integrity sha512-rdQYFTrGBemgy1C23r2s18ONXgnsp4dXyZMTTcy7e6QkF3JBTI4za2GUWY4XQaKnchGV9UhylPNFqh59hXzEUA== 437 | dependencies: 438 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 439 | "@aws-cdk/cx-api" "1.134.0" 440 | "@aws-cdk/region-info" "1.134.0" 441 | "@balena/dockerignore" "^1.0.2" 442 | constructs "^3.3.69" 443 | fs-extra "^9.1.0" 444 | ignore "^5.1.9" 445 | minimatch "^3.0.4" 446 | 447 | "@aws-cdk/custom-resources@1.134.0": 448 | version "1.134.0" 449 | resolved "https://registry.yarnpkg.com/@aws-cdk/custom-resources/-/custom-resources-1.134.0.tgz#9ab47a5a4c98a573ddb2747f73b87e31e2424e50" 450 | integrity sha512-BfVq0IsXpPV6Eyzt0kLzNzLHKZvslNzQexSXY3yN5E2NHqXucqCem4X+qN+xp8p5eO0R83pmsPKUW9OT9SyEqg== 451 | dependencies: 452 | "@aws-cdk/aws-cloudformation" "1.134.0" 453 | "@aws-cdk/aws-ec2" "1.134.0" 454 | "@aws-cdk/aws-iam" "1.134.0" 455 | "@aws-cdk/aws-lambda" "1.134.0" 456 | "@aws-cdk/aws-logs" "1.134.0" 457 | "@aws-cdk/aws-sns" "1.134.0" 458 | "@aws-cdk/core" "1.134.0" 459 | constructs "^3.3.69" 460 | 461 | "@aws-cdk/cx-api@1.134.0": 462 | version "1.134.0" 463 | resolved "https://registry.yarnpkg.com/@aws-cdk/cx-api/-/cx-api-1.134.0.tgz#29e20be4fbcd909d10809e11011f0ca1002ec189" 464 | integrity sha512-D822T7LI+61Sktv4bdR6g2rKXdGNgYZXkkHrSSpATp6ov++0E0ttsQVblkLrzcOtKtB5nbaGrF7cf/0fxdoKeg== 465 | dependencies: 466 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 467 | semver "^7.3.5" 468 | 469 | "@aws-cdk/lambda-layer-awscli@1.134.0": 470 | version "1.134.0" 471 | resolved "https://registry.yarnpkg.com/@aws-cdk/lambda-layer-awscli/-/lambda-layer-awscli-1.134.0.tgz#0fe8173791323a066f5257fdb2799a01072a823a" 472 | integrity sha512-LXzfg3aadSHkSeefMnGveIDXj+K7/79Ug19Sm4Vp0CZXAykkUco1RWk9TZ/dKoEPxGC8MhYoQYxxZegjUlMmzw== 473 | dependencies: 474 | "@aws-cdk/aws-lambda" "1.134.0" 475 | "@aws-cdk/core" "1.134.0" 476 | constructs "^3.3.69" 477 | 478 | "@aws-cdk/lambda-layer-kubectl@1.134.0": 479 | version "1.134.0" 480 | resolved "https://registry.yarnpkg.com/@aws-cdk/lambda-layer-kubectl/-/lambda-layer-kubectl-1.134.0.tgz#901f5a82ef47d04e2d8df6299dfc55fa908c8138" 481 | integrity sha512-FrzgFgLzZkDEzCJfRs0ig7JzkrXfbx+aSPh/aVROr98wlSPoMWFiavGBRS0BIB5JK7eXxpg7I0VPvMFzEx96OA== 482 | dependencies: 483 | "@aws-cdk/aws-lambda" "1.134.0" 484 | "@aws-cdk/core" "1.134.0" 485 | constructs "^3.3.69" 486 | 487 | "@aws-cdk/lambda-layer-node-proxy-agent@1.134.0": 488 | version "1.134.0" 489 | resolved "https://registry.yarnpkg.com/@aws-cdk/lambda-layer-node-proxy-agent/-/lambda-layer-node-proxy-agent-1.134.0.tgz#52ce5d126873209f50fd82dd1fafc134649936cb" 490 | integrity sha512-5l3BmgI5H3nq8+FWK/2W9T8bqbmsyJ4bhCswao6xZ3I2LT2iKPs2Gw0oXPhDc6xakdy2DglSlOUx9kM9ZvWW4w== 491 | dependencies: 492 | "@aws-cdk/aws-lambda" "1.134.0" 493 | "@aws-cdk/core" "1.134.0" 494 | constructs "^3.3.69" 495 | 496 | "@aws-cdk/region-info@1.134.0": 497 | version "1.134.0" 498 | resolved "https://registry.yarnpkg.com/@aws-cdk/region-info/-/region-info-1.134.0.tgz#4aa74e73cf12f26a2e7fcd3e091345ae7116508e" 499 | integrity sha512-1BzvZsdXWvn1ub3yGcpALafBHqISbirgNz9fsFuqvZuBUYJBiIi97awSyFMKZKzLvGNkrAk9rsNjHo9gWnJOWw== 500 | 501 | "@balena/dockerignore@^1.0.2": 502 | version "1.0.2" 503 | resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" 504 | integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== 505 | 506 | "@jsii/check-node@1.44.2": 507 | version "1.44.2" 508 | resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.44.2.tgz#d7786e7ca739cc9a5cd2cd3f1b93c4375ff884e8" 509 | integrity sha512-rVwrKXkuV4qmo0TmPbYMAu2SCC80xPDzY7cS+TDx80wfU5Dcr66lhpUW04hWcYwrVsUYXxtEYLxAbzeNYeJeoA== 510 | dependencies: 511 | chalk "^4.1.2" 512 | semver "^7.3.5" 513 | 514 | "@tootallnate/once@1": 515 | version "1.1.2" 516 | resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" 517 | integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== 518 | 519 | "@types/node@^10.17.0", "@types/node@^10.17.60": 520 | version "10.17.60" 521 | resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" 522 | integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== 523 | 524 | agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2: 525 | version "6.0.2" 526 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" 527 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== 528 | dependencies: 529 | debug "4" 530 | 531 | ajv@^8.0.1: 532 | version "8.8.2" 533 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" 534 | integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== 535 | dependencies: 536 | fast-deep-equal "^3.1.1" 537 | json-schema-traverse "^1.0.0" 538 | require-from-string "^2.0.2" 539 | uri-js "^4.2.2" 540 | 541 | ansi-regex@^5.0.1: 542 | version "5.0.1" 543 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 544 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 545 | 546 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 547 | version "4.3.0" 548 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 549 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 550 | dependencies: 551 | color-convert "^2.0.1" 552 | 553 | anymatch@~3.1.2: 554 | version "3.1.2" 555 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 556 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 557 | dependencies: 558 | normalize-path "^3.0.0" 559 | picomatch "^2.0.4" 560 | 561 | archiver-utils@^2.1.0: 562 | version "2.1.0" 563 | resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" 564 | integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== 565 | dependencies: 566 | glob "^7.1.4" 567 | graceful-fs "^4.2.0" 568 | lazystream "^1.0.0" 569 | lodash.defaults "^4.2.0" 570 | lodash.difference "^4.5.0" 571 | lodash.flatten "^4.4.0" 572 | lodash.isplainobject "^4.0.6" 573 | lodash.union "^4.6.0" 574 | normalize-path "^3.0.0" 575 | readable-stream "^2.0.0" 576 | 577 | archiver@^5.3.0: 578 | version "5.3.0" 579 | resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba" 580 | integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg== 581 | dependencies: 582 | archiver-utils "^2.1.0" 583 | async "^3.2.0" 584 | buffer-crc32 "^0.2.1" 585 | readable-stream "^3.6.0" 586 | readdir-glob "^1.0.0" 587 | tar-stream "^2.2.0" 588 | zip-stream "^4.1.0" 589 | 590 | ast-types@^0.13.2: 591 | version "0.13.4" 592 | resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" 593 | integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== 594 | dependencies: 595 | tslib "^2.0.1" 596 | 597 | astral-regex@^2.0.0: 598 | version "2.0.0" 599 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" 600 | integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== 601 | 602 | async@^3.2.0: 603 | version "3.2.2" 604 | resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd" 605 | integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g== 606 | 607 | at-least-node@^1.0.0: 608 | version "1.0.0" 609 | resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" 610 | integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== 611 | 612 | aws-cdk@1.134.0: 613 | version "1.134.0" 614 | resolved "https://registry.yarnpkg.com/aws-cdk/-/aws-cdk-1.134.0.tgz#c25e8f9fde10a5732e036a7c121444171ee7f872" 615 | integrity sha512-dtCUT9/NvRCe1iNRFcI8rR3RBpOOG9sD99nZeP9KxYfZJjwrl0wi44LYTfZwMubeRkdCG7W12WYSDYWG6Z3HWw== 616 | dependencies: 617 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 618 | "@aws-cdk/cloudformation-diff" "1.134.0" 619 | "@aws-cdk/cx-api" "1.134.0" 620 | "@aws-cdk/region-info" "1.134.0" 621 | "@jsii/check-node" "1.44.2" 622 | archiver "^5.3.0" 623 | aws-sdk "^2.979.0" 624 | camelcase "^6.2.1" 625 | cdk-assets "1.134.0" 626 | chokidar "^3.5.2" 627 | colors "^1.4.0" 628 | decamelize "^5.0.1" 629 | fs-extra "^9.1.0" 630 | glob "^7.2.0" 631 | json-diff "^0.5.4" 632 | minimatch ">=3.0" 633 | promptly "^3.2.0" 634 | proxy-agent "^5.0.0" 635 | semver "^7.3.5" 636 | source-map-support "^0.5.20" 637 | table "^6.7.3" 638 | uuid "^8.3.2" 639 | wrap-ansi "^7.0.0" 640 | yaml "1.10.2" 641 | yargs "^16.2.0" 642 | 643 | aws-sdk@^2.848.0, aws-sdk@^2.979.0: 644 | version "2.1044.0" 645 | resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1044.0.tgz#0708eaf48daf8d961b414e698d84e8cd1f82c4ad" 646 | integrity sha512-n55uGUONQGXteGGG1QlZ1rKx447KSuV/x6jUGNf2nOl41qMI8ZgLUhNUt0uOtw3qJrCTanzCyR/JKBq2PMiqEQ== 647 | dependencies: 648 | buffer "4.9.2" 649 | events "1.1.1" 650 | ieee754 "1.1.13" 651 | jmespath "0.15.0" 652 | querystring "0.2.0" 653 | sax "1.2.1" 654 | url "0.10.3" 655 | uuid "3.3.2" 656 | xml2js "0.4.19" 657 | 658 | balanced-match@^1.0.0: 659 | version "1.0.2" 660 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 661 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 662 | 663 | base64-js@^1.0.2, base64-js@^1.3.1: 664 | version "1.5.1" 665 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 666 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 667 | 668 | binary-extensions@^2.0.0: 669 | version "2.2.0" 670 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 671 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 672 | 673 | bl@^4.0.3: 674 | version "4.1.0" 675 | resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" 676 | integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== 677 | dependencies: 678 | buffer "^5.5.0" 679 | inherits "^2.0.4" 680 | readable-stream "^3.4.0" 681 | 682 | brace-expansion@^1.1.7: 683 | version "1.1.11" 684 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 685 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 686 | dependencies: 687 | balanced-match "^1.0.0" 688 | concat-map "0.0.1" 689 | 690 | braces@~3.0.2: 691 | version "3.0.2" 692 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 693 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 694 | dependencies: 695 | fill-range "^7.0.1" 696 | 697 | buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: 698 | version "0.2.13" 699 | resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" 700 | integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= 701 | 702 | buffer-from@^1.0.0: 703 | version "1.1.2" 704 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 705 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 706 | 707 | buffer@4.9.2: 708 | version "4.9.2" 709 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" 710 | integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== 711 | dependencies: 712 | base64-js "^1.0.2" 713 | ieee754 "^1.1.4" 714 | isarray "^1.0.0" 715 | 716 | buffer@^5.5.0: 717 | version "5.7.1" 718 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 719 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 720 | dependencies: 721 | base64-js "^1.3.1" 722 | ieee754 "^1.1.13" 723 | 724 | bytes@3.1.1: 725 | version "3.1.1" 726 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" 727 | integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== 728 | 729 | camelcase@^6.2.1: 730 | version "6.2.1" 731 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" 732 | integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== 733 | 734 | case@1.6.3: 735 | version "1.6.3" 736 | resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" 737 | integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== 738 | 739 | cdk-assets@1.134.0: 740 | version "1.134.0" 741 | resolved "https://registry.yarnpkg.com/cdk-assets/-/cdk-assets-1.134.0.tgz#95fff88e6695c8de282d0911e77e9834bf2c2a4a" 742 | integrity sha512-bRGai5VZEZr0wSp77QC++BUmNlC05uafs9RT01mz2qtXlDuH3XZfAc5+6yaP/kJ2//NnugihKTQSweuPU84rRg== 743 | dependencies: 744 | "@aws-cdk/cloud-assembly-schema" "1.134.0" 745 | "@aws-cdk/cx-api" "1.134.0" 746 | archiver "^5.3.0" 747 | aws-sdk "^2.848.0" 748 | glob "^7.2.0" 749 | mime "^2.6.0" 750 | yargs "^16.2.0" 751 | 752 | chalk@^4.1.2: 753 | version "4.1.2" 754 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 755 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 756 | dependencies: 757 | ansi-styles "^4.1.0" 758 | supports-color "^7.1.0" 759 | 760 | charenc@0.0.2: 761 | version "0.0.2" 762 | resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" 763 | integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= 764 | 765 | chokidar@^3.5.2: 766 | version "3.5.2" 767 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" 768 | integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== 769 | dependencies: 770 | anymatch "~3.1.2" 771 | braces "~3.0.2" 772 | glob-parent "~5.1.2" 773 | is-binary-path "~2.1.0" 774 | is-glob "~4.0.1" 775 | normalize-path "~3.0.0" 776 | readdirp "~3.6.0" 777 | optionalDependencies: 778 | fsevents "~2.3.2" 779 | 780 | cli-color@~0.1.6: 781 | version "0.1.7" 782 | resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-0.1.7.tgz#adc3200fa471cc211b0da7f566b71e98b9d67347" 783 | integrity sha1-rcMgD6RxzCEbDaf1ZrcemLnWc0c= 784 | dependencies: 785 | es5-ext "0.8.x" 786 | 787 | cliui@^7.0.2: 788 | version "7.0.4" 789 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" 790 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 791 | dependencies: 792 | string-width "^4.2.0" 793 | strip-ansi "^6.0.0" 794 | wrap-ansi "^7.0.0" 795 | 796 | color-convert@^2.0.1: 797 | version "2.0.1" 798 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 799 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 800 | dependencies: 801 | color-name "~1.1.4" 802 | 803 | color-name@~1.1.4: 804 | version "1.1.4" 805 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 806 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 807 | 808 | colors@^1.4.0: 809 | version "1.4.0" 810 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 811 | integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== 812 | 813 | compress-commons@^4.1.0: 814 | version "4.1.1" 815 | resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" 816 | integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== 817 | dependencies: 818 | buffer-crc32 "^0.2.13" 819 | crc32-stream "^4.0.2" 820 | normalize-path "^3.0.0" 821 | readable-stream "^3.6.0" 822 | 823 | concat-map@0.0.1: 824 | version "0.0.1" 825 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 826 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 827 | 828 | constructs@^3.3.69: 829 | version "3.3.162" 830 | resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.162.tgz#c87e0fe75f533353ff9d156028ee765542821980" 831 | integrity sha512-ODzbe3frWuIyIhBJs86pqWQWQYFRHHiM0DW4mnJcFgrMn6pyt2viUaAy38RFLDIGrlejy2oOFrEaOArx/CqFRw== 832 | 833 | core-util-is@~1.0.0: 834 | version "1.0.3" 835 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" 836 | integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== 837 | 838 | crc-32@^1.2.0: 839 | version "1.2.0" 840 | resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" 841 | integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== 842 | dependencies: 843 | exit-on-epipe "~1.0.1" 844 | printj "~1.1.0" 845 | 846 | crc32-stream@^4.0.2: 847 | version "4.0.2" 848 | resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" 849 | integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== 850 | dependencies: 851 | crc-32 "^1.2.0" 852 | readable-stream "^3.4.0" 853 | 854 | crypt@0.0.2: 855 | version "0.0.2" 856 | resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" 857 | integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= 858 | 859 | data-uri-to-buffer@3: 860 | version "3.0.1" 861 | resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" 862 | integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== 863 | 864 | debug@4: 865 | version "4.3.3" 866 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" 867 | integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== 868 | dependencies: 869 | ms "2.1.2" 870 | 871 | decamelize@^5.0.1: 872 | version "5.0.1" 873 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9" 874 | integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA== 875 | 876 | deep-is@~0.1.3: 877 | version "0.1.4" 878 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" 879 | integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== 880 | 881 | degenerator@^3.0.1: 882 | version "3.0.1" 883 | resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-3.0.1.tgz#7ef78ec0c8577a544477308ddf1d2d6e88d51f5b" 884 | integrity sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ== 885 | dependencies: 886 | ast-types "^0.13.2" 887 | escodegen "^1.8.1" 888 | esprima "^4.0.0" 889 | vm2 "^3.9.3" 890 | 891 | depd@~1.1.2: 892 | version "1.1.2" 893 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 894 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 895 | 896 | diff@^5.0.0: 897 | version "5.0.0" 898 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" 899 | integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== 900 | 901 | difflib@~0.2.1: 902 | version "0.2.4" 903 | resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" 904 | integrity sha1-teMDYabbAjF21WKJLbhZQKcY9H4= 905 | dependencies: 906 | heap ">= 0.2.0" 907 | 908 | dreamopt@~0.6.0: 909 | version "0.6.0" 910 | resolved "https://registry.yarnpkg.com/dreamopt/-/dreamopt-0.6.0.tgz#d813ccdac8d39d8ad526775514a13dda664d6b4b" 911 | integrity sha1-2BPM2sjTnYrVJndVFKE92mZNa0s= 912 | dependencies: 913 | wordwrap ">=0.0.2" 914 | 915 | emoji-regex@^8.0.0: 916 | version "8.0.0" 917 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 918 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 919 | 920 | end-of-stream@^1.4.1: 921 | version "1.4.4" 922 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 923 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 924 | dependencies: 925 | once "^1.4.0" 926 | 927 | es5-ext@0.8.x: 928 | version "0.8.2" 929 | resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.8.2.tgz#aba8d9e1943a895ac96837a62a39b3f55ecd94ab" 930 | integrity sha1-q6jZ4ZQ6iVrJaDemKjmz9V7NlKs= 931 | 932 | escalade@^3.1.1: 933 | version "3.1.1" 934 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 935 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 936 | 937 | escodegen@^1.8.1: 938 | version "1.14.3" 939 | resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" 940 | integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== 941 | dependencies: 942 | esprima "^4.0.1" 943 | estraverse "^4.2.0" 944 | esutils "^2.0.2" 945 | optionator "^0.8.1" 946 | optionalDependencies: 947 | source-map "~0.6.1" 948 | 949 | esprima@^4.0.0, esprima@^4.0.1: 950 | version "4.0.1" 951 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 952 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 953 | 954 | estraverse@^4.2.0: 955 | version "4.3.0" 956 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 957 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 958 | 959 | esutils@^2.0.2: 960 | version "2.0.3" 961 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 962 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 963 | 964 | events@1.1.1: 965 | version "1.1.1" 966 | resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" 967 | integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= 968 | 969 | exit-on-epipe@~1.0.1: 970 | version "1.0.1" 971 | resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" 972 | integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== 973 | 974 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 975 | version "3.1.3" 976 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 977 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 978 | 979 | fast-levenshtein@~2.0.6: 980 | version "2.0.6" 981 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 982 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 983 | 984 | file-uri-to-path@2: 985 | version "2.0.0" 986 | resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" 987 | integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== 988 | 989 | fill-range@^7.0.1: 990 | version "7.0.1" 991 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 992 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 993 | dependencies: 994 | to-regex-range "^5.0.1" 995 | 996 | fs-constants@^1.0.0: 997 | version "1.0.0" 998 | resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 999 | integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== 1000 | 1001 | fs-extra@^8.1.0: 1002 | version "8.1.0" 1003 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 1004 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== 1005 | dependencies: 1006 | graceful-fs "^4.2.0" 1007 | jsonfile "^4.0.0" 1008 | universalify "^0.1.0" 1009 | 1010 | fs-extra@^9.1.0: 1011 | version "9.1.0" 1012 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" 1013 | integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== 1014 | dependencies: 1015 | at-least-node "^1.0.0" 1016 | graceful-fs "^4.2.0" 1017 | jsonfile "^6.0.1" 1018 | universalify "^2.0.0" 1019 | 1020 | fs.realpath@^1.0.0: 1021 | version "1.0.0" 1022 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 1023 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 1024 | 1025 | fsevents@~2.3.2: 1026 | version "2.3.2" 1027 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 1028 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 1029 | 1030 | ftp@^0.3.10: 1031 | version "0.3.10" 1032 | resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" 1033 | integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= 1034 | dependencies: 1035 | readable-stream "1.1.x" 1036 | xregexp "2.0.0" 1037 | 1038 | get-caller-file@^2.0.5: 1039 | version "2.0.5" 1040 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 1041 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 1042 | 1043 | get-uri@3: 1044 | version "3.0.2" 1045 | resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" 1046 | integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== 1047 | dependencies: 1048 | "@tootallnate/once" "1" 1049 | data-uri-to-buffer "3" 1050 | debug "4" 1051 | file-uri-to-path "2" 1052 | fs-extra "^8.1.0" 1053 | ftp "^0.3.10" 1054 | 1055 | glob-parent@~5.1.2: 1056 | version "5.1.2" 1057 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 1058 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 1059 | dependencies: 1060 | is-glob "^4.0.1" 1061 | 1062 | glob@^7.1.4, glob@^7.2.0: 1063 | version "7.2.0" 1064 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 1065 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 1066 | dependencies: 1067 | fs.realpath "^1.0.0" 1068 | inflight "^1.0.4" 1069 | inherits "2" 1070 | minimatch "^3.0.4" 1071 | once "^1.3.0" 1072 | path-is-absolute "^1.0.0" 1073 | 1074 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 1075 | version "4.2.8" 1076 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" 1077 | integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== 1078 | 1079 | has-flag@^4.0.0: 1080 | version "4.0.0" 1081 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 1082 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 1083 | 1084 | "heap@>= 0.2.0": 1085 | version "0.2.7" 1086 | resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" 1087 | integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== 1088 | 1089 | http-errors@1.8.1: 1090 | version "1.8.1" 1091 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" 1092 | integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== 1093 | dependencies: 1094 | depd "~1.1.2" 1095 | inherits "2.0.4" 1096 | setprototypeof "1.2.0" 1097 | statuses ">= 1.5.0 < 2" 1098 | toidentifier "1.0.1" 1099 | 1100 | http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: 1101 | version "4.0.1" 1102 | resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" 1103 | integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== 1104 | dependencies: 1105 | "@tootallnate/once" "1" 1106 | agent-base "6" 1107 | debug "4" 1108 | 1109 | https-proxy-agent@5, https-proxy-agent@^5.0.0: 1110 | version "5.0.0" 1111 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" 1112 | integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== 1113 | dependencies: 1114 | agent-base "6" 1115 | debug "4" 1116 | 1117 | iconv-lite@0.4.24: 1118 | version "0.4.24" 1119 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 1120 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 1121 | dependencies: 1122 | safer-buffer ">= 2.1.2 < 3" 1123 | 1124 | ieee754@1.1.13: 1125 | version "1.1.13" 1126 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" 1127 | integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== 1128 | 1129 | ieee754@^1.1.13, ieee754@^1.1.4: 1130 | version "1.2.1" 1131 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 1132 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 1133 | 1134 | ignore@^5.1.9: 1135 | version "5.1.9" 1136 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" 1137 | integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== 1138 | 1139 | inflight@^1.0.4: 1140 | version "1.0.6" 1141 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1142 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 1143 | dependencies: 1144 | once "^1.3.0" 1145 | wrappy "1" 1146 | 1147 | inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: 1148 | version "2.0.4" 1149 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 1150 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 1151 | 1152 | ip@^1.1.5: 1153 | version "1.1.5" 1154 | resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" 1155 | integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= 1156 | 1157 | is-binary-path@~2.1.0: 1158 | version "2.1.0" 1159 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 1160 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 1161 | dependencies: 1162 | binary-extensions "^2.0.0" 1163 | 1164 | is-buffer@~1.1.6: 1165 | version "1.1.6" 1166 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 1167 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 1168 | 1169 | is-extglob@^2.1.1: 1170 | version "2.1.1" 1171 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 1172 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 1173 | 1174 | is-fullwidth-code-point@^3.0.0: 1175 | version "3.0.0" 1176 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 1177 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 1178 | 1179 | is-glob@^4.0.1, is-glob@~4.0.1: 1180 | version "4.0.3" 1181 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 1182 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1183 | dependencies: 1184 | is-extglob "^2.1.1" 1185 | 1186 | is-number@^7.0.0: 1187 | version "7.0.0" 1188 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 1189 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1190 | 1191 | isarray@0.0.1: 1192 | version "0.0.1" 1193 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 1194 | integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= 1195 | 1196 | isarray@^1.0.0, isarray@~1.0.0: 1197 | version "1.0.0" 1198 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 1199 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 1200 | 1201 | jmespath@0.15.0: 1202 | version "0.15.0" 1203 | resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" 1204 | integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= 1205 | 1206 | json-diff@^0.5.4: 1207 | version "0.5.4" 1208 | resolved "https://registry.yarnpkg.com/json-diff/-/json-diff-0.5.4.tgz#7bc8198c441756632aab66c7d9189d365a7a035a" 1209 | integrity sha512-q5Xmx9QXNOzOzIlMoYtLrLiu4Jl/Ce2bn0CNcv54PhyH89CI4GWlGVDye8ei2Ijt9R3U+vsWPsXpLUNob8bs8Q== 1210 | dependencies: 1211 | cli-color "~0.1.6" 1212 | difflib "~0.2.1" 1213 | dreamopt "~0.6.0" 1214 | 1215 | json-schema-traverse@^1.0.0: 1216 | version "1.0.0" 1217 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" 1218 | integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== 1219 | 1220 | jsonfile@^4.0.0: 1221 | version "4.0.0" 1222 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 1223 | integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= 1224 | optionalDependencies: 1225 | graceful-fs "^4.1.6" 1226 | 1227 | jsonfile@^6.0.1: 1228 | version "6.1.0" 1229 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" 1230 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 1231 | dependencies: 1232 | universalify "^2.0.0" 1233 | optionalDependencies: 1234 | graceful-fs "^4.1.6" 1235 | 1236 | jsonschema@^1.4.0: 1237 | version "1.4.0" 1238 | resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" 1239 | integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== 1240 | 1241 | lazystream@^1.0.0: 1242 | version "1.0.1" 1243 | resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" 1244 | integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== 1245 | dependencies: 1246 | readable-stream "^2.0.5" 1247 | 1248 | levn@~0.3.0: 1249 | version "0.3.0" 1250 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 1251 | integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= 1252 | dependencies: 1253 | prelude-ls "~1.1.2" 1254 | type-check "~0.3.2" 1255 | 1256 | lodash.defaults@^4.2.0: 1257 | version "4.2.0" 1258 | resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" 1259 | integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= 1260 | 1261 | lodash.difference@^4.5.0: 1262 | version "4.5.0" 1263 | resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" 1264 | integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= 1265 | 1266 | lodash.flatten@^4.4.0: 1267 | version "4.4.0" 1268 | resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" 1269 | integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= 1270 | 1271 | lodash.isplainobject@^4.0.6: 1272 | version "4.0.6" 1273 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 1274 | integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= 1275 | 1276 | lodash.truncate@^4.4.2: 1277 | version "4.4.2" 1278 | resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" 1279 | integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= 1280 | 1281 | lodash.union@^4.6.0: 1282 | version "4.6.0" 1283 | resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" 1284 | integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= 1285 | 1286 | lru-cache@^5.1.1: 1287 | version "5.1.1" 1288 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" 1289 | integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== 1290 | dependencies: 1291 | yallist "^3.0.2" 1292 | 1293 | lru-cache@^6.0.0: 1294 | version "6.0.0" 1295 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 1296 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1297 | dependencies: 1298 | yallist "^4.0.0" 1299 | 1300 | md5@^2.3.0: 1301 | version "2.3.0" 1302 | resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" 1303 | integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== 1304 | dependencies: 1305 | charenc "0.0.2" 1306 | crypt "0.0.2" 1307 | is-buffer "~1.1.6" 1308 | 1309 | mime@^2.6.0: 1310 | version "2.6.0" 1311 | resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" 1312 | integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== 1313 | 1314 | minimatch@>=3.0, minimatch@^3.0.4: 1315 | version "3.0.4" 1316 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1317 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 1318 | dependencies: 1319 | brace-expansion "^1.1.7" 1320 | 1321 | ms@2.1.2: 1322 | version "2.1.2" 1323 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1324 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1325 | 1326 | mute-stream@~0.0.4: 1327 | version "0.0.8" 1328 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 1329 | integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== 1330 | 1331 | netmask@^2.0.1: 1332 | version "2.0.2" 1333 | resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" 1334 | integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== 1335 | 1336 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1337 | version "3.0.0" 1338 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 1339 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1340 | 1341 | once@^1.3.0, once@^1.4.0: 1342 | version "1.4.0" 1343 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1344 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 1345 | dependencies: 1346 | wrappy "1" 1347 | 1348 | optionator@^0.8.1: 1349 | version "0.8.3" 1350 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" 1351 | integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== 1352 | dependencies: 1353 | deep-is "~0.1.3" 1354 | fast-levenshtein "~2.0.6" 1355 | levn "~0.3.0" 1356 | prelude-ls "~1.1.2" 1357 | type-check "~0.3.2" 1358 | word-wrap "~1.2.3" 1359 | 1360 | pac-proxy-agent@^5.0.0: 1361 | version "5.0.0" 1362 | resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz#b718f76475a6a5415c2efbe256c1c971c84f635e" 1363 | integrity sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ== 1364 | dependencies: 1365 | "@tootallnate/once" "1" 1366 | agent-base "6" 1367 | debug "4" 1368 | get-uri "3" 1369 | http-proxy-agent "^4.0.1" 1370 | https-proxy-agent "5" 1371 | pac-resolver "^5.0.0" 1372 | raw-body "^2.2.0" 1373 | socks-proxy-agent "5" 1374 | 1375 | pac-resolver@^5.0.0: 1376 | version "5.0.0" 1377 | resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.0.tgz#1d717a127b3d7a9407a16d6e1b012b13b9ba8dc0" 1378 | integrity sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA== 1379 | dependencies: 1380 | degenerator "^3.0.1" 1381 | ip "^1.1.5" 1382 | netmask "^2.0.1" 1383 | 1384 | path-is-absolute@^1.0.0: 1385 | version "1.0.1" 1386 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1387 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1388 | 1389 | picomatch@^2.0.4, picomatch@^2.2.1: 1390 | version "2.3.0" 1391 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" 1392 | integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== 1393 | 1394 | prelude-ls@~1.1.2: 1395 | version "1.1.2" 1396 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 1397 | integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= 1398 | 1399 | printj@~1.1.0: 1400 | version "1.1.2" 1401 | resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" 1402 | integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== 1403 | 1404 | process-nextick-args@~2.0.0: 1405 | version "2.0.1" 1406 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 1407 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 1408 | 1409 | promptly@^3.2.0: 1410 | version "3.2.0" 1411 | resolved "https://registry.yarnpkg.com/promptly/-/promptly-3.2.0.tgz#a5517fbbf59bd31c1751d4e1d9bef1714f42b9d8" 1412 | integrity sha512-WnR9obtgW+rG4oUV3hSnNGl1pHm3V1H/qD9iJBumGSmVsSC5HpZOLuu8qdMb6yCItGfT7dcRszejr/5P3i9Pug== 1413 | dependencies: 1414 | read "^1.0.4" 1415 | 1416 | proxy-agent@^5.0.0: 1417 | version "5.0.0" 1418 | resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-5.0.0.tgz#d31405c10d6e8431fde96cba7a0c027ce01d633b" 1419 | integrity sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g== 1420 | dependencies: 1421 | agent-base "^6.0.0" 1422 | debug "4" 1423 | http-proxy-agent "^4.0.0" 1424 | https-proxy-agent "^5.0.0" 1425 | lru-cache "^5.1.1" 1426 | pac-proxy-agent "^5.0.0" 1427 | proxy-from-env "^1.0.0" 1428 | socks-proxy-agent "^5.0.0" 1429 | 1430 | proxy-from-env@^1.0.0: 1431 | version "1.1.0" 1432 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 1433 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 1434 | 1435 | punycode@1.3.2: 1436 | version "1.3.2" 1437 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" 1438 | integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= 1439 | 1440 | punycode@^2.1.0: 1441 | version "2.1.1" 1442 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 1443 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 1444 | 1445 | querystring@0.2.0: 1446 | version "0.2.0" 1447 | resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 1448 | integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= 1449 | 1450 | raw-body@^2.2.0: 1451 | version "2.4.2" 1452 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" 1453 | integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== 1454 | dependencies: 1455 | bytes "3.1.1" 1456 | http-errors "1.8.1" 1457 | iconv-lite "0.4.24" 1458 | unpipe "1.0.0" 1459 | 1460 | read@^1.0.4: 1461 | version "1.0.7" 1462 | resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" 1463 | integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= 1464 | dependencies: 1465 | mute-stream "~0.0.4" 1466 | 1467 | readable-stream@1.1.x: 1468 | version "1.1.14" 1469 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 1470 | integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= 1471 | dependencies: 1472 | core-util-is "~1.0.0" 1473 | inherits "~2.0.1" 1474 | isarray "0.0.1" 1475 | string_decoder "~0.10.x" 1476 | 1477 | readable-stream@^2.0.0, readable-stream@^2.0.5: 1478 | version "2.3.7" 1479 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 1480 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 1481 | dependencies: 1482 | core-util-is "~1.0.0" 1483 | inherits "~2.0.3" 1484 | isarray "~1.0.0" 1485 | process-nextick-args "~2.0.0" 1486 | safe-buffer "~5.1.1" 1487 | string_decoder "~1.1.1" 1488 | util-deprecate "~1.0.1" 1489 | 1490 | readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: 1491 | version "3.6.0" 1492 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 1493 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 1494 | dependencies: 1495 | inherits "^2.0.3" 1496 | string_decoder "^1.1.1" 1497 | util-deprecate "^1.0.1" 1498 | 1499 | readdir-glob@^1.0.0: 1500 | version "1.1.1" 1501 | resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" 1502 | integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA== 1503 | dependencies: 1504 | minimatch "^3.0.4" 1505 | 1506 | readdirp@~3.6.0: 1507 | version "3.6.0" 1508 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 1509 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1510 | dependencies: 1511 | picomatch "^2.2.1" 1512 | 1513 | require-directory@^2.1.1: 1514 | version "2.1.1" 1515 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 1516 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 1517 | 1518 | require-from-string@^2.0.2: 1519 | version "2.0.2" 1520 | resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" 1521 | integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== 1522 | 1523 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1524 | version "5.1.2" 1525 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1526 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1527 | 1528 | safe-buffer@~5.2.0: 1529 | version "5.2.1" 1530 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1531 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1532 | 1533 | "safer-buffer@>= 2.1.2 < 3": 1534 | version "2.1.2" 1535 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1536 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1537 | 1538 | sax@1.2.1: 1539 | version "1.2.1" 1540 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" 1541 | integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= 1542 | 1543 | sax@>=0.6.0: 1544 | version "1.2.4" 1545 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 1546 | integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== 1547 | 1548 | semver@^7.3.5: 1549 | version "7.3.5" 1550 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" 1551 | integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== 1552 | dependencies: 1553 | lru-cache "^6.0.0" 1554 | 1555 | setprototypeof@1.2.0: 1556 | version "1.2.0" 1557 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 1558 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 1559 | 1560 | slice-ansi@^4.0.0: 1561 | version "4.0.0" 1562 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" 1563 | integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== 1564 | dependencies: 1565 | ansi-styles "^4.0.0" 1566 | astral-regex "^2.0.0" 1567 | is-fullwidth-code-point "^3.0.0" 1568 | 1569 | smart-buffer@^4.1.0: 1570 | version "4.2.0" 1571 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" 1572 | integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== 1573 | 1574 | socks-proxy-agent@5, socks-proxy-agent@^5.0.0: 1575 | version "5.0.1" 1576 | resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" 1577 | integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== 1578 | dependencies: 1579 | agent-base "^6.0.2" 1580 | debug "4" 1581 | socks "^2.3.3" 1582 | 1583 | socks@^2.3.3: 1584 | version "2.6.1" 1585 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" 1586 | integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== 1587 | dependencies: 1588 | ip "^1.1.5" 1589 | smart-buffer "^4.1.0" 1590 | 1591 | source-map-support@^0.5.20: 1592 | version "0.5.21" 1593 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 1594 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 1595 | dependencies: 1596 | buffer-from "^1.0.0" 1597 | source-map "^0.6.0" 1598 | 1599 | source-map@^0.6.0, source-map@~0.6.1: 1600 | version "0.6.1" 1601 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1602 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1603 | 1604 | "statuses@>= 1.5.0 < 2": 1605 | version "1.5.0" 1606 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 1607 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 1608 | 1609 | string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 1610 | version "4.2.3" 1611 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1612 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1613 | dependencies: 1614 | emoji-regex "^8.0.0" 1615 | is-fullwidth-code-point "^3.0.0" 1616 | strip-ansi "^6.0.1" 1617 | 1618 | string_decoder@^1.1.1: 1619 | version "1.3.0" 1620 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 1621 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 1622 | dependencies: 1623 | safe-buffer "~5.2.0" 1624 | 1625 | string_decoder@~0.10.x: 1626 | version "0.10.31" 1627 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 1628 | integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= 1629 | 1630 | string_decoder@~1.1.1: 1631 | version "1.1.1" 1632 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1633 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 1634 | dependencies: 1635 | safe-buffer "~5.1.0" 1636 | 1637 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 1638 | version "6.0.1" 1639 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1640 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1641 | dependencies: 1642 | ansi-regex "^5.0.1" 1643 | 1644 | supports-color@^7.1.0: 1645 | version "7.2.0" 1646 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1647 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1648 | dependencies: 1649 | has-flag "^4.0.0" 1650 | 1651 | table@^6.7.3: 1652 | version "6.7.5" 1653 | resolved "https://registry.yarnpkg.com/table/-/table-6.7.5.tgz#f04478c351ef3d8c7904f0e8be90a1b62417d238" 1654 | integrity sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw== 1655 | dependencies: 1656 | ajv "^8.0.1" 1657 | lodash.truncate "^4.4.2" 1658 | slice-ansi "^4.0.0" 1659 | string-width "^4.2.3" 1660 | strip-ansi "^6.0.1" 1661 | 1662 | tar-stream@^2.2.0: 1663 | version "2.2.0" 1664 | resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" 1665 | integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== 1666 | dependencies: 1667 | bl "^4.0.3" 1668 | end-of-stream "^1.4.1" 1669 | fs-constants "^1.0.0" 1670 | inherits "^2.0.3" 1671 | readable-stream "^3.1.1" 1672 | 1673 | to-regex-range@^5.0.1: 1674 | version "5.0.1" 1675 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1676 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1677 | dependencies: 1678 | is-number "^7.0.0" 1679 | 1680 | toidentifier@1.0.1: 1681 | version "1.0.1" 1682 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" 1683 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 1684 | 1685 | tslib@^2.0.1: 1686 | version "2.3.1" 1687 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" 1688 | integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== 1689 | 1690 | type-check@~0.3.2: 1691 | version "0.3.2" 1692 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1693 | integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 1694 | dependencies: 1695 | prelude-ls "~1.1.2" 1696 | 1697 | typescript@~3.7.2: 1698 | version "3.7.7" 1699 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.7.tgz#c931733e2ec10dda56b855b379cc488a72a81199" 1700 | integrity sha512-MmQdgo/XenfZPvVLtKZOq9jQQvzaUAUpcKW8Z43x9B2fOm4S5g//tPtMweZUIP+SoBqrVPEIm+dJeQ9dfO0QdA== 1701 | 1702 | universalify@^0.1.0: 1703 | version "0.1.2" 1704 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 1705 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 1706 | 1707 | universalify@^2.0.0: 1708 | version "2.0.0" 1709 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" 1710 | integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== 1711 | 1712 | unpipe@1.0.0: 1713 | version "1.0.0" 1714 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1715 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 1716 | 1717 | uri-js@^4.2.2: 1718 | version "4.4.1" 1719 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 1720 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1721 | dependencies: 1722 | punycode "^2.1.0" 1723 | 1724 | url@0.10.3: 1725 | version "0.10.3" 1726 | resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" 1727 | integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= 1728 | dependencies: 1729 | punycode "1.3.2" 1730 | querystring "0.2.0" 1731 | 1732 | util-deprecate@^1.0.1, util-deprecate@~1.0.1: 1733 | version "1.0.2" 1734 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1735 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1736 | 1737 | uuid@3.3.2: 1738 | version "3.3.2" 1739 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" 1740 | integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== 1741 | 1742 | uuid@^8.3.2: 1743 | version "8.3.2" 1744 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" 1745 | integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== 1746 | 1747 | vm2@^3.9.3: 1748 | version "3.9.5" 1749 | resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.5.tgz#5288044860b4bbace443101fcd3bddb2a0aa2496" 1750 | integrity sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng== 1751 | 1752 | word-wrap@~1.2.3: 1753 | version "1.2.3" 1754 | resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 1755 | integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 1756 | 1757 | wordwrap@>=0.0.2: 1758 | version "1.0.0" 1759 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1760 | integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= 1761 | 1762 | wrap-ansi@^7.0.0: 1763 | version "7.0.0" 1764 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 1765 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 1766 | dependencies: 1767 | ansi-styles "^4.0.0" 1768 | string-width "^4.1.0" 1769 | strip-ansi "^6.0.0" 1770 | 1771 | wrappy@1: 1772 | version "1.0.2" 1773 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1774 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1775 | 1776 | xml2js@0.4.19: 1777 | version "0.4.19" 1778 | resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" 1779 | integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== 1780 | dependencies: 1781 | sax ">=0.6.0" 1782 | xmlbuilder "~9.0.1" 1783 | 1784 | xmlbuilder@~9.0.1: 1785 | version "9.0.7" 1786 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" 1787 | integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= 1788 | 1789 | xregexp@2.0.0: 1790 | version "2.0.0" 1791 | resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" 1792 | integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= 1793 | 1794 | y18n@^5.0.5: 1795 | version "5.0.8" 1796 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 1797 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 1798 | 1799 | yallist@^3.0.2: 1800 | version "3.1.1" 1801 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 1802 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== 1803 | 1804 | yallist@^4.0.0: 1805 | version "4.0.0" 1806 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1807 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1808 | 1809 | yaml@1.10.2: 1810 | version "1.10.2" 1811 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" 1812 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== 1813 | 1814 | yargs-parser@^20.2.2: 1815 | version "20.2.9" 1816 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" 1817 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== 1818 | 1819 | yargs@^16.2.0: 1820 | version "16.2.0" 1821 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" 1822 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== 1823 | dependencies: 1824 | cliui "^7.0.2" 1825 | escalade "^3.1.1" 1826 | get-caller-file "^2.0.5" 1827 | require-directory "^2.1.1" 1828 | string-width "^4.2.0" 1829 | y18n "^5.0.5" 1830 | yargs-parser "^20.2.2" 1831 | 1832 | zip-stream@^4.1.0: 1833 | version "4.1.0" 1834 | resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" 1835 | integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== 1836 | dependencies: 1837 | archiver-utils "^2.1.0" 1838 | compress-commons "^4.1.0" 1839 | readable-stream "^3.6.0" 1840 | --------------------------------------------------------------------------------