diff --git a/.gitignore b/.gitignore index 5de213807..bd9a04ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules/ dist/ cdk.out/ -cdk.context.json \ No newline at end of file +cdk.context.json diff --git a/README.md b/README.md index 815fb9873..095b1f696 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Topo Workflows -Topo workflows are run on a AWS EKS Cluster using [Argo Workflows](https://argoproj.github.io/argo-workflows/) +Topo workflows are run on a AWS EKS Cluster using [Argo Workflows](https://argoproj.github.io/argo-workflows/). The detailed configuration is available in [this repo](./config/). To get setup you need access to the Argo user role inside the EKS cluster, you will need to contact someone from Topo Data Engineering to get access, all Imagery maintainers will already have access. diff --git a/cdk8s.yaml b/cdk8s.yaml index 850e3e6db..5375d20e5 100644 --- a/cdk8s.yaml +++ b/cdk8s.yaml @@ -1,2 +1,5 @@ app: npx tsx config/cdk8s.ts language: typescript +imports: + - https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.sh_provisioners.yaml + - https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.k8s.aws_awsnodetemplates.yaml diff --git a/config/README.md b/config/README.md index 8f0fb3636..41a451c40 100644 --- a/config/README.md +++ b/config/README.md @@ -1,14 +1,20 @@ -# Kubernetes configuration with CDK8s and AWS-CDK +# Topo-Workflows Infrastructure -Collection of AWS & Kubernetes resources. +The infrastructure running the workflows is mainly based on a Kubernetes (EKS) cluster and Argo Workflows. It is currently run on AWS. +Generally all Kubernetes resources are defined with cdk8s and anything that needs AWS interactions such as service accounts are defined with CDK. -## Components +## EKS Cluster / AWS CDK -Main entry point: [cdk8s](./cdk8s.ts) and [cdk](./cdk.ts) +The EKS Cluster base configuration is defined in `./cdk.ts` using [`aws-cdk`](https://aws.amazon.com/cdk/). -Generally all Kubernetes resources are defined with cdk8s and anything that needs AWS interactions such as service accounts are defined with CDK. +## Kubernetes resources / CDK8s + +The additional components (or Kubernetes resources) running on the EKS cluster are defined in `./cdk8s` using [`cdk8s`](https://cdk8s.io/). + +Main entry point: [app](./cdk8s.ts) -- argo - Argo workflows for use with [linz/topo-workflows](https://github.com/linz/topo-workflows) +- Argo - Argo workflows for use with [linz/topo-workflows](https://github.com/linz/topo-workflows) +- Karpenter ### Argo Workflows @@ -16,9 +22,25 @@ Generally all Kubernetes resources are defined with cdk8s and anything that need ConfigMap that list the synchronization limits for parallel execution of the workflows. -## Development +### Karpenter + +TODO + +### Generate code + +Generate code from Helm: +It is possible to generate a specific Helm construct for the component if their chart includes a `value.schema.json`. This is useful to provide typing hints when specifying their configuration () - +To generate the Helm Construct for a specific Chart, follow the instructions [here](https://github.com/cdk8s-team/cdk8s/blob/master/docs/cli/import.md#values-schema): + +Specify the output for the imports: + +`--output config/imports/` + +However, some of the component Helm charts do not have a `values.schema.json`. For those we won't generate any code and use the default `Helm` construct: + +- aws-for-fluent-bit () +- Karpenter ## Usage (for test) @@ -45,3 +67,7 @@ kubectl apply -f dist/ ## Deployment The deployment of the K8s config is managed by GithubActions in [main](../.github/workflows/main.yml). + +## Troubleshoot + +- [DNS](../docs/dns.configuration.md) diff --git a/config/cdk.ts b/config/cdk.ts index 532865190..27fc83b3c 100644 --- a/config/cdk.ts +++ b/config/cdk.ts @@ -1,11 +1,14 @@ import { App } from 'aws-cdk-lib'; +import { CLUSTER_NAME } from './constants'; import { LinzEksCluster } from './eks/cluster'; const app = new App(); async function main(): Promise { - new LinzEksCluster(app, 'Workflows', { env: { region: 'ap-southeast-2', account: process.env.CDK_DEFAULT_ACCOUNT } }); + new LinzEksCluster(app, CLUSTER_NAME, { + env: { region: 'ap-southeast-2', account: process.env.CDK_DEFAULT_ACCOUNT }, + }); app.synth(); } diff --git a/config/cdk8s.ts b/config/cdk8s.ts index 0e5100da9..395051ae2 100644 --- a/config/cdk8s.ts +++ b/config/cdk8s.ts @@ -1,11 +1,42 @@ import { App } from 'cdk8s'; import { ArgoSemaphore } from './charts/argo.semaphores'; +import { Karpenter, KarpenterProvisioner } from './charts/karpenter'; +import { CoreDns } from './charts/kube-system.coredns'; +import { CfnOutputKeys } from './constants'; +import { CLUSTER_NAME } from './constants'; +import { getCfnOutputs } from './util/cloud.formation'; const app = new App(); async function main(): Promise { + // Get cloudformation outputs + const cfnOutputs = await getCfnOutputs(CLUSTER_NAME); + const missingKeys = [...Object.values(CfnOutputKeys.Karpenter)].filter((f) => cfnOutputs[f] == null); + if (missingKeys.length > 0) { + throw new Error(`Missing CloudFormation Outputs for keys ${missingKeys.join(', ')}`); + } + new ArgoSemaphore(app, 'semaphore', {}); + new CoreDns(app, 'Dns', {}); + + const karpenter = new Karpenter(app, 'karpenter', { + clusterName: CLUSTER_NAME, + clusterEndpoint: cfnOutputs[CfnOutputKeys.Karpenter.ClusterEndpoint], + saRoleName: cfnOutputs[CfnOutputKeys.Karpenter.ServiceAccountName], + saRoleArn: cfnOutputs[CfnOutputKeys.Karpenter.ServiceAccountRoleArn], + instanceProfile: cfnOutputs[CfnOutputKeys.Karpenter.DefaultInstanceProfile], + }); + + const karpenterProvisioner = new KarpenterProvisioner(app, 'karpenter-provisioner', { + clusterName: CLUSTER_NAME, + clusterEndpoint: cfnOutputs[CfnOutputKeys.Karpenter.ClusterEndpoint], + saRoleName: cfnOutputs[CfnOutputKeys.Karpenter.ServiceAccountName], + saRoleArn: cfnOutputs[CfnOutputKeys.Karpenter.ServiceAccountRoleArn], + instanceProfile: cfnOutputs[CfnOutputKeys.Karpenter.DefaultInstanceProfile], + }); + + karpenterProvisioner.addDependency(karpenter); app.synth(); } diff --git a/config/cfn.output.ts b/config/cfn.output.ts deleted file mode 100644 index fc89d60d4..000000000 --- a/config/cfn.output.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const CfnOutput = { - Karpenter: { - ServiceAccountRoleArn: 'KarpenterServiceAccountRoleArn', - }, -}; diff --git a/config/charts/argo.semaphores.ts b/config/charts/argo.semaphores.ts index fe2faddf0..e0d822120 100644 --- a/config/charts/argo.semaphores.ts +++ b/config/charts/argo.semaphores.ts @@ -2,7 +2,7 @@ import { Chart, ChartProps } from 'cdk8s'; import * as kplus from 'cdk8s-plus-27'; import { Construct } from 'constructs'; -import { applyDefaultLabels } from '../util/labels'; +import { applyDefaultLabels } from '../util/labels.js'; export class ArgoSemaphore extends Chart { constructor(scope: Construct, id: string, props: ChartProps) { diff --git a/config/charts/karpenter.ts b/config/charts/karpenter.ts new file mode 100644 index 000000000..3b6f41f83 --- /dev/null +++ b/config/charts/karpenter.ts @@ -0,0 +1,128 @@ +import { Chart, ChartProps, Duration, Helm } from 'cdk8s'; +import { Construct } from 'constructs'; + +import { AwsNodeTemplateSpec } from '../imports/karpenter.k8s.aws.js'; +import { Provisioner, ProvisionerSpecLimitsResources } from '../imports/karpenter.sh.js'; +import { applyDefaultLabels } from '../util/labels.js'; + +export interface KarpenterProps { + clusterName: string; + saRoleName: string; + saRoleArn: string; + clusterEndpoint: string; + instanceProfile: string; +} + +export class Karpenter extends Chart { + constructor(scope: Construct, id: string, props: KarpenterProps & ChartProps) { + super(scope, id, applyDefaultLabels(props, 'karpenter', 'v0.31.0', 'karpenter', 'workflows')); + + // Deploying the CRD + new Helm(this, 'karpenter-crd', { + chart: 'oci://public.ecr.aws/karpenter/karpenter-crd', + namespace: 'karpenter', + version: 'v0.31.0', + }); + + // Karpenter is using `oci` rather than regular helm repo: https://gallery.ecr.aws/karpenter/karpenter. + // This Helm constructor has been tricked to be able to use `oci`, + // the `oci` repo is passed inside `chart` instead of `repo` so the generated `helm` + // command is the following: + // [ + // 'template', + // '-f', + // '/tmp/cdk8s-helm-keYZCA/overrides.yaml', + // '--version', + // 'v0.31.0', + // '--namespace', + // 'karpenter', + // 'karpenter-c870a560', + // 'oci://public.ecr.aws/karpenter/karpenter' + // ] + new Helm(this, 'karpenter', { + chart: 'oci://public.ecr.aws/karpenter/karpenter', + namespace: 'karpenter', + version: 'v0.31.0', + values: { + serviceAccount: { + create: false, + name: props.saRoleName, + annotations: { 'eks.amazonaws.com/role-arn': props.saRoleArn }, + }, + settings: { + aws: { + clusterName: props.clusterName, + clusterEndpoint: props.clusterEndpoint, + defaultInstanceProfile: props.instanceProfile, + }, + }, + }, + }); + } +} + +export class KarpenterProvisioner extends Chart { + constructor(scope: Construct, id: string, props: KarpenterProps & ChartProps) { + super(scope, id, applyDefaultLabels(props, 'karpenter', 'v0.31.0', 'karpenter', 'workflows')); + + // Subnets need to be opted into, ideally a tag on subnets would be the best bet here + // but CDK does not easily allow us to tag Subnets that are not created by us + const subnetSelector = { Name: '*' }; + + const provider: AwsNodeTemplateSpec = { + amiFamily: 'Bottlerocket', + subnetSelector, + securityGroupSelector: { [`kubernetes.io/cluster/${props.clusterName}`]: 'owned' }, + instanceProfile: props.instanceProfile, + blockDeviceMappings: [ + // { + // deviceName: '/dev/xvdb', + // ebs: { + // volumeType: 'gp3', + // volumeSize: '200Gi', + // deleteOnTermination: true, + // }, + // }, + ], + }; + + new Provisioner(this, 'ClusterAmd64WorkerNodes', { + metadata: { name: `eks-karpenter-${props.clusterName}-amd64`.toLowerCase(), namespace: 'karpenter' }, + spec: { + // Ensure only pods that tolerate spot run on spot instance types + // to prevent long running pods (eg kube-dns) being moved. + taints: [{ key: 'karpenter.sh/capacity-type', value: 'spot', effect: 'NoSchedule' }], + requirements: [ + { key: 'karpenter.sh/capacity-type', operator: 'In', values: ['spot'] }, + { key: 'kubernetes.io/arch', operator: 'In', values: ['amd64'] }, + { key: 'karpenter.k8s.aws/instance-family', operator: 'In', values: ['c5', 'c6i', 'c6a'] }, + ], + limits: { resources: { cpu: ProvisionerSpecLimitsResources.fromString('20000m') } }, + provider, + ttlSecondsAfterEmpty: Duration.minutes(1).toSeconds(), // optional, but never scales down if not set + }, + }); + + new Provisioner(this, 'ClusterArmWorkerNodes', { + metadata: { name: `eks-karpenter-${props.clusterName}-arm64`.toLowerCase(), namespace: 'karpenter' }, + spec: { + taints: [ + // Instances that want ARM have to tolerate the arm taint + // This prevents some pods from accidentally trying to start on ARM + { key: 'kubernetes.io/arch', value: 'arm64', effect: 'NoSchedule' }, + // Ensure only pods that tolerate spot run on spot instance types + // to prevent long running pods (eg kube-dns) being moved. + { key: 'karpenter.sh/capacity-type', value: 'spot', effect: 'NoSchedule' }, + ], + requirements: [ + { key: 'karpenter.sh/capacity-type', operator: 'In', values: ['spot'] }, + { key: 'kubernetes.io/arch', operator: 'In', values: ['arm64'] }, + { key: 'karpenter.k8s.aws/instance-family', operator: 'In', values: ['c7g', 'c6g'] }, + ], + limits: { resources: { cpu: ProvisionerSpecLimitsResources.fromString('20000m') } }, + provider, + ttlSecondsAfterEmpty: Duration.minutes(1).toSeconds(), // optional, but never scales down if not set + }, + }); + } +} diff --git a/config/charts/kube-system.coredns.ts b/config/charts/kube-system.coredns.ts new file mode 100644 index 000000000..aaaf3ca8a --- /dev/null +++ b/config/charts/kube-system.coredns.ts @@ -0,0 +1,65 @@ +import { Chart, ChartProps } from 'cdk8s'; +import * as kplus from 'cdk8s-plus-27'; +import { Construct } from 'constructs'; + +import { applyDefaultLabels } from '../util/labels.js'; + +/** + * This cluster is setup as dual ipv4/ipv6 where ipv4 is used for external traffic + * and ipv6 for internal traffic. + * + * This means all traffic ingressing/egressing the cluster must go over ipv4. + * + * By default coredns will resolve AAAA (ipv6) records for external hosts even though + * the cluster is unable to reach them + * + * This configuration splits coredns into two zones + * + * - `.cluster.local` - internal to the cluster + * - `.` - everything else + * + * The internal cluster allows `ipv6` resolutions, while `.` prevents `AAAA` resolutions using + * `rewrite stop type AAAA A` + * + */ +export class CoreDns extends Chart { + constructor(scope: Construct, id: string, props: ChartProps) { + super(scope, id, applyDefaultLabels(props, 'coredns', 'v1', 'kube-dns', 'kube-dns')); + + new kplus.ConfigMap(this, 'coredns', { + metadata: { name: 'coredns', namespace: 'kube-system' }, + data: { + // FIXME: is there a better way of handling config files inside of cdk8s + Corefile: ` +cluster.local:53 { + log + errors + health + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance +} + +.:53 { + log + errors + health + rewrite stop type AAAA A + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance +}`, + }, + }); + } +} diff --git a/config/constants.ts b/config/constants.ts new file mode 100644 index 000000000..b5a57f15a --- /dev/null +++ b/config/constants.ts @@ -0,0 +1,12 @@ +/* Cluster name */ +export const CLUSTER_NAME = 'Workflows'; + +/* CloudFormation Output to access from CDK8s */ +export const CfnOutputKeys = { + Karpenter: { + ServiceAccountName: 'KarpenterServiceAccountName', + ServiceAccountRoleArn: 'KarpenterServiceAccountRoleArn', + ClusterEndpoint: 'ClusterEndpoint', + DefaultInstanceProfile: 'DefaultInstanceProfile', + }, +}; diff --git a/config/eks/cluster.ts b/config/eks/cluster.ts index 2ab084144..9adfcf960 100644 --- a/config/eks/cluster.ts +++ b/config/eks/cluster.ts @@ -1,25 +1,40 @@ import { KubectlV27Layer } from '@aws-cdk/lambda-layer-kubectl-v27'; -import { Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { Aws, CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; import { InstanceType, IVpc, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; import { Cluster, ClusterLoggingTypes, IpFamily, KubernetesVersion, NodegroupAmiType } from 'aws-cdk-lib/aws-eks'; -import { Role } from 'aws-cdk-lib/aws-iam'; +import { + CfnInstanceProfile, + Effect, + ManagedPolicy, + Policy, + PolicyStatement, + Role, + ServicePrincipal, +} from 'aws-cdk-lib/aws-iam'; +import { IBucket } from 'aws-cdk-lib/aws-s3'; import { BlockPublicAccess, Bucket } from 'aws-cdk-lib/aws-s3'; import { Construct } from 'constructs'; +import { CfnOutputKeys } from '../constants'; + interface EksClusterProps extends StackProps {} export class LinzEksCluster extends Stack { + /* Cluster ID */ + id: string; /** Version of EKS to use, this must be aligned to the `kubectlLayer` */ version = KubernetesVersion.V1_27; /** Argo needs a temporary bucket to store objects */ tempBucket: Bucket; - + /* Bucket where read/write roles config files are stored */ + configBucket: IBucket; vpc: IVpc; - cluster: Cluster; + nodeRole: Role; constructor(scope: Construct, id: string, props: EksClusterProps) { super(scope, id, props); + this.id = id; this.tempBucket = new Bucket(this, 'Scratch', { /** linz-workflows-scratch */ @@ -36,6 +51,8 @@ export class LinzEksCluster extends Stack { ], }); + this.configBucket = Bucket.fromBucketName(this, 'BucketConfig', 'linz-bucket-config'); + this.vpc = Vpc.fromLookup(this, 'Vpc', { tags: { BaseVPC: 'true' } }); this.cluster = new Cluster(this, `Eks${id}`, { @@ -69,6 +86,25 @@ export class LinzEksCluster extends Stack { const accountAdminRole = Role.fromRoleName(this, 'AccountAdminRole', 'AccountAdminRole'); this.cluster.awsAuth.addMastersRole(accountAdminRole); + // This is the role that the new nodes will start as + this.nodeRole = new Role(this, 'NodeRole', { + assumedBy: new ServicePrincipal(`ec2.${Aws.URL_SUFFIX}`), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('AmazonEKS_CNI_Policy'), + ManagedPolicy.fromAwsManagedPolicyName('AmazonEKSWorkerNodePolicy'), + ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly'), + ], + }); + + // `aws-node` needs to assign ipv6 addresses which is not part of the base AmazonEKS_CNI_Policy + this.nodeRole.addToPolicy( + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['ec2:AssignIpv6Addresses', 'ec2:UnassignIpv6Addresses'], + resources: ['arn:aws:ec2:*:*:network-interface/*'], + }), + ); + this.configureEks(); } @@ -79,6 +115,79 @@ export class LinzEksCluster extends Stack { * or name space creation */ configureEks(): void { + // Karpenter + this.tempBucket.grantReadWrite(this.nodeRole); + this.configBucket.grantRead(this.nodeRole); + this.nodeRole.addToPrincipalPolicy(new PolicyStatement({ actions: ['sts:AssumeRole'], resources: ['*'] })); + + this.cluster.awsAuth.addRoleMapping(this.nodeRole, { + username: 'system:node:{{EC2PrivateDNSName}}', + groups: ['system:bootstrappers', 'system:nodes'], + }); + + const namespace = this.cluster.addManifest('namespace', { + apiVersion: 'v1', + kind: 'Namespace', + metadata: { name: 'karpenter' }, + }); + const serviceAccount = this.cluster.addServiceAccount('karpenter-controller-sa', { namespace: 'karpenter' }); + serviceAccount.node.addDependency(namespace); + // Nasty hack so this account has access to spin up EC2s inside of LINZ's network + serviceAccount.role.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonEC2SpotFleetTaggingRole'), + ); + + // Allow Karpenter to start ec2 instances + // FIXME: some policies are missing. See https://github.com/aws/karpenter/blob/8c33a40733b90aa0bb42a6436152374f7b359f69/website/content/en/docs/getting-started/getting-started-with-karpenter/cloudformation.yaml#L40 + // The current policies are based on https://github.com/eksctl-io/eksctl/blob/main/pkg/cfn/builder/karpenter_test.go#L111 + new Policy(this, 'ControllerPolicy', { + roles: [serviceAccount.role], + statements: [ + new PolicyStatement({ + actions: [ + 'ec2:CreateFleet', + 'ec2:CreateLaunchTemplate', + 'ec2:CreateTags', + 'ec2:DescribeAvailabilityZones', + 'ec2:DescribeInstanceTypeOfferings', + 'ec2:DescribeInstanceTypes', + 'ec2:DescribeInstances', + 'ec2:DescribeLaunchTemplates', + 'ec2:DescribeSecurityGroups', + 'ec2:DescribeSubnets', + 'ec2:DeleteLaunchTemplate', + 'ec2:RunInstances', + 'ec2:TerminateInstances', + 'ec2:DescribeImages', + 'ec2:DescribeSpotPriceHistory', + 'iam:PassRole', + 'iam:CreateServiceLinkedRole', + 'ssm:GetParameter', + 'pricing:GetProducts', + // LINZ requires instances to be encrypted with a KMS key + 'kms:Encrypt', + 'kms:Decrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + 'kms:CreateGrant', + 'kms:DescribeKey', + ], + resources: ['*'], + }), + ], + }); + + const instanceProfile = new CfnInstanceProfile(this, 'InstanceProfile', { + roles: [this.nodeRole.roleName], + instanceProfileName: `${this.cluster.clusterName}-${this.id}`, // Must be specified to avoid CFN error + }); + + // Save configuration for CDK8s to access it + new CfnOutput(this, CfnOutputKeys.Karpenter.DefaultInstanceProfile, { value: instanceProfile.ref }); + new CfnOutput(this, CfnOutputKeys.Karpenter.ClusterEndpoint, { value: this.cluster.clusterEndpoint }); + new CfnOutput(this, CfnOutputKeys.Karpenter.ServiceAccountRoleArn, { value: serviceAccount.role.roleArn }); + new CfnOutput(this, CfnOutputKeys.Karpenter.ServiceAccountName, { value: serviceAccount.serviceAccountName }); + // Use fluent bit to ship logs from eks into aws const fluentBitNs = this.cluster.addManifest('FluentBitNamespace', { apiVersion: 'v1', @@ -90,6 +199,7 @@ export class LinzEksCluster extends Stack { namespace: 'fluent-bit', }); fluentBitSa.node.addDependency(fluentBitNs); // Ensure the namespace created first + new CfnOutput(this, 'FluentBitServiceAccountRoleArn', { value: fluentBitSa.role.roleArn }); // Basic constructs for argo to be deployed into const argoNs = this.cluster.addManifest('ArgoNameSpace', { @@ -102,5 +212,6 @@ export class LinzEksCluster extends Stack { namespace: 'argo', }); argoRunnerSa.node.addDependency(argoNs); + new CfnOutput(this, 'ArgoRunnerServiceAccountRoleArn', { value: fluentBitSa.role.roleArn }); } } diff --git a/config/imports/karpenter.k8s.aws.ts b/config/imports/karpenter.k8s.aws.ts new file mode 100644 index 000000000..d42a378d3 --- /dev/null +++ b/config/imports/karpenter.k8s.aws.ts @@ -0,0 +1,461 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// generated by cdk8s +import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; +import { Construct } from 'constructs'; + +/** + * AWSNodeTemplate is the Schema for the AWSNodeTemplate API + * + * @schema AWSNodeTemplate + */ +export class AwsNodeTemplate extends ApiObject { + /** + * Returns the apiVersion and kind for "AWSNodeTemplate" + */ + public static readonly GVK: GroupVersionKind = { + apiVersion: 'karpenter.k8s.aws/v1alpha1', + kind: 'AWSNodeTemplate', + }; + + /** + * Renders a Kubernetes manifest for "AWSNodeTemplate". + * + * This can be used to inline resource manifests inside other objects (e.g. as templates). + * + * @param props initialization props + */ + public static manifest(props: AwsNodeTemplateProps = {}): any { + return { + ...AwsNodeTemplate.GVK, + ...toJson_AwsNodeTemplateProps(props), + }; + } + + /** + * Defines a "AWSNodeTemplate" API object + * @param scope the scope in which to define this object + * @param id a scope-local name for the object + * @param props initialization props + */ + public constructor(scope: Construct, id: string, props: AwsNodeTemplateProps = {}) { + super(scope, id, { + ...AwsNodeTemplate.GVK, + ...props, + }); + } + + /** + * Renders the object to Kubernetes JSON. + */ + public toJson(): any { + const resolved = super.toJson(); + + return { + ...AwsNodeTemplate.GVK, + ...toJson_AwsNodeTemplateProps(resolved), + }; + } +} + +/** + * AWSNodeTemplate is the Schema for the AWSNodeTemplate API + * + * @schema AWSNodeTemplate + */ +export interface AwsNodeTemplateProps { + /** + * @schema AWSNodeTemplate#metadata + */ + readonly metadata?: ApiObjectMetadata; + + /** + * AWSNodeTemplateSpec is the top level specification for the AWS Karpenter Provider. This will contain configuration necessary to launch instances in AWS. + * + * @schema AWSNodeTemplate#spec + */ + readonly spec?: AwsNodeTemplateSpec; +} + +/** + * Converts an object of type 'AwsNodeTemplateProps' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_AwsNodeTemplateProps(obj: AwsNodeTemplateProps | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + metadata: obj.metadata, + spec: toJson_AwsNodeTemplateSpec(obj.spec), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * AWSNodeTemplateSpec is the top level specification for the AWS Karpenter Provider. This will contain configuration necessary to launch instances in AWS. + * + * @schema AwsNodeTemplateSpec + */ +export interface AwsNodeTemplateSpec { + /** + * AMIFamily is the AMI family that instances use. + * + * @schema AwsNodeTemplateSpec#amiFamily + */ + readonly amiFamily?: string; + + /** + * AMISelector discovers AMIs to be used by Amazon EC2 tags. + * + * @schema AwsNodeTemplateSpec#amiSelector + */ + readonly amiSelector?: { [key: string]: string }; + + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + * + * @schema AwsNodeTemplateSpec#apiVersion + */ + readonly apiVersion?: string; + + /** + * BlockDeviceMappings to be applied to provisioned nodes. + * + * @schema AwsNodeTemplateSpec#blockDeviceMappings + */ + readonly blockDeviceMappings?: AwsNodeTemplateSpecBlockDeviceMappings[]; + + /** + * Context is a Reserved field in EC2 APIs https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateFleet.html + * + * @schema AwsNodeTemplateSpec#context + */ + readonly context?: string; + + /** + * DetailedMonitoring controls if detailed monitoring is enabled for instances that are launched + * + * @schema AwsNodeTemplateSpec#detailedMonitoring + */ + readonly detailedMonitoring?: boolean; + + /** + * InstanceProfile is the AWS identity that instances use. + * + * @schema AwsNodeTemplateSpec#instanceProfile + */ + readonly instanceProfile?: string; + + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + * + * @schema AwsNodeTemplateSpec#kind + */ + readonly kind?: string; + + /** + * LaunchTemplateName for the node. If not specified, a launch template will be generated. NOTE: This field is for specifying a custom launch template and is exposed in the Spec as `launchTemplate` for backwards compatibility. + * + * @schema AwsNodeTemplateSpec#launchTemplate + */ + readonly launchTemplate?: string; + + /** + * MetadataOptions for the generated launch template of provisioned nodes. + * This specifies the exposure of the Instance Metadata Service to provisioned EC2 nodes. For more information, see Instance Metadata and User Data (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) in the Amazon Elastic Compute Cloud User Guide. + * Refer to recommended, security best practices (https://aws.github.io/aws-eks-best-practices/security/docs/iam/#restrict-access-to-the-instance-profile-assigned-to-the-worker-node) for limiting exposure of Instance Metadata and User Data to pods. If omitted, defaults to httpEndpoint enabled, with httpProtocolIPv6 disabled, with httpPutResponseLimit of 2, and with httpTokens required. + * + * @schema AwsNodeTemplateSpec#metadataOptions + */ + readonly metadataOptions?: AwsNodeTemplateSpecMetadataOptions; + + /** + * SecurityGroups specify the names of the security groups. + * + * @schema AwsNodeTemplateSpec#securityGroupSelector + */ + readonly securityGroupSelector?: { [key: string]: string }; + + /** + * SubnetSelector discovers subnets by tags. A value of "" is a wildcard. + * + * @schema AwsNodeTemplateSpec#subnetSelector + */ + readonly subnetSelector?: { [key: string]: string }; + + /** + * Tags to be applied on ec2 resources like instances and launch templates. + * + * @schema AwsNodeTemplateSpec#tags + */ + readonly tags?: { [key: string]: string }; + + /** + * UserData to be applied to the provisioned nodes. It must be in the appropriate format based on the AMIFamily in use. Karpenter will merge certain fields into this UserData to ensure nodes are being provisioned with the correct configuration. + * + * @schema AwsNodeTemplateSpec#userData + */ + readonly userData?: string; +} + +/** + * Converts an object of type 'AwsNodeTemplateSpec' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_AwsNodeTemplateSpec(obj: AwsNodeTemplateSpec | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + amiFamily: obj.amiFamily, + amiSelector: + obj.amiSelector === undefined + ? undefined + : Object.entries(obj.amiSelector).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + apiVersion: obj.apiVersion, + blockDeviceMappings: obj.blockDeviceMappings?.map((y) => toJson_AwsNodeTemplateSpecBlockDeviceMappings(y)), + context: obj.context, + detailedMonitoring: obj.detailedMonitoring, + instanceProfile: obj.instanceProfile, + kind: obj.kind, + launchTemplate: obj.launchTemplate, + metadataOptions: toJson_AwsNodeTemplateSpecMetadataOptions(obj.metadataOptions), + securityGroupSelector: + obj.securityGroupSelector === undefined + ? undefined + : Object.entries(obj.securityGroupSelector).reduce( + (r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), + {}, + ), + subnetSelector: + obj.subnetSelector === undefined + ? undefined + : Object.entries(obj.subnetSelector).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + tags: + obj.tags === undefined + ? undefined + : Object.entries(obj.tags).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + userData: obj.userData, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * @schema AwsNodeTemplateSpecBlockDeviceMappings + */ +export interface AwsNodeTemplateSpecBlockDeviceMappings { + /** + * The device name (for example, /dev/sdh or xvdh). + * + * @schema AwsNodeTemplateSpecBlockDeviceMappings#deviceName + */ + readonly deviceName?: string; + + /** + * EBS contains parameters used to automatically set up EBS volumes when an instance is launched. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappings#ebs + */ + readonly ebs?: AwsNodeTemplateSpecBlockDeviceMappingsEbs; +} + +/** + * Converts an object of type 'AwsNodeTemplateSpecBlockDeviceMappings' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_AwsNodeTemplateSpecBlockDeviceMappings( + obj: AwsNodeTemplateSpecBlockDeviceMappings | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + deviceName: obj.deviceName, + ebs: toJson_AwsNodeTemplateSpecBlockDeviceMappingsEbs(obj.ebs), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * MetadataOptions for the generated launch template of provisioned nodes. + * This specifies the exposure of the Instance Metadata Service to provisioned EC2 nodes. For more information, see Instance Metadata and User Data (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) in the Amazon Elastic Compute Cloud User Guide. + * Refer to recommended, security best practices (https://aws.github.io/aws-eks-best-practices/security/docs/iam/#restrict-access-to-the-instance-profile-assigned-to-the-worker-node) for limiting exposure of Instance Metadata and User Data to pods. If omitted, defaults to httpEndpoint enabled, with httpProtocolIPv6 disabled, with httpPutResponseLimit of 2, and with httpTokens required. + * + * @schema AwsNodeTemplateSpecMetadataOptions + */ +export interface AwsNodeTemplateSpecMetadataOptions { + /** + * HTTPEndpoint enables or disables the HTTP metadata endpoint on provisioned nodes. If metadata options is non-nil, but this parameter is not specified, the default state is "enabled". + * If you specify a value of "disabled", instance metadata will not be accessible on the node. + * + * @schema AwsNodeTemplateSpecMetadataOptions#httpEndpoint + */ + readonly httpEndpoint?: string; + + /** + * HTTPProtocolIPv6 enables or disables the IPv6 endpoint for the instance metadata service on provisioned nodes. If metadata options is non-nil, but this parameter is not specified, the default state is "disabled". + * + * @schema AwsNodeTemplateSpecMetadataOptions#httpProtocolIPv6 + */ + readonly httpProtocolIPv6?: string; + + /** + * HTTPPutResponseHopLimit is the desired HTTP PUT response hop limit for instance metadata requests. The larger the number, the further instance metadata requests can travel. Possible values are integers from 1 to 64. If metadata options is non-nil, but this parameter is not specified, the default value is 1. + * + * @schema AwsNodeTemplateSpecMetadataOptions#httpPutResponseHopLimit + */ + readonly httpPutResponseHopLimit?: number; + + /** + * HTTPTokens determines the state of token usage for instance metadata requests. If metadata options is non-nil, but this parameter is not specified, the default state is "optional". + * If the state is optional, one can choose to retrieve instance metadata with or without a signed token header on the request. If one retrieves the IAM role credentials without a token, the version 1.0 role credentials are returned. If one retrieves the IAM role credentials using a valid signed token, the version 2.0 role credentials are returned. + * If the state is "required", one must send a signed token header with any instance metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available. + * + * @schema AwsNodeTemplateSpecMetadataOptions#httpTokens + */ + readonly httpTokens?: string; +} + +/** + * Converts an object of type 'AwsNodeTemplateSpecMetadataOptions' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_AwsNodeTemplateSpecMetadataOptions( + obj: AwsNodeTemplateSpecMetadataOptions | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + httpEndpoint: obj.httpEndpoint, + httpProtocolIPv6: obj.httpProtocolIPv6, + httpPutResponseHopLimit: obj.httpPutResponseHopLimit, + httpTokens: obj.httpTokens, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * EBS contains parameters used to automatically set up EBS volumes when an instance is launched. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs + */ +export interface AwsNodeTemplateSpecBlockDeviceMappingsEbs { + /** + * DeleteOnTermination indicates whether the EBS volume is deleted on instance termination. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#deleteOnTermination + */ + readonly deleteOnTermination?: boolean; + + /** + * Encrypted indicates whether the EBS volume is encrypted. Encrypted volumes can only be attached to instances that support Amazon EBS encryption. If you are creating a volume from a snapshot, you can't specify an encryption value. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#encrypted + */ + readonly encrypted?: boolean; + + /** + * IOPS is the number of I/O operations per second (IOPS). For gp3, io1, and io2 volumes, this represents the number of IOPS that are provisioned for the volume. For gp2 volumes, this represents the baseline performance of the volume and the rate at which the volume accumulates I/O credits for bursting. + * The following are the supported values for each volume type: + * * gp3: 3,000-16,000 IOPS + * * io1: 100-64,000 IOPS + * * io2: 100-64,000 IOPS + * For io1 and io2 volumes, we guarantee 64,000 IOPS only for Instances built on the Nitro System (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html#ec2-nitro-instances). Other instance families guarantee performance up to 32,000 IOPS. + * This parameter is supported for io1, io2, and gp3 volumes only. This parameter is not supported for gp2, st1, sc1, or standard volumes. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#iops + */ + readonly iops?: number; + + /** + * KMSKeyID (ARN) of the symmetric Key Management Service (KMS) CMK used for encryption. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#kmsKeyID + */ + readonly kmsKeyId?: string; + + /** + * SnapshotID is the ID of an EBS snapshot + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#snapshotID + */ + readonly snapshotId?: string; + + /** + * Throughput to provision for a gp3 volume, with a maximum of 1,000 MiB/s. Valid Range: Minimum value of 125. Maximum value of 1000. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#throughput + */ + readonly throughput?: number; + + /** + * VolumeSize in GiBs. You must specify either a snapshot ID or a volume size. The following are the supported volumes sizes for each volume type: + * * gp2 and gp3: 1-16,384 + * * io1 and io2: 4-16,384 + * * st1 and sc1: 125-16,384 + * * standard: 1-1,024 + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#volumeSize + */ + readonly volumeSize?: string; // FIXME this should be a string not AwsNodeTemplateSpecBlockDeviceMappingsEbs + + /** + * VolumeType of the block device. For more information, see Amazon EBS volume types (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html) in the Amazon Elastic Compute Cloud User Guide. + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbs#volumeType + */ + readonly volumeType?: string; +} + +/** + * Converts an object of type 'AwsNodeTemplateSpecBlockDeviceMappingsEbs' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_AwsNodeTemplateSpecBlockDeviceMappingsEbs( + obj: AwsNodeTemplateSpecBlockDeviceMappingsEbs | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + deleteOnTermination: obj.deleteOnTermination, + encrypted: obj.encrypted, + iops: obj.iops, + kmsKeyID: obj.kmsKeyId, + snapshotID: obj.snapshotId, + throughput: obj.throughput, + volumeSize: obj.volumeSize?.value, + volumeType: obj.volumeType, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * VolumeSize in GiBs. You must specify either a snapshot ID or a volume size. The following are the supported volumes sizes for each volume type: + * * gp2 and gp3: 1-16,384 + * * io1 and io2: 4-16,384 + * * st1 and sc1: 125-16,384 + * * standard: 1-1,024 + * + * @schema AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize + */ +export class AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize { + public static fromNumber(value: number): AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize { + return new AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize(value); + } + public static fromString(value: string): AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize { + return new AwsNodeTemplateSpecBlockDeviceMappingsEbsVolumeSize(value); + } + private constructor(public readonly value: number | string) {} +} diff --git a/config/imports/karpenter.sh.ts b/config/imports/karpenter.sh.ts new file mode 100644 index 000000000..d2b10461f --- /dev/null +++ b/config/imports/karpenter.sh.ts @@ -0,0 +1,690 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// generated by cdk8s +import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; +import { Construct } from 'constructs'; + +/** + * Provisioner is the Schema for the Provisioners API + * + * @schema Provisioner + */ +export class Provisioner extends ApiObject { + /** + * Returns the apiVersion and kind for "Provisioner" + */ + public static readonly GVK: GroupVersionKind = { + apiVersion: 'karpenter.sh/v1alpha5', + kind: 'Provisioner', + }; + + /** + * Renders a Kubernetes manifest for "Provisioner". + * + * This can be used to inline resource manifests inside other objects (e.g. as templates). + * + * @param props initialization props + */ + public static manifest(props: ProvisionerProps = {}): any { + return { + ...Provisioner.GVK, + ...toJson_ProvisionerProps(props), + }; + } + + /** + * Defines a "Provisioner" API object + * @param scope the scope in which to define this object + * @param id a scope-local name for the object + * @param props initialization props + */ + public constructor(scope: Construct, id: string, props: ProvisionerProps = {}) { + super(scope, id, { + ...Provisioner.GVK, + ...props, + }); + } + + /** + * Renders the object to Kubernetes JSON. + */ + public toJson(): any { + const resolved = super.toJson(); + + return { + ...Provisioner.GVK, + ...toJson_ProvisionerProps(resolved), + }; + } +} + +/** + * Provisioner is the Schema for the Provisioners API + * + * @schema Provisioner + */ +export interface ProvisionerProps { + /** + * @schema Provisioner#metadata + */ + readonly metadata?: ApiObjectMetadata; + + /** + * ProvisionerSpec is the top level provisioner specification. Provisioners launch nodes in response to pods that are unschedulable. A single provisioner is capable of managing a diverse set of nodes. Node properties are determined from a combination of provisioner and pod scheduling constraints. + * + * @schema Provisioner#spec + */ + readonly spec?: ProvisionerSpec; +} + +/** + * Converts an object of type 'ProvisionerProps' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerProps(obj: ProvisionerProps | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + metadata: obj.metadata, + spec: toJson_ProvisionerSpec(obj.spec), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * ProvisionerSpec is the top level provisioner specification. Provisioners launch nodes in response to pods that are unschedulable. A single provisioner is capable of managing a diverse set of nodes. Node properties are determined from a combination of provisioner and pod scheduling constraints. + * + * @schema ProvisionerSpec + */ +export interface ProvisionerSpec { + /** + * Annotations are applied to every node. + * + * @schema ProvisionerSpec#annotations + */ + readonly annotations?: { [key: string]: string }; + + /** + * Consolidation are the consolidation parameters + * + * @schema ProvisionerSpec#consolidation + */ + readonly consolidation?: ProvisionerSpecConsolidation; + + /** + * KubeletConfiguration are options passed to the kubelet when provisioning nodes + * + * @schema ProvisionerSpec#kubeletConfiguration + */ + readonly kubeletConfiguration?: ProvisionerSpecKubeletConfiguration; + + /** + * Labels are layered with Requirements and applied to every node. + * + * @schema ProvisionerSpec#labels + */ + readonly labels?: { [key: string]: string }; + + /** + * Limits define a set of bounds for provisioning capacity. + * + * @schema ProvisionerSpec#limits + */ + readonly limits?: ProvisionerSpecLimits; + + /** + * Provider contains fields specific to your cloudprovider. + * + * @schema ProvisionerSpec#provider + */ + readonly provider?: any; + + /** + * ProviderRef is a reference to a dedicated CRD for the chosen provider, that holds additional configuration options + * + * @schema ProvisionerSpec#providerRef + */ + readonly providerRef?: ProvisionerSpecProviderRef; + + /** + * Requirements are layered with Labels and applied to every node. + * + * @schema ProvisionerSpec#requirements + */ + readonly requirements?: ProvisionerSpecRequirements[]; + + /** + * StartupTaints are taints that are applied to nodes upon startup which are expected to be removed automatically within a short period of time, typically by a DaemonSet that tolerates the taint. These are commonly used by daemonsets to allow initialization and enforce startup ordering. StartupTaints are ignored for provisioning purposes in that pods are not required to tolerate a StartupTaint in order to have nodes provisioned for them. + * + * @schema ProvisionerSpec#startupTaints + */ + readonly startupTaints?: ProvisionerSpecStartupTaints[]; + + /** + * Taints will be applied to every node launched by the Provisioner. If specified, the provisioner will not provision nodes for pods that do not have matching tolerations. Additional taints will be created that match pod tolerations on a per-node basis. + * + * @schema ProvisionerSpec#taints + */ + readonly taints?: ProvisionerSpecTaints[]; + + /** + * TTLSecondsAfterEmpty is the number of seconds the controller will wait before attempting to delete a node, measured from when the node is detected to be empty. A Node is considered to be empty when it does not have pods scheduled to it, excluding daemonsets. + * Termination due to no utilization is disabled if this field is not set. + * + * @schema ProvisionerSpec#ttlSecondsAfterEmpty + */ + readonly ttlSecondsAfterEmpty?: number; + + /** + * TTLSecondsUntilExpired is the number of seconds the controller will wait before terminating a node, measured from when the node is created. This is useful to implement features like eventually consistent node upgrade, memory leak protection, and disruption testing. + * Termination due to expiration is disabled if this field is not set. + * + * @schema ProvisionerSpec#ttlSecondsUntilExpired + */ + readonly ttlSecondsUntilExpired?: number; + + /** + * Weight is the priority given to the provisioner during scheduling. A higher numerical weight indicates that this provisioner will be ordered ahead of other provisioners with lower weights. A provisioner with no weight will be treated as if it is a provisioner with a weight of 0. + * + * @schema ProvisionerSpec#weight + */ + readonly weight?: number; +} + +/** + * Converts an object of type 'ProvisionerSpec' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpec(obj: ProvisionerSpec | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + annotations: + obj.annotations === undefined + ? undefined + : Object.entries(obj.annotations).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + consolidation: toJson_ProvisionerSpecConsolidation(obj.consolidation), + kubeletConfiguration: toJson_ProvisionerSpecKubeletConfiguration(obj.kubeletConfiguration), + labels: + obj.labels === undefined + ? undefined + : Object.entries(obj.labels).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + limits: toJson_ProvisionerSpecLimits(obj.limits), + provider: obj.provider, + providerRef: toJson_ProvisionerSpecProviderRef(obj.providerRef), + requirements: obj.requirements?.map((y) => toJson_ProvisionerSpecRequirements(y)), + startupTaints: obj.startupTaints?.map((y) => toJson_ProvisionerSpecStartupTaints(y)), + taints: obj.taints?.map((y) => toJson_ProvisionerSpecTaints(y)), + ttlSecondsAfterEmpty: obj.ttlSecondsAfterEmpty, + ttlSecondsUntilExpired: obj.ttlSecondsUntilExpired, + weight: obj.weight, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * Consolidation are the consolidation parameters + * + * @schema ProvisionerSpecConsolidation + */ +export interface ProvisionerSpecConsolidation { + /** + * Enabled enables consolidation if it has been set + * + * @schema ProvisionerSpecConsolidation#enabled + */ + readonly enabled?: boolean; +} + +/** + * Converts an object of type 'ProvisionerSpecConsolidation' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecConsolidation( + obj: ProvisionerSpecConsolidation | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + enabled: obj.enabled, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * KubeletConfiguration are options passed to the kubelet when provisioning nodes + * + * @schema ProvisionerSpecKubeletConfiguration + */ +export interface ProvisionerSpecKubeletConfiguration { + /** + * clusterDNS is a list of IP addresses for the cluster DNS server. Note that not all providers may use all addresses. + * + * @schema ProvisionerSpecKubeletConfiguration#clusterDNS + */ + readonly clusterDns?: string[]; + + /** + * ContainerRuntime is the container runtime to be used with your worker nodes. + * + * @schema ProvisionerSpecKubeletConfiguration#containerRuntime + */ + readonly containerRuntime?: string; + + /** + * CPUCFSQuota enables CPU CFS quota enforcement for containers that specify CPU limits. + * + * @schema ProvisionerSpecKubeletConfiguration#cpuCFSQuota + */ + readonly cpuCfsQuota?: boolean; + + /** + * EvictionHard is the map of signal names to quantities that define hard eviction thresholds + * + * @schema ProvisionerSpecKubeletConfiguration#evictionHard + */ + readonly evictionHard?: { [key: string]: string }; + + /** + * EvictionMaxPodGracePeriod is the maximum allowed grace period (in seconds) to use when terminating pods in response to soft eviction thresholds being met. + * + * @schema ProvisionerSpecKubeletConfiguration#evictionMaxPodGracePeriod + */ + readonly evictionMaxPodGracePeriod?: number; + + /** + * EvictionSoft is the map of signal names to quantities that define soft eviction thresholds + * + * @schema ProvisionerSpecKubeletConfiguration#evictionSoft + */ + readonly evictionSoft?: { [key: string]: string }; + + /** + * EvictionSoftGracePeriod is the map of signal names to quantities that define grace periods for each eviction signal + * + * @schema ProvisionerSpecKubeletConfiguration#evictionSoftGracePeriod + */ + readonly evictionSoftGracePeriod?: { [key: string]: string }; + + /** + * ImageGCHighThresholdPercent is the percent of disk usage after which image garbage collection is always run. The percent is calculated by dividing this field value by 100, so this field must be between 0 and 100, inclusive. When specified, the value must be greater than ImageGCLowThresholdPercent. + * + * @schema ProvisionerSpecKubeletConfiguration#imageGCHighThresholdPercent + */ + readonly imageGcHighThresholdPercent?: number; + + /** + * ImageGCLowThresholdPercent is the percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. The percent is calculated by dividing this field value by 100, so the field value must be between 0 and 100, inclusive. When specified, the value must be less than imageGCHighThresholdPercent + * + * @schema ProvisionerSpecKubeletConfiguration#imageGCLowThresholdPercent + */ + readonly imageGcLowThresholdPercent?: number; + + /** + * KubeReserved contains resources reserved for Kubernetes system components. + * + * @schema ProvisionerSpecKubeletConfiguration#kubeReserved + */ + readonly kubeReserved?: { [key: string]: ProvisionerSpecKubeletConfigurationKubeReserved }; + + /** + * MaxPods is an override for the maximum number of pods that can run on a worker node instance. + * + * @schema ProvisionerSpecKubeletConfiguration#maxPods + */ + readonly maxPods?: number; + + /** + * PodsPerCore is an override for the number of pods that can run on a worker node instance based on the number of cpu cores. This value cannot exceed MaxPods, so, if MaxPods is a lower value, that value will be used. + * + * @schema ProvisionerSpecKubeletConfiguration#podsPerCore + */ + readonly podsPerCore?: number; + + /** + * SystemReserved contains resources reserved for OS system daemons and kernel memory. + * + * @schema ProvisionerSpecKubeletConfiguration#systemReserved + */ + readonly systemReserved?: { [key: string]: ProvisionerSpecKubeletConfigurationSystemReserved }; +} + +/** + * Converts an object of type 'ProvisionerSpecKubeletConfiguration' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecKubeletConfiguration( + obj: ProvisionerSpecKubeletConfiguration | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + clusterDNS: obj.clusterDns?.map((y) => y), + containerRuntime: obj.containerRuntime, + cpuCFSQuota: obj.cpuCfsQuota, + evictionHard: + obj.evictionHard === undefined + ? undefined + : Object.entries(obj.evictionHard).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + evictionMaxPodGracePeriod: obj.evictionMaxPodGracePeriod, + evictionSoft: + obj.evictionSoft === undefined + ? undefined + : Object.entries(obj.evictionSoft).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}), + evictionSoftGracePeriod: + obj.evictionSoftGracePeriod === undefined + ? undefined + : Object.entries(obj.evictionSoftGracePeriod).reduce( + (r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), + {}, + ), + imageGCHighThresholdPercent: obj.imageGcHighThresholdPercent, + imageGCLowThresholdPercent: obj.imageGcLowThresholdPercent, + kubeReserved: + obj.kubeReserved === undefined + ? undefined + : Object.entries(obj.kubeReserved).reduce( + (r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1]?.value }), + {}, + ), + maxPods: obj.maxPods, + podsPerCore: obj.podsPerCore, + systemReserved: + obj.systemReserved === undefined + ? undefined + : Object.entries(obj.systemReserved).reduce( + (r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1]?.value }), + {}, + ), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * Limits define a set of bounds for provisioning capacity. + * + * @schema ProvisionerSpecLimits + */ +export interface ProvisionerSpecLimits { + /** + * Resources contains all the allocatable resources that Karpenter supports for limiting. + * + * @schema ProvisionerSpecLimits#resources + */ + readonly resources?: { [key: string]: ProvisionerSpecLimitsResources }; +} + +/** + * Converts an object of type 'ProvisionerSpecLimits' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecLimits(obj: ProvisionerSpecLimits | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + resources: + obj.resources === undefined + ? undefined + : Object.entries(obj.resources).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1]?.value }), {}), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * ProviderRef is a reference to a dedicated CRD for the chosen provider, that holds additional configuration options + * + * @schema ProvisionerSpecProviderRef + */ +export interface ProvisionerSpecProviderRef { + /** + * API version of the referent + * + * @schema ProvisionerSpecProviderRef#apiVersion + */ + readonly apiVersion?: string; + + /** + * Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + * + * @schema ProvisionerSpecProviderRef#kind + */ + readonly kind?: string; + + /** + * Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + * + * @schema ProvisionerSpecProviderRef#name + */ + readonly name: string; +} + +/** + * Converts an object of type 'ProvisionerSpecProviderRef' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecProviderRef( + obj: ProvisionerSpecProviderRef | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + apiVersion: obj.apiVersion, + kind: obj.kind, + name: obj.name, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + * + * @schema ProvisionerSpecRequirements + */ +export interface ProvisionerSpecRequirements { + /** + * The label key that the selector applies to. + * + * @schema ProvisionerSpecRequirements#key + */ + readonly key: string; + + /** + * Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + * + * @schema ProvisionerSpecRequirements#operator + */ + readonly operator: string; + + /** + * An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + * + * @schema ProvisionerSpecRequirements#values + */ + readonly values?: string[]; +} + +/** + * Converts an object of type 'ProvisionerSpecRequirements' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecRequirements( + obj: ProvisionerSpecRequirements | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + key: obj.key, + operator: obj.operator, + values: obj.values?.map((y) => y), + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * The node this Taint is attached to has the "effect" on any pod that does not tolerate the Taint. + * + * @schema ProvisionerSpecStartupTaints + */ +export interface ProvisionerSpecStartupTaints { + /** + * Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute. + * + * @schema ProvisionerSpecStartupTaints#effect + */ + readonly effect: string; + + /** + * Required. The taint key to be applied to a node. + * + * @schema ProvisionerSpecStartupTaints#key + */ + readonly key: string; + + /** + * TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints. + * + * @schema ProvisionerSpecStartupTaints#timeAdded + */ + readonly timeAdded?: Date; + + /** + * The taint value corresponding to the taint key. + * + * @schema ProvisionerSpecStartupTaints#value + */ + readonly value?: string; +} + +/** + * Converts an object of type 'ProvisionerSpecStartupTaints' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecStartupTaints( + obj: ProvisionerSpecStartupTaints | undefined, +): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + effect: obj.effect, + key: obj.key, + timeAdded: obj.timeAdded?.toISOString(), + value: obj.value, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * The node this Taint is attached to has the "effect" on any pod that does not tolerate the Taint. + * + * @schema ProvisionerSpecTaints + */ +export interface ProvisionerSpecTaints { + /** + * Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute. + * + * @schema ProvisionerSpecTaints#effect + */ + readonly effect: string; + + /** + * Required. The taint key to be applied to a node. + * + * @schema ProvisionerSpecTaints#key + */ + readonly key: string; + + /** + * TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints. + * + * @schema ProvisionerSpecTaints#timeAdded + */ + readonly timeAdded?: Date; + + /** + * The taint value corresponding to the taint key. + * + * @schema ProvisionerSpecTaints#value + */ + readonly value?: string; +} + +/** + * Converts an object of type 'ProvisionerSpecTaints' to JSON representation. + */ +/* eslint-disable max-len, quote-props */ +export function toJson_ProvisionerSpecTaints(obj: ProvisionerSpecTaints | undefined): Record | undefined { + if (obj === undefined) { + return undefined; + } + const result = { + effect: obj.effect, + key: obj.key, + timeAdded: obj.timeAdded?.toISOString(), + value: obj.value, + }; + // filter undefined values + return Object.entries(result).reduce((r, i) => (i[1] === undefined ? r : { ...r, [i[0]]: i[1] }), {}); +} +/* eslint-enable max-len, quote-props */ + +/** + * @schema ProvisionerSpecKubeletConfigurationKubeReserved + */ +export class ProvisionerSpecKubeletConfigurationKubeReserved { + public static fromNumber(value: number): ProvisionerSpecKubeletConfigurationKubeReserved { + return new ProvisionerSpecKubeletConfigurationKubeReserved(value); + } + public static fromString(value: string): ProvisionerSpecKubeletConfigurationKubeReserved { + return new ProvisionerSpecKubeletConfigurationKubeReserved(value); + } + private constructor(public readonly value: number | string) {} +} + +/** + * @schema ProvisionerSpecKubeletConfigurationSystemReserved + */ +export class ProvisionerSpecKubeletConfigurationSystemReserved { + public static fromNumber(value: number): ProvisionerSpecKubeletConfigurationSystemReserved { + return new ProvisionerSpecKubeletConfigurationSystemReserved(value); + } + public static fromString(value: string): ProvisionerSpecKubeletConfigurationSystemReserved { + return new ProvisionerSpecKubeletConfigurationSystemReserved(value); + } + private constructor(public readonly value: number | string) {} +} + +/** + * @schema ProvisionerSpecLimitsResources + */ +export class ProvisionerSpecLimitsResources { + public static fromNumber(value: number): ProvisionerSpecLimitsResources { + return new ProvisionerSpecLimitsResources(value); + } + public static fromString(value: string): ProvisionerSpecLimitsResources { + return new ProvisionerSpecLimitsResources(value); + } + private constructor(public readonly value: number | string) {} +} diff --git a/config/util/cloud.formation.ts b/config/util/cloud.formation.ts new file mode 100644 index 000000000..618a3f136 --- /dev/null +++ b/config/util/cloud.formation.ts @@ -0,0 +1,23 @@ +import { CloudFormation } from '@aws-sdk/client-cloudformation'; + +export async function getCfnOutputs(stackName: string): Promise> { + const cfn = new CloudFormation(); + const searchStacks = await cfn.describeStacks({ StackName: stackName }); + cfn.listExports; + const outputs: Record = {}; + const stacks = (searchStacks && searchStacks.Stacks) || []; + const stack = stacks.find((s) => s.StackName === stackName); + + if (!stack) { + throw new Error(`Unable to find stack "${stackName}"`); + } + if (!stack.Outputs) { + throw new Error(`There is no output for stack "${stackName}"`); + } + stack.Outputs.forEach(({ OutputKey, OutputValue }) => { + if (OutputKey && OutputValue) { + outputs[OutputKey] = OutputValue; + } + }); + return outputs; +} diff --git a/config/util/eks.cluster.ts b/config/util/eks.cluster.ts new file mode 100644 index 000000000..4ef5d6c1e --- /dev/null +++ b/config/util/eks.cluster.ts @@ -0,0 +1,9 @@ +import { EKS } from '@aws-sdk/client-eks'; + +export async function getClusterFromName(clusterName: string): Promise { + const eks = new EKS(); + const eksCluster = await eks.describeCluster({ name: clusterName }); + if (eksCluster.cluster) { + console.log(eksCluster.cluster); + } +} diff --git a/config/util/labels.ts b/config/util/labels.ts index 0f02012be..22f654096 100644 --- a/config/util/labels.ts +++ b/config/util/labels.ts @@ -1,4 +1,4 @@ -import { getGitBuildInfo } from './build'; +import { getGitBuildInfo } from './build.js'; /** * Generate a collection of standard labels for all components diff --git a/docs/dns.configuration.md b/docs/dns.configuration.md new file mode 100644 index 000000000..04a1229c6 --- /dev/null +++ b/docs/dns.configuration.md @@ -0,0 +1,81 @@ +# Debugging DNS issues + +Because our default cluster is setup as ipv6/ipv4 it can cause a number of connection issues, below are some tips for trying to figure out where the connections are failing. + +## Debugging tool installs + +To add some debugging tools to your container + +Start a shell on the container + +```bash +k exec -n :namespace -it :podName -- /bin/bash +``` + +Install basic dns utils `dig` `ping` `wget` and `curl` + +```bash +apt install dnsutils iptools-ping wget curl +``` + +### Name resolution + +Check that the dns can be resolved + +```bash +dig google.com # Lookups A (IPv4) record for google.com +dig google.com AAAA # lookup AAAA (IPv6) records +``` + +Check that a ipv4 connection can be made + +```bash +ping google.com # should resolve ipv4 +``` + +### Cluster DNS resolution + +Check DNS can resolve other services + +K8s host names follow a common pattern + +``` +:service.:namespace.svc.cluster.local +``` + +Because this cluster is ipv6 `AAAA` records should resolve to a ipv6 record and `A` should not exist + +```bash +dig AAAA kube-dns.kube-system.svc.cluster.local +short +dig A kube-dns.kube-system.svc.cluster.local +short +``` + +### AWS testing + +Installing the AWS Cli can cause a huge amount of dependencies to be installed, sometimes it is easier to just use `s5cmd`. + +Download the package for your arch (arm64 vs amd64) https://github.com/peak/s5cmd/releases + +Using a [public imagery bucket](https://github.com/linz/imagery) + +```bash +s5cmd --no-sign-request ls s3://nz-imagery/ +s5cmd --no-sign-request cat s3://nz-imagery/catalog.json +``` + +### Script testing + +Depending on the container you may have access to scripting languages. + +#### NodeJS + +file: index.mjs + +```javascript +fetch('https://google.com').then((c) => console.log(c)); +``` + +```bash +node --version +node index.mjs +``` diff --git a/package-lock.json b/package-lock.json index cd000d8e6..4714a41d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "devDependencies": { "@aws-cdk/lambda-layer-kubectl-v27": "^2.0.0", + "@aws-sdk/client-cloudformation": "^3.429.0", + "@aws-sdk/client-eks": "^3.429.0", "@linzjs/style": "^5.0.0", "aws-cdk": "2.93.x", "aws-cdk-lib": "2.93.x", @@ -59,6 +61,648 @@ "constructs": "^10.0.5" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dev": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudformation": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudformation/-/client-cloudformation-3.431.0.tgz", + "integrity": "sha512-ZBKyaro07W8ofkzmPg51LcgICeVu0nEEE9t91OxOIEnV3OFD7A2j/HWfPLL0KJ2zXXHGZn8oOJ4CiTNidV9bqQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.431.0", + "@aws-sdk/credential-provider-node": "3.431.0", + "@aws-sdk/middleware-host-header": "3.431.0", + "@aws-sdk/middleware-logger": "3.428.0", + "@aws-sdk/middleware-recursion-detection": "3.428.0", + "@aws-sdk/middleware-signing": "3.428.0", + "@aws-sdk/middleware-user-agent": "3.428.0", + "@aws-sdk/region-config-resolver": "3.430.0", + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@aws-sdk/util-user-agent-browser": "3.428.0", + "@aws-sdk/util-user-agent-node": "3.430.0", + "@smithy/config-resolver": "^2.0.15", + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/hash-node": "^2.0.11", + "@smithy/invalid-dependency": "^2.0.11", + "@smithy/middleware-content-length": "^2.0.13", + "@smithy/middleware-endpoint": "^2.1.2", + "@smithy/middleware-retry": "^2.0.17", + "@smithy/middleware-serde": "^2.0.11", + "@smithy/middleware-stack": "^2.0.5", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/protocol-http": "^3.0.7", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.15", + "@smithy/util-defaults-mode-node": "^2.0.20", + "@smithy/util-retry": "^2.0.4", + "@smithy/util-utf8": "^2.0.0", + "@smithy/util-waiter": "^2.0.11", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-eks": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-eks/-/client-eks-3.431.0.tgz", + "integrity": "sha512-ZkEUx/foqi3GYFRspnQHAuh5EImDIAqgPwtuRTIoBf41fePsZ6fQXif6eaLObx6DcwRvMirRi7pzwkUxIYR+MA==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.431.0", + "@aws-sdk/credential-provider-node": "3.431.0", + "@aws-sdk/middleware-host-header": "3.431.0", + "@aws-sdk/middleware-logger": "3.428.0", + "@aws-sdk/middleware-recursion-detection": "3.428.0", + "@aws-sdk/middleware-signing": "3.428.0", + "@aws-sdk/middleware-user-agent": "3.428.0", + "@aws-sdk/region-config-resolver": "3.430.0", + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@aws-sdk/util-user-agent-browser": "3.428.0", + "@aws-sdk/util-user-agent-node": "3.430.0", + "@smithy/config-resolver": "^2.0.15", + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/hash-node": "^2.0.11", + "@smithy/invalid-dependency": "^2.0.11", + "@smithy/middleware-content-length": "^2.0.13", + "@smithy/middleware-endpoint": "^2.1.2", + "@smithy/middleware-retry": "^2.0.17", + "@smithy/middleware-serde": "^2.0.11", + "@smithy/middleware-stack": "^2.0.5", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/protocol-http": "^3.0.7", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.15", + "@smithy/util-defaults-mode-node": "^2.0.20", + "@smithy/util-retry": "^2.0.4", + "@smithy/util-utf8": "^2.0.0", + "@smithy/util-waiter": "^2.0.11", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.431.0.tgz", + "integrity": "sha512-iK8RxdBHFj1HtWpdTVfFdljZHXLWFv62SuIdkDswGE7L0zNbZIqBDGfEBnbagiQuxkz5D2YtnasydC5R3BcwVw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.431.0", + "@aws-sdk/middleware-logger": "3.428.0", + "@aws-sdk/middleware-recursion-detection": "3.428.0", + "@aws-sdk/middleware-user-agent": "3.428.0", + "@aws-sdk/region-config-resolver": "3.430.0", + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@aws-sdk/util-user-agent-browser": "3.428.0", + "@aws-sdk/util-user-agent-node": "3.430.0", + "@smithy/config-resolver": "^2.0.15", + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/hash-node": "^2.0.11", + "@smithy/invalid-dependency": "^2.0.11", + "@smithy/middleware-content-length": "^2.0.13", + "@smithy/middleware-endpoint": "^2.1.2", + "@smithy/middleware-retry": "^2.0.17", + "@smithy/middleware-serde": "^2.0.11", + "@smithy/middleware-stack": "^2.0.5", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/protocol-http": "^3.0.7", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.15", + "@smithy/util-defaults-mode-node": "^2.0.20", + "@smithy/util-retry": "^2.0.4", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.431.0.tgz", + "integrity": "sha512-IM/Fg3H1WuM9fnVriEoM6+sZ9LNUExxklxAnHwjLnprPRTDGbUXUfYjSry52LaQsZffP3RgWP11CYyjCYC8CfQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.431.0", + "@aws-sdk/middleware-host-header": "3.431.0", + "@aws-sdk/middleware-logger": "3.428.0", + "@aws-sdk/middleware-recursion-detection": "3.428.0", + "@aws-sdk/middleware-sdk-sts": "3.428.0", + "@aws-sdk/middleware-signing": "3.428.0", + "@aws-sdk/middleware-user-agent": "3.428.0", + "@aws-sdk/region-config-resolver": "3.430.0", + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@aws-sdk/util-user-agent-browser": "3.428.0", + "@aws-sdk/util-user-agent-node": "3.430.0", + "@smithy/config-resolver": "^2.0.15", + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/hash-node": "^2.0.11", + "@smithy/invalid-dependency": "^2.0.11", + "@smithy/middleware-content-length": "^2.0.13", + "@smithy/middleware-endpoint": "^2.1.2", + "@smithy/middleware-retry": "^2.0.17", + "@smithy/middleware-serde": "^2.0.11", + "@smithy/middleware-stack": "^2.0.5", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/protocol-http": "^3.0.7", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.15", + "@smithy/util-defaults-mode-node": "^2.0.20", + "@smithy/util-retry": "^2.0.4", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.428.0.tgz", + "integrity": "sha512-e6fbY174Idzw0r5ZMT1qkDh+dpOp1DX3ickhr7J6ipo3cUGLI45Y5lnR9nYXWfB5o/wiNv4zXgN+Y3ORJJHzyA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.431.0.tgz", + "integrity": "sha512-SILMZuscwxeqB4kuZjWiu24wfvmvN3Tx7/j5n0t0Ob+cdpweK0IqkBQ/QkTbTiG0M1l8trMtMkrTb5510fupcQ==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.428.0", + "@aws-sdk/credential-provider-process": "3.428.0", + "@aws-sdk/credential-provider-sso": "3.431.0", + "@aws-sdk/credential-provider-web-identity": "3.428.0", + "@aws-sdk/types": "3.428.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.431.0.tgz", + "integrity": "sha512-jj2gm92nfsFw5e48+7OCYM5PfiW3pd9FvhEoBfvKANwM6ztXzmNpQcz3iWsGVfzd+MUooVBoO2exhH9M8t+VDg==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.428.0", + "@aws-sdk/credential-provider-ini": "3.431.0", + "@aws-sdk/credential-provider-process": "3.428.0", + "@aws-sdk/credential-provider-sso": "3.431.0", + "@aws-sdk/credential-provider-web-identity": "3.428.0", + "@aws-sdk/types": "3.428.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.428.0.tgz", + "integrity": "sha512-UG2S2/4Wrskbkbgt9fBlnzwQ2hfTXvLJwUgGOluSOf6+mGCcoDku4zzc9EQdk1MwN5Us+ziyMrIMNY5sbdLg6g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.431.0.tgz", + "integrity": "sha512-fh/yWKJtgEpxfuzd/KTVPQz0FjykbiPnU0OLm1wKgNZAyKTE9EyNvWR6P57TWv/sU8faa5uLaxdD0TBPxWReDA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.431.0", + "@aws-sdk/token-providers": "3.431.0", + "@aws-sdk/types": "3.428.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.428.0.tgz", + "integrity": "sha512-ueuUPPlrJFvtDUVTGnClUGt1wxCbEiKArknah/w9cfcc/c1HtFd/M7x/z2Sm0gSItR45sVcK54qjzmhm29DMzg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.431.0.tgz", + "integrity": "sha512-j+OBsCDDRXlMEQ4GCtTxVaMwxIHNKiwbDIZVyB6CDor8AFflKxWbO3cPSpUuGKlUN9OEexMR+XgwsjmaI6AGwg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/protocol-http": "^3.0.7", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.428.0.tgz", + "integrity": "sha512-1P0V0quL9u2amdNOn6yYT7/ToQUmkLJqCKHPxsRyDB829vBThWndvvH5MkoItj/VgE1zWqMtrzN3xtzD7zx6Qg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.428.0.tgz", + "integrity": "sha512-xC0OMduCByyRdiQz324RXy4kunnCG4LUJCfvdoegM33Elp9ex0D3fcfO1mUgV8qiLwSennIsSRVXHuhNxE2HZA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/protocol-http": "^3.0.7", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.428.0.tgz", + "integrity": "sha512-Uutl2niYXTnNP8v84v6umWDHD5no7d5/OqkZE1DsmeKR/dje90J5unJWf7MOsqvYm0JGDEWF4lk9xGVyqsw+Aw==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.428.0", + "@aws-sdk/types": "3.428.0", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.428.0.tgz", + "integrity": "sha512-oMSerTPwtsQAR7fIU/G0b0BA30wF+MC4gZSrJjbypF8MK8nPC2yMfKLR8+QavGOGEW7rUMQ0uklThMTTwQEXNQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.7", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.3.5", + "@smithy/util-middleware": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.428.0.tgz", + "integrity": "sha512-+GAhObeHRick2D5jr3YkPckjcggt5v6uUVtEUQW2AdD65cE5PjIvmksv6FuM/mME/9nNA+wufQnHbLI8teLeaw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@smithy/protocol-http": "^3.0.7", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.430.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.430.0.tgz", + "integrity": "sha512-9lqgtkcd4dqsQ2yN6V/i06blyDh4yLmS+fAS7LwEZih/NZZ2cBIR+5kb9c236auvTcuMcL1zFxVRloWwesYZjA==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.2", + "@smithy/types": "^2.3.5", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.431.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.431.0.tgz", + "integrity": "sha512-0ksZogF3Gy2i+yBb7T2g2e7QXzwZeQHmf09ihR1cwXwg7UIjsap6P3gPtC085bDkOD9iY8OdpL0Esp06N6xmCg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.431.0", + "@aws-sdk/middleware-logger": "3.428.0", + "@aws-sdk/middleware-recursion-detection": "3.428.0", + "@aws-sdk/middleware-user-agent": "3.428.0", + "@aws-sdk/types": "3.428.0", + "@aws-sdk/util-endpoints": "3.428.0", + "@aws-sdk/util-user-agent-browser": "3.428.0", + "@aws-sdk/util-user-agent-node": "3.430.0", + "@smithy/config-resolver": "^2.0.15", + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/hash-node": "^2.0.11", + "@smithy/invalid-dependency": "^2.0.11", + "@smithy/middleware-content-length": "^2.0.13", + "@smithy/middleware-endpoint": "^2.1.2", + "@smithy/middleware-retry": "^2.0.17", + "@smithy/middleware-serde": "^2.0.11", + "@smithy/middleware-stack": "^2.0.5", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.7", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.15", + "@smithy/util-defaults-mode-node": "^2.0.20", + "@smithy/util-retry": "^2.0.4", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.428.0.tgz", + "integrity": "sha512-4T0Ps2spjg3qbWE6ZK13Vd3FnzpfliaiotqjxUK5YhjDrKXeT36HJp46JhDupElQuHtTkpdiJOSYk2lvY2H4IA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.428.0.tgz", + "integrity": "sha512-ToKMhYlUWJ0YrbggpJLZeyZZNDXtQ4NITxqo/oeGltTT9KG4o/LqVY59EveV0f8P32ObDyj9Vh1mnjxeo3DxGw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.428.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.428.0.tgz", + "integrity": "sha512-qlc2UoGsmCpuh1ErY3VayZuAGl74TWWcLmhhQMkeByFSb6KooBlwOmDpDzJRtgwJoe0KXnyHBO6lzl9iczcozg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/types": "^2.3.5", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.430.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.430.0.tgz", + "integrity": "sha512-DPpFPL3mFMPtipFxjY7TKQBjnhmsPzYCr4Y+qna0oR6ij8jZOz2ILQDK33GxTRNh3+bV9YYbx+ZGDOnxoK5Mhw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.428.0", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -437,6 +1081,546 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@smithy/abort-controller": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.11.tgz", + "integrity": "sha512-MSzE1qR2JNyb7ot3blIOT3O3H0Jn06iNDEgHRaqZUwBgx5EG+VIx24Y21tlKofzYryIOcWpIohLrIIyocD6LMA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.15.tgz", + "integrity": "sha512-a2Pfocla5nSrG2RyB8i20jcWgMyR71TUeFKm8pmrnZotr/X22tlg4y/EhSvBK2oTE8MKHlKh4YdpDO2AryJbGQ==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.2", + "@smithy/types": "^2.3.5", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.17.tgz", + "integrity": "sha512-2XcD414yrwbxxuYueTo7tzLC2/w3jj9FZqfenpv3MQkocdOEmuOVS0v9WHsY/nW6V+2EcR340rj/z5HnvsHncQ==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.2", + "@smithy/property-provider": "^2.0.12", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.11.tgz", + "integrity": "sha512-BQCTjxhCYRZIfXapa2LmZSaH8QUBGwMZw7XRN83hrdixbLjIcj+o549zjkedFS07Ve2TlvWUI6BTzP+nv7snBA==", + "dev": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.3.5", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.3.tgz", + "integrity": "sha512-0G9sePU+0R+8d7cie+OXzNbbkjnD4RfBlVCs46ZEuQAMcxK8OniemYXSSkOc80CCk8Il4DnlYZcUSvsIs2OB2w==", + "dev": true, + "dependencies": { + "@smithy/protocol-http": "^3.0.7", + "@smithy/querystring-builder": "^2.0.11", + "@smithy/types": "^2.3.5", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.11.tgz", + "integrity": "sha512-PbleVugN2tbhl1ZoNWVrZ1oTFFas/Hq+s6zGO8B9bv4w/StTriTKA9W+xZJACOj9X7zwfoTLbscM+avCB1KqOQ==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.11.tgz", + "integrity": "sha512-zazq99ujxYv/NOf9zh7xXbNgzoVLsqE0wle8P/1zU/XdhPi/0zohTPKWUzIxjGdqb5hkkwfBkNkl5H+LE0mvgw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.13.tgz", + "integrity": "sha512-Md2kxWpaec3bXp1oERFPQPBhOXCkGSAF7uc1E+4rkwjgw3/tqAXRtbjbggu67HJdwaif76As8AV6XxbD1HzqTQ==", + "dev": true, + "dependencies": { + "@smithy/protocol-http": "^3.0.7", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.1.2.tgz", + "integrity": "sha512-dua4r2EbSTRzNefz72snz+KDuXN73RCe1K+rGeemzUyYemxuh1jujFbLQbTU6DVlTgHkhtrbH0+kdOFY/SV4Qg==", + "dev": true, + "dependencies": { + "@smithy/middleware-serde": "^2.0.11", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/shared-ini-file-loader": "^2.2.1", + "@smithy/types": "^2.3.5", + "@smithy/url-parser": "^2.0.11", + "@smithy/util-middleware": "^2.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.17.tgz", + "integrity": "sha512-ZYVU1MmshCTbEKTNc5h7/Pps1vhH5C7hRclQWnAbVYKkIT+PEGu9dSVqprzEo/nlMA8Zv4Dj5Y+fv3pRnUwElw==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.2", + "@smithy/protocol-http": "^3.0.7", + "@smithy/service-error-classification": "^2.0.4", + "@smithy/types": "^2.3.5", + "@smithy/util-middleware": "^2.0.4", + "@smithy/util-retry": "^2.0.4", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.11.tgz", + "integrity": "sha512-NuxnjMyf4zQqhwwdh0OTj5RqpnuT6HcH5Xg5GrPijPcKzc2REXVEVK4Yyk8ckj8ez1XSj/bCmJ+oNjmqB02GWA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.5.tgz", + "integrity": "sha512-bVQU/rZzBY7CbSxIrDTGZYnBWKtIw+PL/cRc9B7etZk1IKSOe0NvKMJyWllfhfhrTeMF6eleCzOihIQympAvPw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.2.tgz", + "integrity": "sha512-tbYh/JK/ddxKWYTtjLgap0juyivJ0wCvywMqINb54zyOVHoKYM6iYl7DosQA0owFaNp6GAx1lXFjqGz7L2fAqA==", + "dev": true, + "dependencies": { + "@smithy/property-provider": "^2.0.12", + "@smithy/shared-ini-file-loader": "^2.2.1", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.7.tgz", + "integrity": "sha512-PQIKZXlp3awCDn/xNlCSTFE7aYG/5Tx33M05NfQmWYeB5yV1GZZOSz4dXpwiNJYTXb9jPqjl+ueXXkwtEluFFA==", + "dev": true, + "dependencies": { + "@smithy/abort-controller": "^2.0.11", + "@smithy/protocol-http": "^3.0.7", + "@smithy/querystring-builder": "^2.0.11", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.12.tgz", + "integrity": "sha512-Un/OvvuQ1Kg8WYtoMCicfsFFuHb/TKL3pCA6ZIo/WvNTJTR94RtoRnL7mY4XkkUAoFMyf6KjcQJ76y1FX7S5rw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.7.tgz", + "integrity": "sha512-HnZW8y+r66ntYueCDbLqKwWcMNWW8o3eVpSrHNluwtBJ/EUWfQHRKSiu6vZZtc6PGfPQWgVfucoCE/C3QufMAA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.11.tgz", + "integrity": "sha512-b4kEbVMxpmfv2VWUITn2otckTi7GlMteZQxi+jlwedoATOGEyrCJPfRcYQJjbCi3fZ2QTfh3PcORvB27+j38Yg==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.11.tgz", + "integrity": "sha512-YXe7jhi7s3dQ0Fu9dLoY/gLu6NCyy8tBWJL/v2c9i7/RLpHgKT+uT96/OqZkHizCJ4kr0ZD46tzMjql/o60KLg==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.4.tgz", + "integrity": "sha512-77506l12I5gxTZqBkx3Wb0RqMG81bMYLaVQ+EqIWFwQDJRs5UFeXogKxSKojCmz1wLUziHZQXm03MBzPQiumQw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.1.tgz", + "integrity": "sha512-eAYajwo2eTTVU5KPX90+V6ccfrWphrzcUwOt7n9pLOMBO0fOKlRVshbvCBqfRCxEn7OYDGH6TsL3yrx+hAjddA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.11.tgz", + "integrity": "sha512-EFVU1dT+2s8xi227l1A9O27edT/GNKvyAK6lZnIZ0zhIHq/jSLznvkk15aonGAM1kmhmZBVGpI7Tt0odueZK9A==", + "dev": true, + "dependencies": { + "@smithy/eventstream-codec": "^2.0.11", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.3.5", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.4", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.11.tgz", + "integrity": "sha512-okjMbuBBCTiieK665OFN/ap6u9+Z9z55PMphS5FYCsS6Zfp137Q3qlnt0OgBAnUVnH/mNGyoJV0LBX9gkTWptg==", + "dev": true, + "dependencies": { + "@smithy/middleware-stack": "^2.0.5", + "@smithy/types": "^2.3.5", + "@smithy/util-stream": "^2.0.16", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.3.5.tgz", + "integrity": "sha512-ehyDt8M9hehyxrLQGoA1BGPou8Js1Ocoh5M0ngDhJMqbFmNK5N6Xhr9/ZExWkyIW8XcGkiMPq3ZUEE0ScrhbuQ==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.11.tgz", + "integrity": "sha512-h89yXMCCF+S5k9XIoKltMIWTYj+FcEkU/IIFZ6RtE222fskOTL4Iak6ZRG+ehSvZDt8yKEcxqheTDq7JvvtK3g==", + "dev": true, + "dependencies": { + "@smithy/querystring-parser": "^2.0.11", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "dev": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", + "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", + "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "dev": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.15.tgz", + "integrity": "sha512-2raMZOYKSuke7QlDg/HDcxQdrp0zteJ8z+S0B9Rn23J55ZFNK1+IjG4HkN6vo/0u3Xy/JOdJ93ibiBSB8F7kOw==", + "dev": true, + "dependencies": { + "@smithy/property-provider": "^2.0.12", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.20.tgz", + "integrity": "sha512-kJjcZ/Lzvs3sPDKBwlhZsFFcgPNIpB3CMb6/saCakawRzo0E+JkyS3ZZRjVR3ce29yHtwoP/0YLKC1PeH0Dffg==", + "dev": true, + "dependencies": { + "@smithy/config-resolver": "^2.0.15", + "@smithy/credential-provider-imds": "^2.0.17", + "@smithy/node-config-provider": "^2.1.2", + "@smithy/property-provider": "^2.0.12", + "@smithy/smithy-client": "^2.1.11", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.4.tgz", + "integrity": "sha512-Pbu6P4MBwRcjrLgdTR1O4Y3c0sTZn2JdOiJNcgL7EcIStcQodj+6ZTXtbyU/WTEU3MV2NMA10LxFc3AWHZ3+4A==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.4.tgz", + "integrity": "sha512-b+n1jBBKc77C1E/zfBe1Zo7S9OXGBiGn55N0apfhZHxPUP/fMH5AhFUUcWaJh7NAnah284M5lGkBKuhnr3yK5w==", + "dev": true, + "dependencies": { + "@smithy/service-error-classification": "^2.0.4", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.16.tgz", + "integrity": "sha512-b5ZSRh1KzUzC7LoJcpfk7+iXGoRr3WylEfmPd4FnBLm90OwxSB9VgK1fDZwicfYxSEvWHdYXgvvjPtenEYBBhw==", + "dev": true, + "dependencies": { + "@smithy/fetch-http-handler": "^2.2.3", + "@smithy/node-http-handler": "^2.1.7", + "@smithy/types": "^2.3.5", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "dev": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.11.tgz", + "integrity": "sha512-8SJWUl9O1YhjC77EccgltI3q4XZQp3vp9DGEW6o0OdkUcwqm/H4qOLnMkA2n+NDojuM5Iia2jWoCdbluIiG7TA==", + "dev": true, + "dependencies": { + "@smithy/abort-controller": "^2.0.11", + "@smithy/types": "^2.3.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.13", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", @@ -1425,6 +2609,12 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "dev": true + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -1568,9 +2758,9 @@ } }, "node_modules/cdk8s-cli": { - "version": "2.136.0", - "resolved": "https://registry.npmjs.org/cdk8s-cli/-/cdk8s-cli-2.136.0.tgz", - "integrity": "sha512-gPCp7LmVtwMAa8DjObW/gprufwGxb3oojDubfKUtjixeIO4lG/8Srwh56X9CYWYvLmVCeZwnn/Rodwm1lEUPlQ==", + "version": "2.138.0", + "resolved": "https://registry.npmjs.org/cdk8s-cli/-/cdk8s-cli-2.138.0.tgz", + "integrity": "sha512-kKiZqoZU+2a83Kl3gEisa4ndXeTGuZqnN0PUJfpElHib41KZjrNJNSt+1zzU042CYR+C1OADCNpV5moxK6uMbw==", "dev": true, "dependencies": { "@types/node": "^16", @@ -2946,6 +4136,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "dev": true, + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -6408,6 +7620,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6777,6 +7995,15 @@ "node": ">= 4" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index e059ec901..7caf73322 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "format": "npx prettier . -w" }, "devDependencies": { + "@aws-sdk/client-cloudformation": "^3.429.0", + "@aws-sdk/client-eks": "^3.429.0", "@aws-cdk/lambda-layer-kubectl-v27": "^2.0.0", "@linzjs/style": "^5.0.0", "aws-cdk": "2.93.x",