Unverified Commit 07c8074c authored by Jack Kleeman's avatar Jack Kleeman Committed by GitHub
Browse files

Support EKS Pod Identity credentials (#3416)



## Motivation and Context
I would like to support EKS Pod Identity credentials in the Rust SDKs

## Description
This brings the ECS provider in line with other sdks (eg, Go) by
supporting AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE as well as permitting
http IPs to be non-loopback if they are the EKS pod identity IPs.

## Testing
I have added various new unit tests, and I have updated the existing
integration test to also create pods with eks pod identity creds, which
I have used to test in a real EKS cluster as well.

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Signed-off-by: default avatarJack Kleeman <jackkleeman@gmail.com>
Co-authored-by: default avatarJohn DiSanti <john@vinylsquid.com>
Co-authored-by: default avatarJohn DiSanti <jdisanti@amazon.com>
parent 607c8918
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ repos:
  hooks:
  - id: check-yaml
  - id: end-of-file-fixer
    exclude: ^aws/rust-runtime/aws-sigv4/aws-sig-v4-test-suite/
    exclude: ^aws/rust-runtime/(aws-sigv4/aws-sig-v4-test-suite/|aws-config/test-data/default-provider-chain/eks_pod_identity_credentials/fs/token.jwt$)
  - id: trailing-whitespace
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
  rev: v2.11.0
+7 −1
Original line number Diff line number Diff line
@@ -10,3 +10,9 @@
# references = ["smithy-rs#920"]
# meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"}
# author = "rcoh"

[[aws-sdk-rust]]
message = "EKS Pod Identity is now supported as part of the default ECS credential provider."
references = ["smithy-rs#3416"]
meta = { "breaking" = false, "bug" = false, "tada" = true }
author = "jackkleeman"
+7 −3
Original line number Diff line number Diff line
# CDK Stack for EKS credentials provider testing

This project defines a CDK stack that launches an EKS cluster, creates a DynamoDB table, and sets up a service account role so that the DynamoDB pod can access the table.
This project defines a CDK stack that launches an EKS cluster, creates a DynamoDB table, and sets up:
1. A service account role that allows a pod to access the table via IRSA.
2. A service account and pod identity association that allows a pod to access the table via EKS Pod Identity.

`test.rs` is provided as an example script to run.

@@ -9,6 +11,8 @@ This project defines a CDK stack that launches an EKS cluster, creates a DynamoD
cdk bootstrap aws://accountid/region
cdk deploy
# make lunch, go for a bike ride, etc. ~1h.
kubectl exec rust-sdk-test -it bash
# write some rust code, e.g. test.rs, run it.
kubectl exec rust-sdk-test-irsa -it bash
# write some rust code, e.g. test.rs, run it. will have irsa identity
kubectl exec rust-sdk-test-pod-identity -it bash
# run more rust code. will have eks pod identity
```
+73 −7
Original line number Diff line number Diff line
@@ -5,8 +5,10 @@

import { Stack, StackProps } from "aws-cdk-lib";
import * as eks from "aws-cdk-lib/aws-eks";
import * as iam from "aws-cdk-lib/aws-iam";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import { Construct } from "constructs";
import {KubectlV28Layer} from "@aws-cdk/lambda-layer-kubectl-v28";

export class EksCredentialsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
@@ -15,21 +17,83 @@ export class EksCredentialsStack extends Stack {

    // create a cluster
    const cluster = new eks.Cluster(this, 'hello-eks', {
      version: eks.KubernetesVersion.V1_21,
      version: eks.KubernetesVersion.V1_28,
      kubectlLayer: new KubectlV28Layer(this, 'hello-eks-kubectl'),
    });
    const serviceAccount = cluster.addServiceAccount("eks-service-account");

    const podIdentityAddon = new eks.CfnAddon(this, 'eks-pod-identity-addon', {
      addonName: 'eks-pod-identity-agent',
      clusterName: cluster.clusterName,
      addonVersion: 'v1.1.0-eksbuild.1',
    });

    const serviceAccountIRSA = cluster.addServiceAccount("eks-service-account-irsa");
    const serviceAccountPodIdentity = cluster.addManifest("eks-service-account-pod-identity", {
      apiVersion: "v1",
      kind: "ServiceAccount",
      metadata: {
        name: "eks-service-account-pod-identity",
        namespace: "default",
        labels: {"app.kubernetes.io/name": "eks-service-account-pod-identity"},
      }
    })

    const table = new dynamodb.Table(this, 'Table', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }
    });
    table.grantReadWriteData(serviceAccount);
    table.grantReadWriteData(serviceAccountIRSA);

    let podIdentityPrincipal = new iam.ServicePrincipal("pods.eks.amazonaws.com")

    let podIdentityRole = new iam.Role(cluster, "eks-role-pod-identity", {
      assumedBy: podIdentityPrincipal,
    })
    podIdentityRole.assumeRolePolicy?.addStatements(iam.PolicyStatement.fromJson({
          "Sid": "AllowEksAuthToAssumeRoleForPodIdentity",
          "Effect": "Allow",
          "Principal": {
            "Service": "pods.eks.amazonaws.com"
          },
          "Action": [
            "sts:AssumeRole",
            "sts:TagSession"
          ]
    }))
    table.grantReadWriteData(podIdentityRole);

    let podIdentityAssociation = new eks.CfnPodIdentityAssociation(cluster, "eks-pod-identity-associations", {
      roleArn: podIdentityRole.roleArn,
      clusterName: cluster.clusterName,
      namespace: "default",
      serviceAccount: "eks-service-account-pod-identity",
    })

    // apply a kubernetes manifest to the cluster
    const pod = cluster.addManifest('rust-sdk-test', {
    const podIRSA = cluster.addManifest('rust-sdk-test-irsa', {
      apiVersion: 'v1',
      kind: 'Pod',
      metadata: { name: 'rust-sdk-test-irsa' },
      spec: {
        serviceAccountName: serviceAccountIRSA.serviceAccountName,
        containers: [
          {
            name: 'hello',
            image: 'rust:buster',
            ports: [{ containerPort: 8080 }],
            command: ['sh', '-c', 'sleep infinity'],
            env: [{name: 'DYNAMO_TABLE', value: table.tableName}]
          }
        ]
      }
    });
    podIRSA.node.addDependency(serviceAccountIRSA);

    const podPodIdentity = cluster.addManifest('rust-sdk-test-pod-identity', {
      apiVersion: 'v1',
      kind: 'Pod',
      metadata: { name: 'rust-sdk-test' },
      metadata: { name: 'rust-sdk-test-pod-identity' },
      spec: {
        serviceAccountName: serviceAccount.serviceAccountName,
        serviceAccountName: "eks-service-account-pod-identity",
        containers: [
          {
            name: 'hello',
@@ -41,6 +105,8 @@ export class EksCredentialsStack extends Stack {
        ]
      }
    });
    pod.node.addDependency(serviceAccount);
    podPodIdentity.node.addDependency(serviceAccountPodIdentity);
    podPodIdentity.node.addDependency(podIdentityAssociation);
    podPodIdentity.node.addDependency(podIdentityAddon)
  }
}
+384 −40
Original line number Diff line number Diff line
@@ -12,14 +12,43 @@
        "eks-credentials": "bin/eks-credentials.js"
      },
      "devDependencies": {
        "@aws-cdk/lambda-layer-kubectl-v28": "^2.2.0",
        "@types/node": "10.17.27",
        "aws-cdk": "^2.0.0",
        "aws-cdk-lib": "^2.0.0",
        "aws-cdk-lib": "^2.124.0",
        "constructs": "^10.0.0",
        "ts-node": "^9.0.0",
        "typescript": "~4.5.5"
      }
    },
    "node_modules/@aws-cdk/asset-awscli-v1": {
      "version": "2.2.202",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz",
      "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==",
      "dev": true
    },
    "node_modules/@aws-cdk/asset-kubectl-v20": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz",
      "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==",
      "dev": true
    },
    "node_modules/@aws-cdk/asset-node-proxy-agent-v6": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz",
      "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==",
      "dev": true
    },
    "node_modules/@aws-cdk/lambda-layer-kubectl-v28": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/@aws-cdk/lambda-layer-kubectl-v28/-/lambda-layer-kubectl-v28-2.2.0.tgz",
      "integrity": "sha512-m7nMDn/Ff9S+gJ5Sok5NuYHBzgsj3Xz3dOo0BxXYJJNPl9UtD1HnPcKV56lHn9+BACJff/h8aPUMln0xCUPuIw==",
      "dev": true,
      "peerDependencies": {
        "aws-cdk-lib": "^2.28.0",
        "constructs": "^10.0.5"
      }
    },
    "node_modules/@types/node": {
      "version": "10.17.27",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.27.tgz",
@@ -48,9 +77,9 @@
      }
    },
    "node_modules/aws-cdk-lib": {
      "version": "2.17.0",
      "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.17.0.tgz",
      "integrity": "sha512-bga2HptbGx3rMdSkIKxBS13miogj/DHB2VPfQZAoKoCOAanOot+M3mHhYqe5aNdxhrppaRjG2eid2p1/MvRnvg==",
      "version": "2.128.0",
      "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.128.0.tgz",
      "integrity": "sha512-cAU1L4jtPXPQXpa9kS2/HhHdkg3xGc5GCqwRgivdoj/iLQF3dDwIouOkwDBY/S5pXMqOUC7IoVdIPPbIgfGlsQ==",
      "bundleDependencies": [
        "@balena/dockerignore",
        "case",
@@ -60,18 +89,23 @@
        "minimatch",
        "punycode",
        "semver",
        "table",
        "yaml"
      ],
      "dev": true,
      "dependencies": {
        "@aws-cdk/asset-awscli-v1": "^2.2.202",
        "@aws-cdk/asset-kubectl-v20": "^2.1.2",
        "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1",
        "@balena/dockerignore": "^1.0.2",
        "case": "1.6.3",
        "fs-extra": "^9.1.0",
        "ignore": "^5.2.0",
        "jsonschema": "^1.4.0",
        "fs-extra": "^11.2.0",
        "ignore": "^5.3.1",
        "jsonschema": "^1.4.1",
        "minimatch": "^3.1.2",
        "punycode": "^2.1.1",
        "semver": "^7.3.5",
        "punycode": "^2.3.1",
        "semver": "^7.5.4",
        "table": "^6.8.1",
        "yaml": "1.10.2"
      },
      "engines": {
@@ -87,13 +121,53 @@
      "inBundle": true,
      "license": "Apache-2.0"
    },
    "node_modules/aws-cdk-lib/node_modules/at-least-node": {
      "version": "1.0.0",
    "node_modules/aws-cdk-lib/node_modules/ajv": {
      "version": "8.12.0",
      "dev": true,
      "inBundle": true,
      "license": "ISC",
      "license": "MIT",
      "dependencies": {
        "fast-deep-equal": "^3.1.1",
        "json-schema-traverse": "^1.0.0",
        "require-from-string": "^2.0.2",
        "uri-js": "^4.2.2"
      },
      "funding": {
        "type": "github",
        "url": "https://github.com/sponsors/epoberezkin"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/ansi-regex": {
      "version": "5.0.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/ansi-styles": {
      "version": "4.3.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "color-convert": "^2.0.1"
      },
      "engines": {
        "node": ">= 4.0.0"
        "node": ">=8"
      },
      "funding": {
        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/astral-regex": {
      "version": "2.0.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/balanced-match": {
@@ -121,35 +195,64 @@
        "node": ">= 0.8.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/color-convert": {
      "version": "2.0.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "color-name": "~1.1.4"
      },
      "engines": {
        "node": ">=7.0.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/color-name": {
      "version": "1.1.4",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/concat-map": {
      "version": "0.0.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/emoji-regex": {
      "version": "8.0.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": {
      "version": "3.1.3",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/fs-extra": {
      "version": "9.1.0",
      "version": "11.2.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "at-least-node": "^1.0.0",
        "graceful-fs": "^4.2.0",
        "jsonfile": "^6.0.1",
        "universalify": "^2.0.0"
      },
      "engines": {
        "node": ">=10"
        "node": ">=14.14"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/graceful-fs": {
      "version": "4.2.9",
      "version": "4.2.11",
      "dev": true,
      "inBundle": true,
      "license": "ISC"
    },
    "node_modules/aws-cdk-lib/node_modules/ignore": {
      "version": "5.2.0",
      "version": "5.3.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
@@ -157,6 +260,21 @@
        "node": ">= 4"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": {
      "version": "3.0.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": {
      "version": "1.0.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/jsonfile": {
      "version": "6.1.0",
      "dev": true,
@@ -170,7 +288,7 @@
      }
    },
    "node_modules/aws-cdk-lib/node_modules/jsonschema": {
      "version": "1.4.0",
      "version": "1.4.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
@@ -178,6 +296,12 @@
        "node": "*"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/lodash.truncate": {
      "version": "4.4.2",
      "dev": true,
      "inBundle": true,
      "license": "MIT"
    },
    "node_modules/aws-cdk-lib/node_modules/lru-cache": {
      "version": "6.0.0",
      "dev": true,
@@ -203,7 +327,7 @@
      }
    },
    "node_modules/aws-cdk-lib/node_modules/punycode": {
      "version": "2.1.1",
      "version": "2.3.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
@@ -211,8 +335,17 @@
        "node": ">=6"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/require-from-string": {
      "version": "2.0.2",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/semver": {
      "version": "7.3.5",
      "version": "7.5.4",
      "dev": true,
      "inBundle": true,
      "license": "ISC",
@@ -226,8 +359,67 @@
        "node": ">=10"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/slice-ansi": {
      "version": "4.0.0",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "ansi-styles": "^4.0.0",
        "astral-regex": "^2.0.0",
        "is-fullwidth-code-point": "^3.0.0"
      },
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/string-width": {
      "version": "4.2.3",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "emoji-regex": "^8.0.0",
        "is-fullwidth-code-point": "^3.0.0",
        "strip-ansi": "^6.0.1"
      },
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/strip-ansi": {
      "version": "6.0.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
      "dependencies": {
        "ansi-regex": "^5.0.1"
      },
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/table": {
      "version": "6.8.1",
      "dev": true,
      "inBundle": true,
      "license": "BSD-3-Clause",
      "dependencies": {
        "ajv": "^8.0.1",
        "lodash.truncate": "^4.4.2",
        "slice-ansi": "^4.0.0",
        "string-width": "^4.2.3",
        "strip-ansi": "^6.0.1"
      },
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/universalify": {
      "version": "2.0.0",
      "version": "2.0.1",
      "dev": true,
      "inBundle": true,
      "license": "MIT",
@@ -235,6 +427,15 @@
        "node": ">= 10.0.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/uri-js": {
      "version": "4.4.1",
      "dev": true,
      "inBundle": true,
      "license": "BSD-2-Clause",
      "dependencies": {
        "punycode": "^2.1.0"
      }
    },
    "node_modules/aws-cdk-lib/node_modules/yallist": {
      "version": "4.0.0",
      "dev": true,
@@ -369,6 +570,31 @@
    }
  },
  "dependencies": {
    "@aws-cdk/asset-awscli-v1": {
      "version": "2.2.202",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz",
      "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==",
      "dev": true
    },
    "@aws-cdk/asset-kubectl-v20": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz",
      "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==",
      "dev": true
    },
    "@aws-cdk/asset-node-proxy-agent-v6": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz",
      "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==",
      "dev": true
    },
    "@aws-cdk/lambda-layer-kubectl-v28": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/@aws-cdk/lambda-layer-kubectl-v28/-/lambda-layer-kubectl-v28-2.2.0.tgz",
      "integrity": "sha512-m7nMDn/Ff9S+gJ5Sok5NuYHBzgsj3Xz3dOo0BxXYJJNPl9UtD1HnPcKV56lHn9+BACJff/h8aPUMln0xCUPuIw==",
      "dev": true,
      "requires": {}
    },
    "@types/node": {
      "version": "10.17.27",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.27.tgz",
@@ -391,19 +617,23 @@
      }
    },
    "aws-cdk-lib": {
      "version": "2.17.0",
      "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.17.0.tgz",
      "integrity": "sha512-bga2HptbGx3rMdSkIKxBS13miogj/DHB2VPfQZAoKoCOAanOot+M3mHhYqe5aNdxhrppaRjG2eid2p1/MvRnvg==",
      "version": "2.128.0",
      "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.128.0.tgz",
      "integrity": "sha512-cAU1L4jtPXPQXpa9kS2/HhHdkg3xGc5GCqwRgivdoj/iLQF3dDwIouOkwDBY/S5pXMqOUC7IoVdIPPbIgfGlsQ==",
      "dev": true,
      "requires": {
        "@aws-cdk/asset-awscli-v1": "^2.2.202",
        "@aws-cdk/asset-kubectl-v20": "^2.1.2",
        "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1",
        "@balena/dockerignore": "^1.0.2",
        "case": "1.6.3",
        "fs-extra": "^9.1.0",
        "ignore": "^5.2.0",
        "jsonschema": "^1.4.0",
        "fs-extra": "^11.2.0",
        "ignore": "^5.3.1",
        "jsonschema": "^1.4.1",
        "minimatch": "^3.1.2",
        "punycode": "^2.1.1",
        "semver": "^7.3.5",
        "punycode": "^2.3.1",
        "semver": "^7.5.4",
        "table": "^6.8.1",
        "yaml": "1.10.2"
      },
      "dependencies": {
@@ -412,8 +642,32 @@
          "bundled": true,
          "dev": true
        },
        "at-least-node": {
          "version": "1.0.0",
        "ajv": {
          "version": "8.12.0",
          "bundled": true,
          "dev": true,
          "requires": {
            "fast-deep-equal": "^3.1.1",
            "json-schema-traverse": "^1.0.0",
            "require-from-string": "^2.0.2",
            "uri-js": "^4.2.2"
          }
        },
        "ansi-regex": {
          "version": "5.0.1",
          "bundled": true,
          "dev": true
        },
        "ansi-styles": {
          "version": "4.3.0",
          "bundled": true,
          "dev": true,
          "requires": {
            "color-convert": "^2.0.1"
          }
        },
        "astral-regex": {
          "version": "2.0.0",
          "bundled": true,
          "dev": true
        },
@@ -436,29 +690,61 @@
          "bundled": true,
          "dev": true
        },
        "color-convert": {
          "version": "2.0.1",
          "bundled": true,
          "dev": true,
          "requires": {
            "color-name": "~1.1.4"
          }
        },
        "color-name": {
          "version": "1.1.4",
          "bundled": true,
          "dev": true
        },
        "concat-map": {
          "version": "0.0.1",
          "bundled": true,
          "dev": true
        },
        "emoji-regex": {
          "version": "8.0.0",
          "bundled": true,
          "dev": true
        },
        "fast-deep-equal": {
          "version": "3.1.3",
          "bundled": true,
          "dev": true
        },
        "fs-extra": {
          "version": "9.1.0",
          "version": "11.2.0",
          "bundled": true,
          "dev": true,
          "requires": {
            "at-least-node": "^1.0.0",
            "graceful-fs": "^4.2.0",
            "jsonfile": "^6.0.1",
            "universalify": "^2.0.0"
          }
        },
        "graceful-fs": {
          "version": "4.2.9",
          "version": "4.2.11",
          "bundled": true,
          "dev": true
        },
        "ignore": {
          "version": "5.2.0",
          "version": "5.3.1",
          "bundled": true,
          "dev": true
        },
        "is-fullwidth-code-point": {
          "version": "3.0.0",
          "bundled": true,
          "dev": true
        },
        "json-schema-traverse": {
          "version": "1.0.0",
          "bundled": true,
          "dev": true
        },
@@ -472,7 +758,12 @@
          }
        },
        "jsonschema": {
          "version": "1.4.0",
          "version": "1.4.1",
          "bundled": true,
          "dev": true
        },
        "lodash.truncate": {
          "version": "4.4.2",
          "bundled": true,
          "dev": true
        },
@@ -493,23 +784,76 @@
          }
        },
        "punycode": {
          "version": "2.1.1",
          "version": "2.3.1",
          "bundled": true,
          "dev": true
        },
        "require-from-string": {
          "version": "2.0.2",
          "bundled": true,
          "dev": true
        },
        "semver": {
          "version": "7.3.5",
          "version": "7.5.4",
          "bundled": true,
          "dev": true,
          "requires": {
            "lru-cache": "^6.0.0"
          }
        },
        "slice-ansi": {
          "version": "4.0.0",
          "bundled": true,
          "dev": true,
          "requires": {
            "ansi-styles": "^4.0.0",
            "astral-regex": "^2.0.0",
            "is-fullwidth-code-point": "^3.0.0"
          }
        },
        "string-width": {
          "version": "4.2.3",
          "bundled": true,
          "dev": true,
          "requires": {
            "emoji-regex": "^8.0.0",
            "is-fullwidth-code-point": "^3.0.0",
            "strip-ansi": "^6.0.1"
          }
        },
        "strip-ansi": {
          "version": "6.0.1",
          "bundled": true,
          "dev": true,
          "requires": {
            "ansi-regex": "^5.0.1"
          }
        },
        "table": {
          "version": "6.8.1",
          "bundled": true,
          "dev": true,
          "requires": {
            "ajv": "^8.0.1",
            "lodash.truncate": "^4.4.2",
            "slice-ansi": "^4.0.0",
            "string-width": "^4.2.3",
            "strip-ansi": "^6.0.1"
          }
        },
        "universalify": {
          "version": "2.0.0",
          "version": "2.0.1",
          "bundled": true,
          "dev": true
        },
        "uri-js": {
          "version": "4.4.1",
          "bundled": true,
          "dev": true,
          "requires": {
            "punycode": "^2.1.0"
          }
        },
        "yallist": {
          "version": "4.0.0",
          "bundled": true,
Loading