diff --git a/examples/README.md b/examples/README.md index 0a9c232..b4e16eb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,9 +1,22 @@ # Examples -Different examples for the deployment of the FIWARE Data Space Connector +Different examples for the deployment of the FIWARE Data Space Connector, as well as the integration with +other frameworks. +
+Contents -## IPS Service Provider (helm) +- [Deployment of service providers](#deployment-of-service-providers) + - [IPS Service Provider (helm)](#ips-service-provider-helm) + - [Packet Delivery Company (ArgoCD)](#packet-delivery-company-argocd) +- [Integration with AWS Garnet Framework](#integration-with-aws-garnet-framework-formerly-aws-smart-territory-framework) + +
+ + +## Deployment of service providers + +### IPS Service Provider (helm) This is an example of a data service provider, providing a fictitious digital service for packet delivery services as a company called `IPS`. @@ -13,11 +26,12 @@ access to the entities of certain delivery orders. The example uses plain helm for the deployment. +More information can be found here: * [./service-provider-ips](./service-provider-ips) -## Packet Delivery Company (ArgoCD) +### Packet Delivery Company (ArgoCD) This is an example of a data service provider called Packet Delivery Company (PDC). @@ -26,3 +40,18 @@ Basically, it's identical to IPS above, but deployment is performed via The configuration can be found at the [fiware-gitops repository](https://github.com/FIWARE-Ops/fiware-gitops/tree/master/aws/dsba/packet-delivery/data-space-connector). + + + + +## Integration with AWS Garnet Framework (formerly AWS Smart Territory Framework) + +This is an example of a data service provider that is integrated with the +[AWS Garnet Framwork (formerly AWS STF)](https://github.com/aws-samples/aws-stf). + +In general, this example deploys a data service provider based on the Data Space Connector, +but integrating the FIWARE Context Broker from the STF. + +More information can be found here: +* [./aws-garnet](./aws-garnet) + diff --git a/examples/aws-garnet/README.md b/examples/aws-garnet/README.md new file mode 100644 index 0000000..2a1c580 --- /dev/null +++ b/examples/aws-garnet/README.md @@ -0,0 +1,147 @@ +# Integration with AWS Garnet Framework + +## Overview + +AWS Garnet Framework is an open-source framework aimed at simplifying the creation and operation of interoperable platforms across diverse domains, including Smart Cities, Energy, Agriculture, and more. +Compliant with the NGSI-LD open standard and harnessing NGSI-LD compliant Smart Data Models, this solution promotes openness and efficiency. +At its core, AWS Garnet Framework integrates the FIWARE Context Broker, an integral component that facilitates data management. +[In the official project GitHub repository](https://github.com/aws-samples/aws-stf-core), you'll find the necessary resources to deploy both the FIWARE Context Broker and the Garnet IoT module as separate AWS Cloud Development Kit (CDK) nested stacks, offering a flexible and modular approach to enhance and integrate existing solutions over time. + +For the context of Data Spaces, the AWS Garnet Framwork can be extended with the capabilities of the FIWARE Data Spaces Connector, which can instrument an existing deployment of the FIWARE Context Broker, as seen in other examples of this repository. + +In this example, the procedure to deploy the packet delivery service provider named IPS on AWS is provided. This deployment pattern can be reused to implement data spaces use cases requiring the infrastructure of the FIWARE Data Spaces Connector. + +## Prerequisites + +This deployment example focuses on 2 possible initial configurations of infrastructure: + +* 1/ No existing AWS Garnet Framework deployment in the AWS Account + +![Target Architecture for a fresh deployment of AWS Garnet Framework with the DS Connector](./static-assets/garnet-ds-connector-scenario1.png) + +* 2/ Existing AWS Garnet Framework deployment in the AWS Account with a Context Broker on AWS ECS Fargate + +![Target Architecture for extending the deployment of an existing AWS Garnet Framework](./static-assets/garnet-ds-connector-scenario2.png) + +In any of the previous cases, an Amazon EKS Cluster is needed to deploy the Data Space Connector. However, if there is an existing Amazon EKS Cluster in your AWS, it can be leveraged for this deployment and no additional cluster must be created. The next steps will help deploying a new cluster from scratch for the connector deployment. + +### Amazon EKS Cluster Creation +If the creation of a dedicated Kubernetes cluster is considered for the deployment of the FIWARE Data Spaces Connector, it is recommended that users follow the instructions to create a new Amazon EKS Cluster available in the [official Amazon EKS Immersion Workshop](https://catalog.workshops.aws/eks-immersionday/en-US/introduction#confirm-eks-setup) + +#### AWS EKS Cluster Setup with Fargate Profile + +* Assign environment variables to choose the deployment parameters +```shell +export AWS_REGION=eu-west-1 +export ekscluster_name="fiware-dsc-cluster" +``` + +* Create the VPC to host the Amazon EKS cluster on your AWS Account - update the `eks-vpc-3az.yaml` file to select the desired region for your deployment + +```shell +aws cloudformation deploy --stack-name "eks-vpc" --template-file "./yaml/eks-vpc-3az.yaml" --capabilities CAPABILITY_NAMED_IAM +``` + +* Store the VPC ID in an environment variable + +```shell +export vpc_ID=$(aws ec2 describe-vpcs --filters Name=tag:Name,Values=eks-vpc | jq -r '.Vpcs[].VpcId') +echo $vpc_ID +``` + +* Export the Subnet ID, CIDR, and Subnet Name to a text file for tracking + +```shell +aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' +echo $vpc_ID > vpc_subnet.txt +aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' >> vpc_subnet.txt +cat vpc_subnet.txt +``` + +* Store VPC ID, Subnet IDs as environment variables that will be used on next steps + +```shell +export PublicSubnet01=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PublicSubnet01/{print $1}') +export PublicSubnet02=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PublicSubnet02/{print $1}') +export PublicSubnet03=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PublicSubnet03/{print $1}') +export PrivateSubnet01=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PrivateSubnet01/{print $1}') +export PrivateSubnet02=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PrivateSubnet02/{print $1}') +export PrivateSubnet03=$(aws ec2 describe-subnets --filter Name=vpc-id,Values=$vpc_ID | jq -r '.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)' | awk '/eks-vpc-PrivateSubnet03/{print $1}') +echo "export vpc_ID=${vpc_ID}" | tee -a ~/.bash_profile +echo "export PublicSubnet01=${PublicSubnet01}" | tee -a ~/.bash_profile +echo "export PublicSubnet02=${PublicSubnet02}" | tee -a ~/.bash_profile +echo "export PublicSubnet03=${PublicSubnet03}" | tee -a ~/.bash_profile +echo "export PrivateSubnet01=${PrivateSubnet01}" | tee -a ~/.bash_profile +echo "export PrivateSubnet02=${PrivateSubnet02}" | tee -a ~/.bash_profile +echo "export PrivateSubnet03=${PrivateSubnet03}" | tee -a ~/.bash_profile +source ~/.bash_profile +``` + +* Use the provided script `eks-cluster-fargateProfiler.sh` [available in this repository](./scripts/eks-cluster-fargateProfiler.sh) to populate your resources IDs to instantiate the Amazon EKS Cluster template + +```shell +chmod +x ./scripts/eks-cluster-fargateProfiler.sh +./scripts/eks-cluster-fargateProfiler.sh +``` + +* Create the Amazon EKS Cluster with Fargate Profile using `eksctl` + +```shell +eksctl create cluster --config-file=./yaml/eks-cluster-3az.yaml +``` + +* Create an IAM Identity Mapping to access your Amazon EKS cluster metadata using the AWS Console + +```shell +eksctl create iamidentitymapping --cluster fiware-dsc-cluster --arn arn:aws:iam:::role/ --group system:masters --username admin +``` + +* Check if your cluster is running properly once the Amazon CloudFormation Stack creation is complete + +```shell +kubectl get svc +``` + +* Configuring OIDC ID Provider(IdP) to EKS cluster allows you to use AWS IAM roles for Kubernetes service accounts, and this requires an IAM OIDC provider in the cluster. Let's run the command below to integrate OIDC into the cluster. + +```shell +eksctl utils associate-iam-oidc-provider --region ${AWS_REGION} --cluster fiware-dsc-cluster --approve +``` + +#### (OPTIONAL) Install [AWS Load Balancer Controller](https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html) add-on to manage ingress configuration +AWS Load Balancer Controller is a Kubernetes add-on that manages AWS Elastic Load Balancers(ELB) used by Kubernetes cluster. +This controller provides: + +* Provision new AWS ALB when Kubernetes Ingress is created. +* Provision new AWS NLB when Kubernetes LoadBalancer is created. + +It is recommended to follow the official AWS documentation to install the AWS Load Balancer Controller add-on to control ingress. The step-by-step procedure is available in [this link](https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html) + +#### nginx Ingress Controller Configuration +In AWS, we use a Network load balancer (NLB) to expose the Ingress-Nginx Controller behind a Service of ```Type=LoadBalancer```. It is advised that the [official Installation Guide is followed for the next steps](https://kubernetes.github.io/ingress-nginx/deploy/#aws) +A short version of the procedure is reproduced below for a quick setup: + +* Create an AWS IAM Policy for the Ingress Controller using the provided file `./policies/aws-lbc-iam_policy.json`. The JSON file can also be found [here](https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json) + +```shell +aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://./policies/aws-lbc-iam_policy.json +``` + +* Create an IAM Role and ServiceAccount for the AWS Load Balancer controller + +```shell +eksctl create iamserviceaccount --cluster=fiware-dsc-cluster --namespace=kube-system --name=ingress-nginx-controller --attach-policy-arn=arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --region ${AWS_REGION} --approve +``` + +* Deploy the Kubernetes Service for the nginx Ingress Controller on your cluster using the provided file `./yaml/nginx-ingress-controller.yaml` . The default deployment file is also available in [this link](https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml) + +```shell +kubectl apply -n kube-system -f ./yaml/nginx-ingress-controller.yaml +``` + +## Next Steps +Once your Amazon EKS Cluster is ready, head to the specific step-by-step procedure that best describes your current environment in the following links of this documentation: + +* [1/ No existing AWS Garnet Framework deployment in the AWS Account](./scenario-1-deployment/) + +* [2/ Existing AWS Garnet Framework deployment in the AWS Account with a Context Broker on AWS ECS Fargate](./scenario-2-deployment/) \ No newline at end of file diff --git a/examples/aws-garnet/policies/aws-lbc-iam_policy.json b/examples/aws-garnet/policies/aws-lbc-iam_policy.json new file mode 100644 index 0000000..7944f2a --- /dev/null +++ b/examples/aws-garnet/policies/aws-lbc-iam_policy.json @@ -0,0 +1,241 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:CreateServiceLinkedRole" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateSecurityGroup" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "StringEquals": { + "ec2:CreateAction": "CreateSecurityGroup" + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags", + "ec2:DeleteTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:DeleteRule" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "StringEquals": { + "elasticloadbalancing:CreateAction": [ + "CreateTargetGroup", + "CreateLoadBalancer" + ] + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets" + ], + "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:SetWebAcl", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule" + ], + "Resource": "*" + } + ] +} diff --git a/examples/aws-garnet/scenario-1-deployment/README.md b/examples/aws-garnet/scenario-1-deployment/README.md new file mode 100644 index 0000000..0d9b747 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/README.md @@ -0,0 +1,94 @@ +# Integration with AWS Garnet Framework + +## 1/ No existing AWS Garnet Framework deployment in the AWS Account +For this scenario, it is recommended that the complete Helm Chart for the Data Spaces Connector is deployed to a Kubernetes Cluster in the service Amazon Elastic Kubernetes Service ([AWS EKS](https://aws.amazon.com/eks/)). +In this case, the FIWARE Context Broker will be hosted by a pod in the Kubernetes cluster and the integration to the AWS Garnet Framework will be performed by deploying only the [AWS Garnet IoT module](https://github.com/aws-samples/aws-stf-core#stf-iot) of the framework. Further configuration will include streamlining the Garnet IoT pipeline to the Internal Service Load Balancer associated to the EKS cluster. + +
+ +![Target Architecture for a fresh deployment of AWS Garnet Framework with the DS Connector](../static-assets/garnet-ds-connector-scenario1.png) + +
+ +### IPS Service Provider Deployment in Amazon EKS +This section covers the setup of the prerequisites of the IPS Service Provider examples of this repository, available in [this reference](../service-provider-ips/README.md). + +* IPS Kubernetes namespace creation + +```shell +kubectl create namespace ips +``` + +* Add FIWARE Data Space Connector Remote repository + +```shell +helm repo add dsc https://fiware-ops.github.io/data-space-connector/ +``` + +* Install the Helm Chart using the provided file `./yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml` [available in this repository](./yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml) + +```shell +helm install -n ips -f ./yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml ips-dsc dsc/data-space-connector +``` + +### Deployment of AWS Garnet Framework IoT Module +An AWS CDK project modified from the AWS Garnet Framwork main project is available in [this repository](./aws-garnet-iot-module/). The project was modified so ONLY the AWS Garnet Framwrork IoT Module is deployed once the CDK stacks are deployed. To integrate this module to the Context Broker deployed in the Amazon EKS Cluster, 2 main parameters must be set in the `./aws-garnet-iot-module/parameters.ts` file : + +```shell + // FIWARE DATA SPACE CONNECTOR PARAMETERS + amazon_eks_cluster_load_balancer_dns: "", + amazon_eks_cluster_load_balancer_listener_arn: "", +``` + +Edit the file including the respective strings referencing your Load Balancer resource for the Data Space Connector deployed in the Amazon EKS Cluster in your AWS Account. + +Then, deploy the CDK stack using the following commands: + +```shell +cd aws-garnet-iot-module +``` + +```shell +npm install +``` + +```shell +cdk bootstrap +``` + +```shell +cdk deploy +``` + +## Other Resources - Troubleshooting +Once the Data Space Connector is deployed via the Helm chart in your cluster, additional scripts are also provided in this [repository](../scripts/) to help any troubleshooting of your connector deployment. +Two main scripts are provided: + +* 1/ Save all pods logs from a EKS cluster namespace using `kubectl` to your local deployment machine under `./podLogs/` [in this repository structure](./podLogs/) +The script `kubectlLogsFromNamespace.sh` [link](../scripts/kubectlLogsFromNamespace.sh) runs a local process in your deployment machine to poll logs from all currently running pods under a namespace from your cluster and save locally for further analysis. +It can be manually modified to change the desired namespace to be analyzed + +```shell +#!/bin/bash + +NAMESPACE="ips" +``` + +and the corresponding polling period by changing the values in the script file before running it + +```shell + # Sleep for a few seconds before checking for new logs and pods again + sleep 3 +done +``` + +* 2/ Delete all currently running pods from an EKS cluster namespace using `kubectl` +The script `deleteAllPodsFromNamespace.sh` [link](../scripts/deleteAllPodsFromNamespace.sh) runs a local process in your deployment machine to force delete long-lived running pods from a failed deployment. +It can be manually modified to change the desired namespace to be analyzed + +```shell +#!/bin/bash + +# Set the fixed namespace +namespace="ips" +``` \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/.gitignore b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/.gitignore new file mode 100644 index 0000000..abaa3d2 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/.gitignore @@ -0,0 +1,10 @@ +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out + +.DS_Store + diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CHANGELOG.md b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CODE_OF_CONDUCT.md b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5b627cf --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CONTRIBUTING.md b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CONTRIBUTING.md new file mode 100644 index 0000000..c4b6a1c --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *main* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/LICENSE b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/LICENSE new file mode 100644 index 0000000..1bb4f21 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/LICENSE @@ -0,0 +1,15 @@ +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/README.md b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/README.md new file mode 100644 index 0000000..e376098 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/README.md @@ -0,0 +1,14 @@ +## Garnet Framework + +**Garnet replaces the Smart Territory Framework.** + +Garnet is an open-source framework for building scalable, reliable and interoperable platforms leveraging open standards, FIWARE open source technology and AWS Cloud services. + +It supports the development and integration of smart and efficient solutions across multiple domains such as Smart Cities, Regions and Campuses, Energy and Utilities, Agriculture, Smart Building and Manufacturing. + +Garnet facilitates the creation and connection of smart solutions. It helps you avoid vendor lock-in and break silos. It makes it easier to bring data together into one single space from heterogeneous sources, both internal and external, and extract insights from it. + +Using Garnet, you can easily integrate existing solutions and add new capabilities and modules over time. + + +__Documentation website coming soon.__ \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/bin/garnet.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/bin/garnet.ts new file mode 100644 index 0000000..e83c4da --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/bin/garnet.ts @@ -0,0 +1,11 @@ +#!/usr/bin/env node +import { App } from 'aws-cdk-lib'; +import { GarnetStack } from '../lib/garnet-stack'; +import { Parameters } from '../parameters'; + +const app = new App(); + +new GarnetStack(app, 'Garnet', { + stackName: 'Garnet', + env: { region: Parameters.aws_region } +}) \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/cdk.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/cdk.json new file mode 100644 index 0000000..b68ba12 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/cdk.json @@ -0,0 +1,33 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/garnet.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ] + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_time.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_time.png new file mode 100644 index 0000000..87cfd32 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_time.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_type.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_type.png new file mode 100644 index 0000000..e32544e Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/bucket_type.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cdkoutputs.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cdkoutputs.png new file mode 100644 index 0000000..63f4402 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cdkoutputs.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cfoutputs.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cfoutputs.png new file mode 100644 index 0000000..b046e69 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/cfoutputs.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deletething.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deletething.png new file mode 100644 index 0000000..de71fa5 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deletething.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow.png new file mode 100644 index 0000000..b075136 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow2.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow2.png new file mode 100644 index 0000000..de7d111 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadow2.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadowlist2.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadowlist2.png new file mode 100644 index 0000000..09e7e38 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/deviceshadowlist2.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/entities.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/entities.png new file mode 100644 index 0000000..e78c490 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/entities.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/geospatialbikes.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/geospatialbikes.png new file mode 100644 index 0000000..7af5610 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/geospatialbikes.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/getthing.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/getthing.png new file mode 100644 index 0000000..4e6d85f Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/getthing.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/iotbucket.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/iotbucket.png new file mode 100644 index 0000000..ec5a307 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/iotbucket.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/lambdavpc.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/lambdavpc.png new file mode 100644 index 0000000..6f9d8d4 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/lambdavpc.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/listthings.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/listthings.png new file mode 100644 index 0000000..b250f92 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/listthings.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/orion_arch.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/orion_arch.png new file mode 100644 index 0000000..9a6b53d Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/orion_arch.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/parameters.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/parameters.png new file mode 100644 index 0000000..c30f35a Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/parameters.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postdevice.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postdevice.png new file mode 100644 index 0000000..904cf25 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postdevice.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmandelentity.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmandelentity.png new file mode 100644 index 0000000..c0f284b Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmandelentity.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmangetentity.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmangetentity.png new file mode 100644 index 0000000..b45647b Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmangetentity.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanheader.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanheader.png new file mode 100644 index 0000000..6e80d61 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanheader.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanpostentity.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanpostentity.png new file mode 100644 index 0000000..7c48e8d Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postmanpostentity.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postshadow.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postshadow.png new file mode 100644 index 0000000..8ace196 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/postshadow.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/referencearch.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/referencearch.png new file mode 100644 index 0000000..88a561b Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/referencearch.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/scorpio_arch.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/scorpio_arch.png new file mode 100644 index 0000000..1bd3a57 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/scorpio_arch.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/shadowstate.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/shadowstate.png new file mode 100644 index 0000000..0105d9a Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/shadowstate.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stf-yt2.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stf-yt2.png new file mode 100644 index 0000000..b79b0f7 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stf-yt2.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stfiot_arch.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stfiot_arch.png new file mode 100644 index 0000000..279bd67 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/stfiot_arch.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/things.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/things.png new file mode 100644 index 0000000..220521c Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/things.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/upsert.png b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/upsert.png new file mode 100644 index 0000000..aa0d975 Binary files /dev/null and b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/docs/images/upsert.png differ diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/install.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/install.js new file mode 100644 index 0000000..24cf97f --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/install.js @@ -0,0 +1,54 @@ +const path = require('path') +const fs = require('fs') +const child_process = require('child_process') + +const root = process.cwd() +npm_install_recursive(root) + +// Since this script is intended to be run as a "preinstall" command, +// it will do `npm install` automatically inside the root folder in the end. +console.log('===================================================================') +console.log(`Performing "npm install" inside root folder`) +console.log('===================================================================') + +// Recurses into a folder +function npm_install_recursive(folder) +{ + const has_package_json = fs.existsSync(path.join(folder, 'package.json')) + + // If there is `package.json` in this folder then perform `npm install`. + // + // Since this script is intended to be run as a "preinstall" command, + // skip the root folder, because it will be `npm install`ed in the end. + // Hence the `folder !== root` condition. + // + if (has_package_json && folder !== root) + { + console.log('===================================================================') + console.log(`Performing "npm install" inside ${folder === root ? 'root folder' : './' + path.relative(root, folder)}`) + console.log('===================================================================') + + npm_install(folder) + } + + // Recurse into subfolders + for (let subfolder of subfolders(folder)) + { + npm_install_recursive(subfolder) + } +} + +// Performs `npm install` +function npm_install(where) +{ + child_process.execSync('npm install', { cwd: where, env: process.env, stdio: 'inherit' }) +} + +// Lists subfolders in a folder +function subfolders(folder) +{ + return fs.readdirSync(folder) + .filter(subfolder => fs.statSync(path.join(folder, subfolder)).isDirectory()) + .filter(subfolder => subfolder !== 'node_modules' && subfolder[0] !== '.') + .map(subfolder => path.join(folder, subfolder)) +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/jest.config.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/jest.config.js new file mode 100644 index 0000000..08263b8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/garnet-stack.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/garnet-stack.ts new file mode 100644 index 0000000..5658d7a --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/garnet-stack.ts @@ -0,0 +1,63 @@ +import { CfnElement, CfnOutput, Names, Stack, StackProps } from 'aws-cdk-lib' +import { Construct } from 'constructs' +//import { GarnetScorpio } from './stacks/garnet-scorpio/garnet-scorpio' +import { GarnetIotStack } from './stacks/garnet-iot/garnet-iot-stack' +import { Parameters } from '../parameters' +import { GarnetOrion } from './stacks/garnet-orion/garnet-orion' +import { GarnetConstructs } from './stacks/garnet-constructs/garnet-constructs' +import { GarnetPrivateSub } from './stacks/garnet-constructs/privatesub' + + +export class GarnetStack extends Stack { + + + getLogicalId(element: CfnElement): string { + if (element.node.id.includes('NestedStackResource')) { + return /([a-zA-Z0-9]+)\.NestedStackResource/.exec(element.node.id)![1] // will be the exact id of the stack + } + return super.getLogicalId(element) +} + + + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props) + + const garnet_constructs = new GarnetConstructs(this, 'CommonContructs') + + let garnet_broker_stack: GarnetOrion + + garnet_broker_stack = new GarnetOrion(this, 'ContextBrokerProxy', { + vpc: garnet_constructs.vpc, + secret: garnet_constructs.secret + }) + + const garnet_iot_stack = new GarnetIotStack(this, 'GarnetIoT', { + dns_context_broker: Parameters.amazon_eks_cluster_load_balancer_dns,//garnet_broker_stack.dns_context_broker, + vpc: garnet_constructs.vpc, + api_ref: garnet_broker_stack.api_ref, + bucket_name: garnet_constructs.bucket_name + }) + + new CfnOutput(this, 'GarnetEndpoint', { + value: garnet_broker_stack.broker_api_endpoint, + description: 'Garnet Unified API to access the Context Broker and Garnet IoT Capabilities' + }) + + new CfnOutput(this, 'GarnetIotQueueArn', { + value: garnet_iot_stack.iot_sqs_endpoint_arn, + description: 'Garnet IoT SQS Queue ARN to connect your Data Producers' + }) + + new CfnOutput(this, 'GarnetPrivateSubEndpoint', { + value: garnet_constructs.private_sub_endpoint, + description: 'Garnet Private Notification Endpoint for Secured Subscriptions. Only accessible within the Garnet VPC' + }) + + new CfnOutput(this, 'GarnetVersion', { + value: `${Parameters.garnet_version}`, + description: 'Garnet Version' + }) + + + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/apigateway/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/apigateway/index.ts new file mode 100644 index 0000000..0e3d279 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/apigateway/index.ts @@ -0,0 +1,68 @@ +import { CfnOutput, Names} from "aws-cdk-lib" +import { CfnApi, CfnIntegration, CfnRoute, CfnStage, CfnVpcLink } from "aws-cdk-lib/aws-apigatewayv2" +import { Vpc } from "aws-cdk-lib/aws-ec2" +import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns" +import { Construct } from "constructs" +import { Parameters } from "../../../../parameters" + +export interface GarnetApiGatewayProps { + readonly vpc: Vpc, + readonly fargate_albListenerArn: string; // Change the type to string +} + +export class GarnetApiGateway extends Construct{ + public readonly api_ref: string + constructor(scope: Construct, id: string, props: GarnetApiGatewayProps) { + super(scope, id) + // Check props + if (!props.vpc){ + throw new Error('The property vpc is required to create an instance of GarnetApiGateway Construct') + } + //if (!props.fargate_alb){ + // throw new Error('The property fargate_alb is required to create an instance of GarnetApiGateway Construct') + //} + + const vpc_link = new CfnVpcLink(this, 'VpcLink', { + name: `garnet-vpc-link-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + subnetIds: props.vpc.privateSubnets.map( (m) => m.subnetId) + }) + + const api = new CfnApi(this, 'HttpApi', { + name: `garnet-api`, + protocolType: 'HTTP', + corsConfiguration: { + allowHeaders: ['*'], + allowMethods: ['*'], + allowOrigins: ['*'] + }, + + + }) + + const stage = new CfnStage(this, 'StageApi', { + apiId: api.ref, + stageName: '$default', + autoDeploy: true + }) + + const integration = new CfnIntegration(this, 'HttpApiIntegration', { + apiId: api.ref, + integrationMethod: "ANY", + integrationType: "HTTP_PROXY", + connectionType: "VPC_LINK", + description: "API Integration", + connectionId: vpc_link.ref, + integrationUri: props.fargate_albListenerArn, //props.fargate_alb.listener.listenerArn, + payloadFormatVersion: "1.0", + }) + + const route = new CfnRoute(this, 'Route', { + apiId: api.ref, + routeKey: "ANY /{proxy+}", + target: `integrations/${integration.ref}` + }) + + this.api_ref = api.ref + + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/index.ts new file mode 100644 index 0000000..76b2734 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/index.ts @@ -0,0 +1,50 @@ +import { CustomResource, Duration, Names } from "aws-cdk-lib"; +import { Runtime, Function, Code, Architecture } from "aws-cdk-lib/aws-lambda"; +import { Construct } from "constructs"; +import { Parameters } from "../../../../parameters"; +import { PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Provider } from "aws-cdk-lib/custom-resources"; + +export interface GarnetBucketProps { + + } + + +export class GarnetBucket extends Construct { + + public readonly bucket_name: string + + constructor(scope: Construct, id: string, props: GarnetBucketProps) { + super(scope, id) + + const lambda_bucket_path = `${__dirname}/lambda/bucketHead` + const lambda_bucket = new Function(this, 'AzFunction', { + functionName: `garnet-custom-bucket-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_bucket_path), + handler: 'index.handler', + timeout: Duration.seconds(50), + architecture: Architecture.ARM_64, + environment: { + BUCKET_NAME: Parameters.garnet_bucket + } + }) + + lambda_bucket.addToRolePolicy(new PolicyStatement({ + actions: ["s3:CreateBucket"], + resources: ["arn:aws:s3:::*"] + })) + + const bucket_provider = new Provider(this, 'CustomBucketProvider', { + onEventHandler: lambda_bucket + }) + + new CustomResource(this, 'CustomBucketProviderResource', { + serviceToken: bucket_provider.serviceToken + }) + + + this.bucket_name = Parameters.garnet_bucket + + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/lambda/bucketHead/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/lambda/bucketHead/index.js new file mode 100644 index 0000000..8b5494b --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/bucket/lambda/bucketHead/index.js @@ -0,0 +1,21 @@ +const { S3Client, CreateBucketCommand } = require("@aws-sdk/client-s3") +const s3 = new S3Client() +BUCKET_NAME = process.env.BUCKET_NAME + +exports.handler = async (event) => { + console.log(event) + let request_type = event['RequestType'] + if (request_type=='Create' || request_type == 'Update') { + + try { + await s3.send( + new CreateBucketCommand({ + Bucket: BUCKET_NAME + }) + ) + } catch (e) { + console.log(e.message) + } + return true + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/constants.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/constants.ts new file mode 100644 index 0000000..1ca4832 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/constants.ts @@ -0,0 +1,35 @@ +// List of AZs that support VPC links for HTTP APIs as https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vpc-links.html#http-api-vpc-link-availability +export const azlist: any = { + "us-east-2": ["use2-az1", "use2-az2", "use2-az3"], + "us-east-1": ["use1-az1", "use1-az2", "use1-az4", "use1-az5", "use1-az6"], + "us-west-1": ["usw1-az1", "usw1-az3"], + "us-west-2": ["usw2-az1", "usw2-az2", "usw2-az3", "usw2-az4"], + "ap-east-1": ["ape1-az2", "ape1-az3"], + "ap-south-1": ["aps1-az1", "aps1-az2", "aps1-az3"], + "ap-northeast-2": ["apne2-az1", "apne2-az2", "apne2-az3"], + "ap-southeast-1": ["apse1-az1", "apse1-az2", "apse1-az3"], + "ap-southeast-2": ["apse2-az1", "apse2-az2", "apse2-az3"], + "ap-northeast-1": ["apne1-az1", "apne1-az2", "apne1-az4"], + "ca-central-1": ["cac1-az1", "cac1-az2"], + "eu-central-1": ["euc1-az1", "euc1-az2", "euc1-az3"], + "eu-west-1": ["euw1-az1", "euw1-az2", "euw1-az3"], + "eu-west-2": ["euw2-az1", "euw2-az2", "euw2-az3"], + "eu-west-3": ["euw3-az1", "euw3-az3"], + "eu-north-1": ["eun1-az1", "eun1-az2", "eun1-az3"], + "me-south-1": ["mes1-az1", "mes1-az2", "mes1-az3"], + "sa-east-1": ["sae1-az1", "sae1-az2", "sae1-az3"], + "us-gov-west-1": ["usgw1-az1", "usgw1-az2", "usgw1-az3"] +} + +export const scorpiobroker_sqs_object = { + "SCORPIO_TOPICS_ENTITY": `garnet-scorpiobroker-entity`, + "SCORPIO_TOPICS_ENTITYBATCH": `garnet-scorpiobroker-entitybatch`, + "SCORPIO_TOPICS_REGISTRY": `garnet-scorpiobroker-registry`, + "SCORPIO_TOPICS_TEMPORAL": `garnet-scorpiobroker-temporal`, + "SCORPIO_TOPICS_INTERNALNOTIFICATION": `garnet-scorpiobroker-internalnotification`, + "SCORPIO_TOPICS_INTERNALREGSUB": `garnet-scorpiobroker-internalregsub`, + "SCORPIO_TOPICS_SUBALIVE": `garnet-scorpiobroker-subalive`, + "SCORPIO_TOPICS_SUBSYNC": `garnet-scorpiobroker-subsync`, + "SCORPIO_TOPICS_REGSUBALIVE": `garnet-scorpiobroker-regsubalive`, + "SCORPIO_TOPICS_REGSUBSYNC": `garnet-scorpiobroker-regsubsync` +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/garnet-constructs.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/garnet-constructs.ts new file mode 100644 index 0000000..2ea74d5 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/garnet-constructs.ts @@ -0,0 +1,38 @@ +import { NestedStack, NestedStackProps } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { GarnetSecret } from "./secret"; +import { GarnetNetworking } from "./networking"; +import { Utils } from "./utils"; +import { Vpc } from "aws-cdk-lib/aws-ec2"; +import { Secret } from "aws-cdk-lib/aws-secretsmanager"; +import { GarnetPrivateSub } from "./privatesub"; +import { GarnetBucket } from "./bucket"; + +export class GarnetConstructs extends NestedStack { + public readonly vpc: Vpc + public readonly secret: Secret + public readonly bucket_name: string + public readonly private_sub_endpoint: string + constructor(scope: Construct, id: string, props?: NestedStackProps) { + super(scope, id, props); + + const utils_construct = new Utils(this, "Utils") + const bucket_construct = new GarnetBucket(this, 'Bucket', {}) + const secret_construct = new GarnetSecret(this, "Secret", {}) + const networking_construct = new GarnetNetworking(this, "Networking", { + az1: utils_construct.az1, + az2: utils_construct.az2 + }) + + networking_construct.node.addDependency(utils_construct) + + const garnet_private_sub = new GarnetPrivateSub(this, 'PrivateSub', { + vpc: networking_construct.vpc + }) + + this.private_sub_endpoint = garnet_private_sub.private_sub_endpoint + this.bucket_name = bucket_construct.bucket_name + this.vpc = networking_construct.vpc + this.secret = secret_construct.secret + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/networking/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/networking/index.ts new file mode 100644 index 0000000..f55040d --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/networking/index.ts @@ -0,0 +1,42 @@ +import { CfnSubnet, Port, SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2" +import { Construct } from "constructs" +import { Parameters } from "../../../../parameters" +import { Aws, CfnOutput, Fn, Lazy, Names, Stack } from "aws-cdk-lib"; + +export interface GarnetNetworkingProps { + az1: string, + az2: string +} + +export class GarnetNetworking extends Construct { + public readonly vpc: Vpc + constructor(scope: Construct, id: string, props: GarnetNetworkingProps) { + super(scope, id) + const stack = Stack.of(this) + let broker_id = Parameters.garnet_broker + // VPC + const vpc = new Vpc(this, `VpcGarnet${broker_id}`, { + natGateways: 1, + availabilityZones: [`${props.az1}`,`${props.az2}`], + vpcName: `garnet-vpc-${broker_id.toLowerCase()}-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + subnetConfiguration: [ + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: `garnet-subnet-egress-${broker_id.toLowerCase()}`, + + }, + { + subnetType: SubnetType.PRIVATE_ISOLATED, + name: `garnet-subnet-isolated-${broker_id.toLowerCase()}`, + }, + { + subnetType: SubnetType.PUBLIC, + name: `garnet-subnet-public-${broker_id.toLowerCase()}`, + }, + ], + }) + + + this.vpc = vpc; + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/index.ts new file mode 100644 index 0000000..dd7f0ba --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/index.ts @@ -0,0 +1,98 @@ +import { Aws, CfnOutput, Duration, Names } from "aws-cdk-lib" +import { EndpointType, LambdaRestApi } from "aws-cdk-lib/aws-apigateway" +import { InterfaceVpcEndpoint, Peer, Port, SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2" +import { AnyPrincipal, Effect, PolicyDocument, PolicyStatement } from "aws-cdk-lib/aws-iam" +import { Architecture, Code, Function, Runtime } from "aws-cdk-lib/aws-lambda" +import { Construct } from "constructs" + +export interface GarnetPrivateSubProps { + vpc: Vpc + } + + export class GarnetPrivateSub extends Construct { + + public readonly private_sub_endpoint: string + + constructor(scope: Construct, id: string, props: GarnetPrivateSubProps) { + super(scope, id) + + // SECURITY GROUP + const sg_garnet_vpc_endpoint = new SecurityGroup(this, 'PrivateSubSecurityGroup', { + securityGroupName: `garnet-private-sub-endpoint-sg-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + vpc: props.vpc, + allowAllOutbound: true + }) + sg_garnet_vpc_endpoint.addIngressRule(Peer.anyIpv4(), Port.tcp(443)) + + // VPC ENDPOINT + const vpc_endpoint = new InterfaceVpcEndpoint(this, 'GarnetPrivateSubEndpoint', { + vpc: props.vpc, + service: { + name: `com.amazonaws.${Aws.REGION}.execute-api`, + port: 443 + }, + privateDnsEnabled: true, + securityGroups: [sg_garnet_vpc_endpoint] + }) + + // LAMBDA + const lambda_garnet_private_sub_path = `${__dirname}/lambda/garnetSub` + const lambda_garnet_private_sub = new Function(this, 'GarnetSubFunction', { + functionName: `garnet-private-sub-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_garnet_private_sub_path), + handler: 'index.handler', + timeout: Duration.seconds(50), + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION + } + }) + + lambda_garnet_private_sub.addToRolePolicy(new PolicyStatement({ + actions: ["iot:Publish"], + resources: [`arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:topic/garnet/subscriptions/*`] + })) + + // POLICY + const api_policy = new PolicyDocument({ + statements: [ + new PolicyStatement({ + principals: [new AnyPrincipal], + actions: ['execute-api:Invoke'], + resources: ['execute-api:/*'], + effect: Effect.DENY, + conditions: { + StringNotEquals: { + "aws:SourceVpce": vpc_endpoint.vpcEndpointId + } + } + }), + new PolicyStatement({ + principals: [new AnyPrincipal], + actions: ['execute-api:Invoke'], + resources: ['execute-api:/*'], + effect: Effect.ALLOW + }) + ] + }) + + + const api_private_sub = new LambdaRestApi(this, 'ApiPrivateSub', { + restApiName:'garnet-private-sub-endpoint-api', + endpointTypes: [EndpointType.PRIVATE], + handler: lambda_garnet_private_sub, + policy: api_policy + }) + + this.private_sub_endpoint = api_private_sub.url + + new CfnOutput(this, 'ApiEndpoint', { + value: api_private_sub.url, + description: 'Private API Endpoint for Subscriptions' + }) + + + + } + } \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/lambda/garnetSub/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/lambda/garnetSub/index.js new file mode 100644 index 0000000..d61bcd4 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/privatesub/lambda/garnetSub/index.js @@ -0,0 +1,56 @@ +const iot_region = process.env.AWSIOTREGION +const { IoTDataPlaneClient, PublishCommand } = require("@aws-sdk/client-iot-data-plane") +const iotdata = new IoTDataPlaneClient({region: iot_region}) + +exports.handler = async (event) => { + try { + const {body} = event + if(!body){ + return { + statusCode: 400, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: 'Bad Request. Notification is the only type valid'}) + } + } + const payload = JSON.parse(body) + if(payload?.type != "Notification") { + console.log('ERROR not Notification') + return { + statusCode: 400, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: 'Bad Request. Notification is the only type valid'}) + } + } + // GET THE SUBSCRIPTION NAME FROM SUBSCRIPTION ID + const subName = `${payload.subscriptionId.split(':').slice(-1)}` + const publish = await iotdata.send( + new PublishCommand({ + topic: `garnet/subscriptions/${subName}`, + payload: JSON.stringify(payload) + }) + ) + + const response = { + statusCode: 200 + } + return response + + } catch (e) { + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + + } + + +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/secret/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/secret/index.ts new file mode 100644 index 0000000..f0c2844 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/secret/index.ts @@ -0,0 +1,31 @@ +import { NestedStack, NestedStackProps } from "aws-cdk-lib" +import { Secret } from "aws-cdk-lib/aws-secretsmanager" +import { Construct } from "constructs" +import { Parameters } from "../../../../parameters" + + +export interface GarnetSecretProps { +} + +export class GarnetSecret extends Construct { + public readonly secret: Secret + + constructor(scope: Construct, id: string, props: GarnetSecretProps) { + super(scope, id) + + this.secret = new Secret(this, 'Secret', { + secretName:`garnet/secret/${Parameters.garnet_broker.toLowerCase()}/${this.node.addr.slice(-8)}`, + generateSecretString: { + secretStringTemplate: JSON.stringify({ + username: 'garnetadmin', + }), + excludePunctuation: true, + excludeCharacters: "/Â¥'%:;{}", + includeSpace: false, + generateStringKey: 'password' + } + }) + + } + +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/index.ts new file mode 100644 index 0000000..7cfe1f3 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/index.ts @@ -0,0 +1,90 @@ +import { Construct } from "constructs"; +import { azlist, scorpiobroker_sqs_object } from "../constants" +import { Aws, CfnOutput, CustomResource, Duration, Names, Stack } from "aws-cdk-lib"; +import { Code, Runtime, Function, Architecture } from "aws-cdk-lib/aws-lambda"; +import { PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Provider } from "aws-cdk-lib/custom-resources"; +import { Parameters } from "../../../../parameters"; + +export interface GarnetUtilProps {} + +export class Utils extends Construct { + + public readonly az1: string + public readonly az2: string + + constructor(scope: Construct, id: string, props?: GarnetUtilProps) { + super(scope, id) + + if(Stack.of(this).region.startsWith('$')){ + throw new Error('Please type a valid region in the parameter.ts file') + } + + if(!azlist[`${Stack.of(this).region}`]){ + throw new Error('The stack is not yet available in the region selected') + } + + const compatible_azs = azlist[`${Stack.of(this).region}`] + + const get_az_func_path = `${__dirname}/lambda/getAzs` + const get_az_func = new Function(this, 'AzFunction', { + functionName: `garnet-utils-az-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(get_az_func_path), + handler: 'index.handler', + timeout: Duration.seconds(50), + architecture: Architecture.ARM_64, + environment: { + COMPATIBLE_AZS: JSON.stringify(compatible_azs) + } + }) + get_az_func.addToRolePolicy(new PolicyStatement({ + actions: ["ec2:DescribeAvailabilityZones"], + resources: ['*'] + })) + + const get_az_provider = new Provider(this, 'getAzCleanUpprovider', { + onEventHandler: get_az_func + }) + + const get_az = new CustomResource(this, 'getAzCustomResource', { + serviceToken: get_az_provider.serviceToken + }) + + this.az1 = get_az.getAtt('az1').toString() + this.az2 = get_az.getAtt('az2').toString() + + + if(Parameters.garnet_broker == 'Scorpio'){ + + let sqs_urls = Object.values(scorpiobroker_sqs_object).map(q => `https://sqs.${Aws.REGION}.amazonaws.com/${Aws.ACCOUNT_ID}/${q}`) + + const scorpio_sqs_lambda_path = `${__dirname}/lambda/scorpioSqs` + const scorpio_sqs_lambda = new Function(this, 'ScorpioSqsFunction', { + functionName: `garnet-utils-scorpio-sqs-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(scorpio_sqs_lambda_path), + handler: 'index.handler', + timeout: Duration.seconds(50), + architecture: Architecture.ARM_64, + environment: { + SQS_QUEUES: JSON.stringify(sqs_urls) + } + }) + scorpio_sqs_lambda.addToRolePolicy(new PolicyStatement({ + actions: ["sqs:DeleteQueue"], + resources: [`arn:aws:sqs:${Aws.REGION}:${Aws.ACCOUNT_ID}:garnet-scorpiobroker-*`] + })) + + const scorpio_sqs_provider = new Provider(this, 'scorpioSqsProvider', { + onEventHandler: scorpio_sqs_lambda + }) + + const scorpio_sqs_resource = new CustomResource(this, 'scorpioSqsCustomResource', { + serviceToken: scorpio_sqs_provider.serviceToken + }) + + } + + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/getAzs/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/getAzs/index.js new file mode 100644 index 0000000..cd0b5db --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/getAzs/index.js @@ -0,0 +1,23 @@ +const { EC2Client, DescribeAvailabilityZonesCommand } = require("@aws-sdk/client-ec2") +const ec2 = new EC2Client({apiVersion: '2016-11-15'}) +const compatible_azs = JSON.parse(process.env.COMPATIBLE_AZS) + +exports.handler = async (event) => { + console.log(event) + let request_type = event['RequestType'] + if (request_type=='Create' || request_type == 'Update') { + const {AvailabilityZones} = await ec2.send( + new DescribeAvailabilityZonesCommand({}) + ) + console.log(AvailabilityZones) + let final_azs = AvailabilityZones.filter((az) => compatible_azs.includes(az.ZoneId)).map((az) => az.ZoneName) + console.log({final_azs}) + console.log({compatible_azs}) + return { + Data: { + az1: final_azs[0], + az2: final_azs[final_azs.length - 1] + } + } + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/scorpioSqs/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/scorpioSqs/index.js new file mode 100644 index 0000000..966a00a --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-constructs/utils/lambda/scorpioSqs/index.js @@ -0,0 +1,25 @@ +const { SQSClient, DeleteQueueCommand } = require("@aws-sdk/client-sqs") +const sqs = new SQSClient({}) +const SQS_QUEUES = JSON.parse(process.env.SQS_QUEUES) + +exports.handler = async (event) => { + console.log(event) + let request_type = event['RequestType'].toLowerCase() + if (request_type=='delete') { + + try { + for await (let queue of Object.values(SQS_QUEUES)){ + await sqs.send( + new DeleteQueueCommand({ + QueueUrl: queue + }) + ) + + } + + } catch (e) { + console.log(e) + } + return true + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/index.ts new file mode 100644 index 0000000..4402272 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/index.ts @@ -0,0 +1,413 @@ +import { Aws, Duration, Names } from "aws-cdk-lib" + +import { CfnIntegration, CfnRoute, CfnVpcLink } from "aws-cdk-lib/aws-apigatewayv2" +import { SubnetType, Vpc } from "aws-cdk-lib/aws-ec2" +import { PolicyStatement } from "aws-cdk-lib/aws-iam" +import { Runtime, Function, Code, CfnPermission, LayerVersion, Architecture } from "aws-cdk-lib/aws-lambda" +import { RetentionDays } from "aws-cdk-lib/aws-logs" +import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources" +import { Construct } from "constructs" +import { Parameters } from "../../../../parameters" + + +export interface GarnetIotApiProps { + readonly api_ref: string, + readonly vpc: Vpc, + dns_context_broker: string +} + +export class GarnetIotApi extends Construct { + + constructor(scope: Construct, id: string, props: GarnetIotApiProps){ + super(scope, id) + + + // LAMBDA LAYER (SHARED LIBRARIES) + const layer_lambda_path = `./lib/stacks/garnet-iot/layers` + const layer_lambda = new LayerVersion(this, 'LayerLambda', { + code: Code.fromAsset(layer_lambda_path), + compatibleRuntimes: [Runtime.NODEJS_18_X] + }) + + +// ********************************************** + + /** + * GARNET VERSION + */ + + // LAMBDA GARNET API VERSION + const lambda_garnet_version_path = `${__dirname}/lambda/garnetVersion` + const lambda_garnet_version = new Function(this, 'LambdaGarnetVersion', { + functionName: `garnet-api-version-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_garnet_version_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + + environment: { + CONTEXT_BROKER: Parameters.garnet_broker, + GARNET_VERSION: Parameters.garnet_version + } + }) + + const garnet_version_integration = new CfnIntegration(this, 'GarnetVersionIntegration', { + apiId: props.api_ref, + integrationMethod: "GET", + integrationType: "AWS_PROXY", + integrationUri: lambda_garnet_version.functionArn, + connectionType: "INTERNET", + description: "GARNET VERSION INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const garnet_version_route = new CfnRoute(this, 'GarnetVersionRoute', { + apiId: props.api_ref, + routeKey: "GET /", + target: `integrations/${garnet_version_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionGarnetVersion', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_garnet_version.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /** + * END GARNET VERSION + */ + + +// ********************************************** + + + +// ********************************************** + + /** + * POST THING + */ + + // LAMBDA THAT POSTS THING + const lambda_post_thing_path = `${__dirname}/lambda/postThing` + const lambda_post_thing = new Function(this, 'LambdaPostThing', { + functionName: `garnet-iot-api-post-thing-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_post_thing_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix, + } + }) + + lambda_post_thing.addToRolePolicy(new PolicyStatement({ + actions: [ + "iot:UpdateThingGroup", + "iot:CreateThingGroup", + "iot:CreateThing", + "iot:AddThingToThingGroup", + "iot:UpdateThingGroupsForThing", + "iot:UpdateThingShadow" + ], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thinggroup/*`, + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*` + ] + })) + + const post_thing_integration = new CfnIntegration(this, 'postThingIntegration', { + apiId: props.api_ref, + integrationMethod: "POST", + integrationType: "AWS_PROXY", + integrationUri: lambda_post_thing.functionArn, + connectionType: "INTERNET", + description: "POST THING INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const post_thing_route = new CfnRoute(this, 'PostThingRoute', { + apiId: props.api_ref, + routeKey: "POST /iot/things", + target: `integrations/${post_thing_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionPostThing', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_post_thing.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /** + * END POST THING + */ + + +// ********************************************** + + /** + * DELETE THING + */ + + // LAMBDA THAT DELETE THING + const lambda_delete_thing_path = `${__dirname}/lambda/deleteThing` + const lambda_delete_thing = new Function(this, 'LambdaDeleteThing', { + functionName: `garnet-iot-api-delete-thing-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + vpc: props.vpc, + vpcSubnets: { + subnetType: SubnetType.PRIVATE_WITH_EGRESS + }, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_delete_thing_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix, + DNS_CONTEXT_BROKER: props.dns_context_broker + } + }) + + lambda_delete_thing.addToRolePolicy(new PolicyStatement({ + actions: [ + "iot:DeleteThing", + "iot:DeleteThingShadow", + "iot:ListNamedShadowsForThing", + + ], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*` + ] + })) + + const delete_thing_integration = new CfnIntegration(this, 'deleteThingIntegration', { + apiId: props.api_ref, + integrationMethod: "DELETE", + integrationType: "AWS_PROXY", + integrationUri: lambda_delete_thing.functionArn, + connectionType: "INTERNET", + description: "DELETE THING INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const delete_thing_route = new CfnRoute(this, 'DeleteThingRoute', { + apiId: props.api_ref, + routeKey: "DELETE /iot/things/{thingName}", + target: `integrations/${delete_thing_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionDeleteThing', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_delete_thing.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /*** + * END DELETE THING + */ + +/************************************************************************** */ + + + /** + * GET THING + */ + + // LAMBDA THAT GETS THING + const lambda_get_thing_path = `${__dirname}/lambda/getThing` + const lambda_get_thing = new Function(this, 'LambdaGetThing', { + functionName: `garnet-iot-api-get-thing-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_get_thing_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix, + } + }) + + lambda_get_thing.addToRolePolicy(new PolicyStatement({ + actions: [ + "iot:GetThing", + "iot:listNamedShadowsForThing", + "iot:getThingShadow" + ], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*` + ] + })) + + const get_thing_integration = new CfnIntegration(this, 'getThingIntegration', { + apiId: props.api_ref, + integrationMethod: "GET", + integrationType: "AWS_PROXY", + integrationUri: lambda_get_thing.functionArn, + connectionType: "INTERNET", + description: "GET THING INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const get_thing_route = new CfnRoute(this, 'GetThingRoute', { + apiId: props.api_ref, + routeKey: "GET /iot/things/{thingName}", + target: `integrations/${get_thing_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionGetThing', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_get_thing.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /** + * END GET THING + */ + + /************************************************************************** */ + + + /** + * GET THINGS + */ + + // LAMBDA THAT GETS THING + const lambda_get_things_path = `${__dirname}/lambda/getThings` + const lambda_get_things = new Function(this, 'LambdaGetThings', { + functionName: `garnet-iot-api-get-things-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_get_things_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix, + } + }) + + lambda_get_things.addToRolePolicy(new PolicyStatement({ + actions: [ + "iot:searchIndex" + ], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:index/*` + ] + })) + + const get_things_integration = new CfnIntegration(this, 'getThingsIntegration', { + apiId: props.api_ref, + integrationMethod: "GET", + integrationType: "AWS_PROXY", + integrationUri: lambda_get_things.functionArn, + connectionType: "INTERNET", + description: "GET THINGS INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const get_things_route = new CfnRoute(this, 'GetThingsRoute', { + apiId: props.api_ref, + routeKey: "GET /iot/things", + target: `integrations/${get_things_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionGetThings', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_get_things.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /** + * END GET THINGS + */ + + + + /************************************************************************** */ + + + /** + * POST SHADOWS + */ + + // LAMBDA THAT POST SHADOWS + const lambda_post_shadows_path = `${__dirname}/lambda/postShadows` + const lambda_post_shadows = new Function(this, 'LambdaPostShadows', { + functionName: `garnet-iot-api-post-shadows-lambda-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_post_shadows_path), + handler: 'index.handler', + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix, + } + }) + + lambda_post_shadows.addToRolePolicy(new PolicyStatement({ + actions: [ + "iot:UpdateThingShadow" + ], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*` + ] + })) + + const post_shadows_integration = new CfnIntegration(this, 'postShadowsIntegration', { + apiId: props.api_ref, + integrationMethod: "POST", + integrationType: "AWS_PROXY", + integrationUri: lambda_post_shadows.functionArn, + connectionType: "INTERNET", + description: "GET THING INTEGRATION", + payloadFormatVersion: "1.0", + }) + + const post_shadows_route = new CfnRoute(this, 'PostShadowsRoute', { + apiId: props.api_ref, + routeKey: "POST /iot/things/{thingName}/shadows", + target: `integrations/${post_shadows_integration.ref}` + }) + + new CfnPermission(this, 'ApiGatewayLambdaPermissionPostShadows', { + principal: `apigateway.amazonaws.com`, + action: 'lambda:InvokeFunction', + functionName: lambda_post_shadows.functionName, + sourceArn: `arn:aws:execute-api:${Aws.REGION}:${Aws.ACCOUNT_ID}:${props.api_ref}/*/*/*` + }) + + /** + * END POST SHADOWS + */ + + + + + + + + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/deleteThing/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/deleteThing/index.js new file mode 100644 index 0000000..554eb18 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/deleteThing/index.js @@ -0,0 +1,80 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX +const dns_broker = `http://${process.env.DNS_CONTEXT_BROKER}/ngsi-ld/v1` + +const axios = require('axios') +const { IoTDataPlaneClient, DeleteThingShadowCommand,ListNamedShadowsForThingCommand } = require("@aws-sdk/client-iot-data-plane") +const { IoTClient, DeleteThingCommand} = require("@aws-sdk/client-iot") +const iot = new IoTClient({region: iot_region}) +const iotdata = new IoTDataPlaneClient({region: iot_region}) + +exports.handler = async (event) => { + + try { + const {pathParameters: {thingName}, headers, queryStringParameters} = event + + console.log(queryStringParameters?.['recursive']) + if(!thingName) { + throw new Error('thingName is required') + } + if (queryStringParameters?.['recursive'] == 'false'){ + + try { + await iotdata.send( + new DeleteThingShadowCommand({ + thingName, shadowName: `${shadow_prefix}-Device` + }) + ) + let delete_entity = await axios.delete(`${dns_broker}/entities/urn:ngsi-ld:Device:${thingName}`) + } catch (e) { + console.log(e.message) + } + } else { + const {results} = await iotdata.send( + new ListNamedShadowsForThingCommand({thingName}) + ) + for await (let shadowName of results){ + if(shadowName.startsWith(shadow_prefix)){ + try { + await iotdata.send( + new DeleteThingShadowCommand({ thingName, shadowName }) + ) + let delete_entity = await axios.delete(`${dns_broker}/entities/urn:ngsi-ld:${shadowName.split(shadow_prefix)[1].split('-')[1]}:${thingName}`) + } catch (e) { + console.log(e.message) + } + + } + } + } + await iot.send( + new DeleteThingCommand({thingName}) + ) + let add = 'and all its associated entities' + if(queryStringParameters?.['recursive'] == 'false'){ + add ='' + } + + let msg = `Successfully deleted the thing ${thingName} ${add}` + + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: msg}), + } + return response + } + catch(e){ + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/garnetVersion/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/garnetVersion/index.js new file mode 100644 index 0000000..f496fc3 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/garnetVersion/index.js @@ -0,0 +1,60 @@ +const axios = require('axios') +const CONTEXT_BROKER = process.env.CONTEXT_BROKER +const GARNET_VERSION = process.env.GARNET_VERSION + + +exports.handler = async (event) => { + + try { + + const {headers : {Host}} = event + let path + if(CONTEXT_BROKER == "Orion"){ + path = `ngsi-ld/ex/v1/version` + } else if (CONTEXT_BROKER == "Scorpio") { + path = `/q/info` + } else { + throw new Error(`${CONTEXT_BROKER} is an invalid value for Context Broker`) + } + let url = `https://${Host}/${path}` + console.log(url) + let dt =null + try { + let {data} = await axios.get(url) + dt= data + } catch (e) { + console.log(e) + } + + + let result = { + "garnet_version": GARNET_VERSION, + "context_broker": CONTEXT_BROKER, + "context_broker_info": dt + } + + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(result), + } + console.log(response) + + return response + + + } catch (e) { + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } + +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThing/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThing/index.js new file mode 100644 index 0000000..6f313bb --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThing/index.js @@ -0,0 +1,83 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX + +const { IoTDataPlaneClient, ListNamedShadowsForThingCommand, GetThingShadowCommand } = require("@aws-sdk/client-iot-data-plane") +const { IoTClient, DeleteThingCommand} = require("@aws-sdk/client-iot") +const iot = new IoTClient({region: iot_region}) +const iotdata = new IoTDataPlaneClient({region: iot_region}) +const { toUtf8 } = require('@aws-sdk/util-utf8-browser') + +exports.handler = async (event) => { + + try { + const {pathParameters: {thingName}, headers, queryStringParameters} = event + + let shadows = null + if(!thingName) { + throw new Error('thingName is required') + } + if(queryStringParameters && 'shadows' in queryStringParameters) { + shadows = queryStringParameters['shadows'].split(',').map((shadow) => `${shadow_prefix}-${shadow.trim()}`) + + } + console.log({queryStringParameters}) + + let {results} = await iotdata.send( + new ListNamedShadowsForThingCommand({ thingName }) + ) + + if(!results.includes(`${shadow_prefix}-Device`)) { + return { + statusCode: 404, + body: JSON.stringify({ + message: `${thingName} is not registered in the Garnet IoT registry.` + }) + } + } + console.log({results}) + if(shadows){ + results = results.filter(result => shadows.includes(result)) + } + + console.log({shadows}) + console.log({results}) + + let result = { + thingName, + entities: [] + } + + for await (let shadowName of results) { + + let {payload} = await iotdata.send( + new GetThingShadowCommand({ thingName, shadowName}) + ) + + result.entities.push( + JSON.parse( toUtf8(payload) ).state.reported + ) + } + + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(result), + } + console.log(response) + + return response + + } catch(e){ + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThings/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThings/index.js new file mode 100644 index 0000000..a69f0ae --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/getThings/index.js @@ -0,0 +1,85 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX + +const { IoTClient, SearchIndexCommand} = require("@aws-sdk/client-iot") +const iot = new IoTClient({region: iot_region}) + + + +exports.handler = async (event) => { + + try { + let { headers, queryStringParameters} = event + + let index = 'Device' + + if(queryStringParameters && queryStringParameters['index']) { + index = queryStringParameters['index'] + } + + // lower case headers + headers = Object.keys(headers).reduce( (acc, key) => { + acc[key.toLowerCase()] = headers[key] + return acc + }, {}) + + let nToken = headers?.['nexttoken'] ? headers['nexttoken'] : null + + let {things, nextToken} = await iot.send( + new SearchIndexCommand({ + nextToken: nToken, + queryString: `thingName:*`, + indexName: 'AWS_Things' + }) + ) + + let tgs = things.reduce((prev, curr) => { + return [ ...prev, + { thingName: curr.thingName, + thingGroupNames: curr.thingGroupNames, + thingTypeName: curr.thingTypeName, + entity: {...curr?.shadow?.name[`${shadow_prefix}-${index}`]?.reported} + } + ] + }, []) + + + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + nextToken, + things: tgs + }), + } + + + return response + + } catch(e){ + + if(e.statusCode == 400){ + let statusCode = e.statusCode ? e.statusCode : 500 + const response = { + statusCode, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: `Error with the submitted index. Please verify it is a valid index and registered in the Garnet IoT Index`}), + } + } + + let statusCode = e.statusCode ? e.statusCode : 500 + const response = { + statusCode, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postShadows/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postShadows/index.js new file mode 100644 index 0000000..4637aca --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postShadows/index.js @@ -0,0 +1,77 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX + +const { IoTDataPlaneClient, UpdateThingShadowCommand } = require("@aws-sdk/client-iot-data-plane") +const iotdata = new IoTDataPlaneClient({region: iot_region}) + +exports.handler = async (event) => { + + try{ + try { JSON.parse(event.body) } catch(e){throw new Error('Invalid body property. The body must be a JSON object. Check the Content-Type in your header')} + + const {body, pathParameters: {thingName}} = event + + if(!thingName) { + throw new Error('The thingName is required') + } + + if(!body){ + throw new Error('An NGSI-LD entity is required in the body') + } + + let entity = JSON.parse(body) + console.log({entity}) + + if(!entity.id || !entity.type) { + throw new Error('id and type are required') + } + + const entity_thingName = `${entity.id.split(':').slice(-1)}` + + if(entity_thingName != thingName){ + throw new Error(`The name ${entity_thingName} extracted from the id of the entity does not match ${thingName}`) + } + if(entity.type == 'Device') { + throw new Error(`Type Device is not a valid type for this operation`) + } + if(!entity.id.startsWith(`urn:ngsi-ld:${entity.type}:`)){ + throw new Error(`Invalid id. The id must start with urn:ngsi-ld:${entity.type}: following with thingName`) + } + + + const shadow_payload = { + state: { + reported: entity + } + } + + let updateThingShadow = await iotdata.send( + new UpdateThingShadowCommand({payload: JSON.stringify(shadow_payload ), thingName: thingName, shadowName: `${shadow_prefix}-${entity.type}`}) + ) + + let msg = `The entity ${entity.id} successfully registered as ${shadow_prefix}-${entity.type} for ${thingName}` + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: msg}), + } + return response + + } catch(e){ + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } + + + + +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postThing/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postThing/index.js new file mode 100644 index 0000000..b4feced --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-api/lambda/postThing/index.js @@ -0,0 +1,106 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX + +const { IoTDataPlaneClient, UpdateThingShadowCommand } = require("@aws-sdk/client-iot-data-plane") +const { IoTClient, CreateThingCommand, CreateThingGroupCommand, AddThingToThingGroupCommand } = require("@aws-sdk/client-iot") +const iot = new IoTClient({region: iot_region}) +const iotdata = new IoTDataPlaneClient({region: iot_region}) + +exports.handler = async (event) => { + + try{ + + try { JSON.parse(event.body) } catch(e){throw new Error('Invalid body property. The body must be a JSON object. Check the Content-Type in your header')} + + const body = JSON.parse(event.body) + let payload = body + let groups = body.thingGroups?.value + if (groups) { + if(!Array.isArray(groups)) { + throw new Error('Invalid thingGroups property. The type must be property and value must be an array') + } + + if(groups.length > 10){ + throw new Error('A thing can be added to a maximum of 10 thing groups') + } + } + + if(!payload.id || !payload.type || !payload.location) { + throw new Error('id, type and location are required') + } + + if(payload.type != 'Device'){ + throw new Error('Invalid type. The value of type must be Device and compliant with Smart Data Models - Device') + } + + if(!payload.id.startsWith('urn:ngsi-ld:Device:')){ + throw new Error('Invalid id. The id must start with urn:ngsi-ld:Device: following with thingName') + } + + // Get the thingName from the id + const thingName = `${payload.id.split(':').slice(-1)}` + + if(!thingName){ + throw new Error('Invalid thingName') + } + + // Create the thing + const thing_in_registry = await iot.send( + new CreateThingCommand({ thingName: thingName }) + ) + + // if a group is specified + if(groups && groups.length > 0){ + for await (let group of groups){ + const thing_group = await iot.send( + new CreateThingGroupCommand({thingGroupName: group}) + ) + const thing_in_group = await iot.send( + new AddThingToThingGroupCommand({thingName: thingName, thingGroupName: group}) + ) + } + } + + console.log(thing_in_registry) + + delete payload.thingGroups + + const shadow_payload = { + state: { + reported: payload + } + } + let updateThingShadow = await iotdata.send( + new UpdateThingShadowCommand({payload: JSON.stringify(shadow_payload ), thingName: thingName, shadowName: `${shadow_prefix}-${payload.type}`}) + ) + + let msg = `${thingName} successfully registered` + if (groups) { + msg = `${msg} and added to ${JSON.stringify(groups)}` + } + + const response = { + statusCode: 200, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: msg}), + } + return response + + } catch(e){ + const response = { + statusCode: 500, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({message: e.message}), + } + console.log(e) + return response + } + + + + +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/index.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/index.ts new file mode 100644 index 0000000..7eddae8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/index.ts @@ -0,0 +1,317 @@ +import { Aws, Duration, Names } from "aws-cdk-lib"; +import { SubnetType, Vpc } from "aws-cdk-lib/aws-ec2"; +import { + ManagedPolicy, + PolicyStatement, + Role, + ServicePrincipal, +} from "aws-cdk-lib/aws-iam"; +import { CfnTopicRule } from "aws-cdk-lib/aws-iot"; +import { CfnDeliveryStream } from "aws-cdk-lib/aws-kinesisfirehose"; +import { + Code, + LayerVersion, + Runtime, + Function, + Architecture, +} from "aws-cdk-lib/aws-lambda"; +import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; +import { RetentionDays } from "aws-cdk-lib/aws-logs"; +import { Bucket } from "aws-cdk-lib/aws-s3"; +import { Topic } from "aws-cdk-lib/aws-sns"; +import { SqsSubscription } from "aws-cdk-lib/aws-sns-subscriptions"; +import { Queue } from "aws-cdk-lib/aws-sqs"; +import { + AwsCustomResource, + AwsCustomResourcePolicy, + PhysicalResourceId, +} from "aws-cdk-lib/custom-resources"; +import { Construct } from "constructs"; +import { Parameters } from "../../../../parameters"; + +export interface GarnetIotprops { + dns_context_broker: string; + vpc: Vpc; + bucket_arn: string; +} + +export class GarnetIot extends Construct { + public readonly sqs_garnet_iot_arn: string; + public readonly sns_garnet_iot: Topic; + + constructor(scope: Construct, id: string, props: GarnetIotprops) { + super(scope, id); + + //CHECK PROPS + if (!props.vpc) { + throw new Error( + "The property vpc is required to create an instance of GarnetIot Construct" + ); + } + if (!props.dns_context_broker) { + throw new Error( + "The property dns_context_broker is required to create an instance of GarnetIot Construct" + ); + } + if (!props.bucket_arn) { + throw new Error( + "The property bucket_arn is required to create an instance of GarnetIot Construct" + ); + } + + // IoT DATALAKE BUCKET + const bucket = Bucket.fromBucketArn(this, "IoTBucket", props.bucket_arn); + + // LAMBDA LAYER (SHARED LIBRARIES) + const layer_lambda_path = `./lib/stacks/garnet-iot/layers`; + const layer_lambda = new LayerVersion(this, "LayerLambda", { + code: Code.fromAsset(layer_lambda_path), + compatibleRuntimes: [Runtime.NODEJS_18_X], + }); + + // SQS ENTRY POINT + const sqs_garnet_endpoint = new Queue(this, "SqsGarnetIot", { + queueName: `garnet-iot-queue-${Aws.REGION}`, + }); + this.sqs_garnet_iot_arn = sqs_garnet_endpoint.queueArn; + + // LAMBDA TO UPDATE DEVICE SHADOW + const lambda_update_shadow_path = `${__dirname}/lambda/updateShadow`; + const lambda_update_shadow = new Function(this, "LambdaUpdateShadow", { + functionName: `garnet-iot-update-shadow-lambda-${Names.uniqueId(this) + .slice(-8) + .toLowerCase()}`, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_update_shadow_path), + handler: "index.handler", + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + architecture: Architecture.ARM_64, + environment: { + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix + }, + }); + + // ADD PERMISSION FOR LAMBDA THAT UPDATES SHADOW TO ACCESS SQS ENTRY POINT + lambda_update_shadow.addToRolePolicy( + new PolicyStatement({ + actions: [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + resources: [`${sqs_garnet_endpoint.queueArn}`], + }) + ); + + // ADD PERMISSION TO ACCESS AWS IoT DEVICE SHADOW + lambda_update_shadow.addToRolePolicy( + new PolicyStatement({ + actions: ["iot:UpdateThingShadow"], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*/${Parameters.garnet_iot.shadow_prefix}-*`, + ], + }) + ); + + // ADD THE SQS ENTRY POINT AS EVENT SOURCE FOR LAMBDA + lambda_update_shadow.addEventSource( + new SqsEventSource(sqs_garnet_endpoint, { batchSize: 10 }) + ); + + // SQS TO LAMBDA CONTEXT BROKER + const sqs_to_context_broker = new Queue(this, "SqsToLambdaContextBroker", { + queueName: `garnet-iot-contextbroker-queue-${Aws.REGION}` + }); + + // ROLE THAT GRANTS ACCESS TO FIREHOSE TO READ/WRITE BUCKET + const role_firehose = new Role(this, "FirehoseRole", { + assumedBy: new ServicePrincipal("firehose.amazonaws.com"), + }); + bucket.grantReadWrite(role_firehose); + + // KINESIS FIREHOSE DELIVERY STREAM + const kinesis_firehose = new CfnDeliveryStream( + this, + "KinesisFirehoseDeliveryGarnetIotDataLake", + { + deliveryStreamName: `garnet-iot-firehose-stream-${Names.uniqueId(this).slice(-8).toLowerCase()}`, + deliveryStreamType: "DirectPut", + extendedS3DestinationConfiguration: { + bucketArn: bucket.bucketArn, + roleArn: role_firehose.roleArn, + bufferingHints: { + intervalInSeconds: 60, + sizeInMBs: 64, + }, + processingConfiguration: { + enabled: true, + processors: [ + { + type: "MetadataExtraction", + parameters: [ + { + parameterName: "MetadataExtractionQuery", + parameterValue: "{type:.type}", + }, + { + parameterName: "JsonParsingEngine", + parameterValue: "JQ-1.6", + }, + ], + }, + ], + }, + dynamicPartitioningConfiguration: { + enabled: true, + }, + prefix: `type=!{partitionKeyFromQuery:type}/dt=!{timestamp:yyyy}-!{timestamp:MM}-!{timestamp:dd}-!{timestamp:HH}/`, + errorOutputPrefix: `type=!{firehose:error-output-type}/dt=!{timestamp:yyy}-!{timestamp:MM}-!{timestamp:dd}-!{timestamp:HH}/`, + }, + } + ); + + // ROLE THAT GRANT ACCESS TO IOT RULE TO ACTIONS + const iot_rule_actions_role = new Role(this, "RoleGarnetIotRuleIngestion", { + assumedBy: new ServicePrincipal("iot.amazonaws.com"), + }); + iot_rule_actions_role.addToPolicy( + new PolicyStatement({ + resources: [ + `${sqs_to_context_broker.queueArn}`, + `${kinesis_firehose.attrArn}`, + ], + actions: [ + "sqs:SendMessage", + "firehose:DescribeDeliveryStream", + "firehose:ListDeliveryStreams", + "firehose:ListTagsForDeliveryStream", + "firehose:PutRecord", + "firehose:PutRecordBatch", + ], + }) + ); + + // IOT RULE THAT LISTENS TO CHANGES IN GARNET SHADOWS AND PUSH TO SQS + const iot_rule = new CfnTopicRule(this, "IoTRuleShadows", { + ruleName: `garnet_iot_rule_${Names.uniqueId(this).slice(-8).toLowerCase()}`, + topicRulePayload: { + awsIotSqlVersion: "2016-03-23", + ruleDisabled: false, + sql: `SELECT current.state.reported.* + FROM '$aws/things/+/shadow/name/+/update/documents' + WHERE startswith(topic(6), '${Parameters.garnet_iot.shadow_prefix}') + AND NOT isUndefined(current.state.reported.type)`, + actions: [ + { + sqs: { + queueUrl: sqs_to_context_broker.queueUrl, + roleArn: iot_rule_actions_role.roleArn, + }, + }, + { + firehose: { + deliveryStreamName: kinesis_firehose.ref, + roleArn: iot_rule_actions_role.roleArn, + separator: "\n", + }, + }, + ], + }, + }) + + + // IOT RULE THAT LISTENS TO SUBSCRIPTIONS AND PUSH TO FIREHOSE + const iot_rule_sub = new CfnTopicRule(this, "IotRuleSub", { + ruleName: `garnet_subscriptions_rule_${Names.uniqueId(this).slice(-8).toLowerCase()}`, + topicRulePayload: { + awsIotSqlVersion: "2016-03-23", + ruleDisabled: false, + sql: `SELECT * FROM 'garnet/subscriptions/+'`, + actions: [ + { + firehose: { + deliveryStreamName: kinesis_firehose.ref, + roleArn: iot_rule_actions_role.roleArn, + separator: "\n", + }, + }, + ], + }, + }) + + + + + // LAMBDA THAT GETS MESSAGES FROM THE QUEUE AND UPDATES CONTEXT BROKER + const lambda_to_context_broker_path = `${__dirname}/lambda/updateContextBroker`; + const lambda_to_context_broker = new Function( + this, + "LambdaUpdateContextBroker", + { + functionName: `garnet-iot-update-broker-lambda-${Names.uniqueId(this) + .slice(-8) + .toLowerCase()}`, + vpc: props.vpc, + vpcSubnets: { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }, + runtime: Runtime.NODEJS_18_X, + code: Code.fromAsset(lambda_to_context_broker_path), + handler: "index.handler", + timeout: Duration.seconds(15), + logRetention: RetentionDays.THREE_MONTHS, + layers: [layer_lambda], + architecture: Architecture.ARM_64, + environment: { + DNS_CONTEXT_BROKER: props.dns_context_broker, + URL_SMART_DATA_MODEL: Parameters.garnet_iot.smart_data_model_url, + AWSIOTREGION: Aws.REGION, + SHADOW_PREFIX: Parameters.garnet_iot.shadow_prefix + }, + } + ); + + lambda_to_context_broker.addToRolePolicy( + new PolicyStatement({ + actions: [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses", + ], + resources: ["*"], + }) + ); + + // ADD PERMISSION FOR LAMBDA TO ACCESS SQS + lambda_to_context_broker.addToRolePolicy( + new PolicyStatement({ + actions: [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + resources: [`${sqs_to_context_broker.queueArn}`], + }) + ); + + lambda_to_context_broker.addToRolePolicy( + new PolicyStatement({ + actions: ["iot:UpdateThingShadow", "iot:GetThingShadow"], + resources: [ + `arn:aws:iot:${Aws.REGION}:${Aws.ACCOUNT_ID}:thing/*/${Parameters.garnet_iot.shadow_prefix}-*`, + ], + }) + ); + + lambda_to_context_broker.addEventSource( + new SqsEventSource(sqs_to_context_broker, { batchSize: 10 }) + ); + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateContextBroker/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateContextBroker/index.js new file mode 100644 index 0000000..6e8d6e8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateContextBroker/index.js @@ -0,0 +1,89 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX +const dns_broker = `http://${process.env.DNS_CONTEXT_BROKER}/ngsi-ld/v1` +const URL_SMART_DATA_MODEL = process.env.URL_SMART_DATA_MODEL + +const {IoTDataPlaneClient, UpdateThingShadowCommand, GetThingShadowCommand} = require('@aws-sdk/client-iot-data-plane') +const iotdata = new IoTDataPlaneClient({region: iot_region}) + +const axios = require('axios') + + + + +exports.handler = async (event, context) => { + + try { + let entities = [] + + for await (let msg of event.Records){ + let payload = JSON.parse(msg.body) + const thingName = `${payload.id.split(':').slice(-1)}` + if(!payload.id || !payload.type){ + throw new Error('Invalid entity: id or type is missing') + } + + // Check if location property is in the payload. If not, get it from the Garnet-Device named shadow + if(!payload.location && payload.type != 'Device') { + + try { + let {payload : device_shadow} = await iotdata.send( + new GetThingShadowCommand({ + thingName: thingName, + shadowName: `${shadow_prefix}-Device` + }) + ) + + device_shadow = JSON.parse( + new TextDecoder('utf-8').decode(device_shadow) + ) + payload.location = device_shadow.state.reported.location + + if(payload.location){ + const shadow_payload = { + state: { + reported: payload + } + } + let updateThingShadow = await iotdata.send( + new UpdateThingShadowCommand({ + payload: JSON.stringify(shadow_payload), + thingName: thingName, + shadowName: `${shadow_prefix}-${payload.type}` + }) + ) + } + + + + } catch (e) { + console.log(e.message) + } + } + if (payload.raw) delete payload.raw + entities.push(payload) + } + const headers = { + 'Content-Type': 'application/json', + 'Link': `<${URL_SMART_DATA_MODEL}>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"` + } + try { + let upsert = await axios.post(`${dns_broker}/entityOperations/upsert`, entities, {headers: headers}) + } catch (e) { + log_error(event,context, e.message, e) + } + } catch (e) { + log_error(event,context, e.message, e) + } +} + + +const log_error = (event, context, message, error) => { + console.error(JSON.stringify({ + message: message, + event: event, + error: error, + context: context + })) +} + diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateShadow/index.js b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateShadow/index.js new file mode 100644 index 0000000..052e3e1 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-core/lambda/updateShadow/index.js @@ -0,0 +1,54 @@ +const iot_region = process.env.AWSIOTREGION +const shadow_prefix = process.env.SHADOW_PREFIX +const url_broker = process.env.URL_CONTEXT_BROKER + +const {IoTDataPlaneClient, UpdateThingShadowCommand} = require('@aws-sdk/client-iot-data-plane') +const iotdata = new IoTDataPlaneClient({region: iot_region}) + + +exports.handler = async (event, context) => { + try { + + for await (let msg of event.Records){ + let payload = JSON.parse(msg.body) + + if(!payload.id || !payload.type){ + throw new Error('Invalid entity - id or type is missing') + } + + const thingName = `${payload.id.split(':').slice(-1)}` + + try { + const shadow_payload = { + state: { + reported: payload + } + } + + let updateThingShadow = await iotdata.send( + new UpdateThingShadowCommand({ + payload: JSON.stringify(shadow_payload), + thingName: thingName, + shadowName: `${shadow_prefix}-${payload.type}` + }) + ) + + } catch (e) { + log_error(event,context, e.message, e) + } + + } + + } catch (e) { + log_error(event,context, e.message, e) + } +} + +const log_error = (event, context, message, error) => { + console.error(JSON.stringify({ + message: message, + event: event, + error: error, + context: context + })) +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-stack.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-stack.ts new file mode 100644 index 0000000..eeff09d --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/garnet-iot-stack.ts @@ -0,0 +1,49 @@ +import { Aws, NestedStack, NestedStackProps } from 'aws-cdk-lib' +import { Vpc } from 'aws-cdk-lib/aws-ec2' +import { Bucket } from 'aws-cdk-lib/aws-s3' +import { Construct } from 'constructs' +import { Parameters } from '../../../parameters' +import { GarnetIotApi } from './garnet-iot-api' +import { GarnetIot } from './garnet-iot-core' + +export interface GarnetIotStackProps extends NestedStackProps { + dns_context_broker: string, + vpc: Vpc, + api_ref: string + bucket_name: string +} + +export class GarnetIotStack extends NestedStack { + + public readonly iot_sqs_endpoint_arn : string + + constructor(scope: Construct, id: string, props: GarnetIotStackProps) { + super(scope, id, props) + + /* + * CREATE GARNET IoT DATALAKE BUCKET + */ + + // DEFAULT BUCKET NAME + const bucket_name = props.bucket_name + const bucket = Bucket.fromBucketName(this, 'IoTBucket', bucket_name) + + + // DEPLOY THE CORE OF GARNET IoT + const garnet_iot_core_construct = new GarnetIot(this, 'Core', { + vpc: props.vpc, + dns_context_broker: props.dns_context_broker, + bucket_arn: bucket.bucketArn + }) + + // DEPLOY THE GARNET IoT API DEVICE MANAGEMENT + const garnet_iot_api_construct= new GarnetIotApi(this, 'Api', { + api_ref: props.api_ref, + vpc: props.vpc, + dns_context_broker: props.dns_context_broker + }) + + + this.iot_sqs_endpoint_arn = garnet_iot_core_construct.sqs_garnet_iot_arn + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package-lock.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package-lock.json new file mode 100644 index 0000000..125f2a8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package-lock.json @@ -0,0 +1,435 @@ +{ + "name": "nodejs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nodejs", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1426.0", + "axios": "^1.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1426.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1426.0.tgz", + "integrity": "sha512-qq4ydcRzQW2IqjMdCz5FklORREEtkSCJ2tm9CUJ2PaUOaljxpdxq9UI64vXiyRD+GIp5vdkmVNoTRi2rCXh3rA==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package.json new file mode 100644 index 0000000..b6289e8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-iot/layers/nodejs/package.json @@ -0,0 +1,16 @@ +{ + "name": "nodejs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1426.0", + "axios": "^1.4.0" + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-orion/garnet-orion.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-orion/garnet-orion.ts new file mode 100644 index 0000000..2e0aa40 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/lib/stacks/garnet-orion/garnet-orion.ts @@ -0,0 +1,42 @@ +import { Aws, CfnOutput, NestedStack, NestedStackProps } from "aws-cdk-lib" +import { Construct } from "constructs" +import { GarnetSecret } from "../garnet-constructs/secret"; +import { GarnetNetworking } from "../garnet-constructs/networking"; +//import { GarnetOrionDatabase } from "./database"; +//import { GarnetOrionFargate } from "./fargate"; +import { Parameters } from "../../../parameters"; +import { Vpc } from "aws-cdk-lib/aws-ec2"; +import { GarnetApiGateway } from "../garnet-constructs/apigateway"; +import { Secret } from "aws-cdk-lib/aws-secretsmanager"; + +export interface GarnetOrionProps extends NestedStackProps{ + vpc: Vpc, + secret: Secret +} + +export class GarnetOrion extends NestedStack { + + public readonly dns_context_broker: string + public readonly broker_api_endpoint: string + public readonly api_ref: string + + + constructor(scope: Construct, id: string, props: GarnetOrionProps) { + super(scope, id, props) + + const api_stack = new GarnetApiGateway(this, "Api", { + vpc: props.vpc, + fargate_albListenerArn: Parameters.amazon_eks_cluster_load_balancer_listener_arn, //TODO Replace with EKS Cluster Load Balancer Listener ARN // Pass the listenerArn as a string fargate_alb: ``//fargate_construct.fargate_alb, + }) + + new CfnOutput(this, "garnet_endpoint", { + value: `https://${api_stack.api_ref}.execute-api.${Aws.REGION}.amazonaws.com`, + }) + + this.broker_api_endpoint = `https://${api_stack.api_ref}.execute-api.${Aws.REGION}.amazonaws.com`; + this.dns_context_broker = Parameters.amazon_eks_cluster_load_balancer_dns //TODO Replace with EKS Cluster Load Balancer DNS name //fargate_construct.fargate_alb.loadBalancer.loadBalancerDnsName; + this.api_ref = api_stack.api_ref; + + } + +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package-lock.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package-lock.json new file mode 100644 index 0000000..4796e23 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package-lock.json @@ -0,0 +1,4156 @@ +{ + "name": "garnet", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "garnet", + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "aws-cdk-lib": "2.87.0", + "constructs": "^10.2.69", + "source-map-support": "^0.5.21" + }, + "bin": { + "garnet": "bin/garnet.js" + }, + "devDependencies": { + "@types/jest": "^29.5.3", + "@types/node": "20.4.2", + "aws-cdk": "2.87.0", + "jest": "^29.6.1", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "~5.1.6" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.200", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.200.tgz", + "integrity": "sha512-Kf5J8DfJK4wZFWT2Myca0lhwke7LwHcHBo+4TvWOGJrFVVKVuuiLCkzPPRBQQVDj0Vtn2NBokZAz8pfMpAqAKg==" + }, + "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==" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.166", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.166.tgz", + "integrity": "sha512-j0xnccpUQHXJKPgCwQcGGNu4lRiC1PptYfdxBIH1L4dRK91iBxtSQHESRQX+yB47oGLaF/WfNN/aF3WXwlhikg==" + }, + "node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", + "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", + "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", + "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.2", + "@jest/reporters": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.6.2", + "jest-haste-map": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-resolve-dependencies": "^29.6.2", + "jest-runner": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", + "jest-watcher": "^29.6.2", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.2", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", + "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "jest-mock": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", + "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "dev": true, + "dependencies": { + "expect": "^29.6.2", + "jest-snapshot": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", + "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", + "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.6.2", + "jest-mock": "^29.6.2", + "jest-util": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", + "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.2", + "@jest/expect": "^29.6.2", + "@jest/types": "^29.6.1", + "jest-mock": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", + "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", + "jest-worker": "^29.6.2", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", + "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", + "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", + "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.2", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", + "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.1", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.6.2", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", + "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aws-cdk": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.87.0.tgz", + "integrity": "sha512-dBm74nl3dMUxoAzgjcfKnzJyoVNIV//B1sqDN11cC3LXEflYapcBxPxZHAyGcRXg5dW3m14dMdKVQfmt4N970g==", + "dev": true, + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.87.0.tgz", + "integrity": "sha512-9kirXX7L7OP/yGmCbaYlkt5OAtowGiGw0AYFIQvSwvx/UU3aJO5XuDwAgDsvToDkRpBi0yX0bNwqa0DItu+C6A==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml" + ], + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.177", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", + "inBundle": true, + "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", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "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", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "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", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.2.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.5.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "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", + "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", + "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", + "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", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", + "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.6.2", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001518", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz", + "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/constructs": { + "version": "10.2.69", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.69.tgz", + "integrity": "sha512-0AiM/uQe5Uk6JVe/62oolmSN2MjbFQkOlYrM3fFGZLKuT+g7xlAI10EebFhyCcZwI2JAcWuWCmmCAyCothxjuw==", + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.478", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.478.tgz", + "integrity": "sha512-qjTA8djMXd+ruoODDFGnRCRBpID+AAfYWCyGtYTNhsuwxI19s8q19gbjKTwRS5z/LyVf5wICaIiPQGLekmbJbA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", + "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.6.2", + "@types/node": "*", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", + "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.2", + "@jest/types": "^29.6.1", + "import-local": "^3.0.2", + "jest-cli": "^29.6.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", + "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.2", + "@jest/expect": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.6.2", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", + "p-limit": "^3.1.0", + "pretty-format": "^29.6.2", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", + "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", + "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.6.2", + "@jest/types": "^29.6.1", + "babel-jest": "^29.6.2", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.6.2", + "jest-environment-node": "^29.6.2", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-runner": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.6.2", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", + "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", + "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.6.2", + "pretty-format": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", + "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.2", + "@jest/fake-timers": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "jest-mock": "^29.6.2", + "jest-util": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", + "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.6.2", + "jest-worker": "^29.6.2", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", + "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", + "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.6.2", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", + "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", + "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "@types/node": "*", + "jest-util": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", + "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.2", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", + "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", + "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.2", + "@jest/environment": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.6.2", + "jest-haste-map": "^29.6.2", + "jest-leak-detector": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-resolve": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-util": "^29.6.2", + "jest-watcher": "^29.6.2", + "jest-worker": "^29.6.2", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", + "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.2", + "@jest/fake-timers": "^29.6.2", + "@jest/globals": "^29.6.2", + "@jest/source-map": "^29.6.0", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-mock": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", + "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.6.2", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.6.2", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^29.6.2", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", + "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", + "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.6.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", + "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", + "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.6.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", + "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package.json new file mode 100644 index 0000000..f997cc5 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/package.json @@ -0,0 +1,28 @@ +{ + "name": "garnet", + "version": "1.0.0", + "bin": { + "garnet": "bin/garnet.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk", + "preinstall": "node install.js" + }, + "devDependencies": { + "@types/jest": "^29.5.3", + "@types/node": "20.4.2", + "jest": "^29.6.1", + "ts-jest": "^29.1.1", + "aws-cdk": "2.87.0", + "ts-node": "^10.9.1", + "typescript": "~5.1.6" + }, + "dependencies": { + "aws-cdk-lib": "2.87.0", + "constructs": "^10.2.69", + "source-map-support": "^0.5.21" + } +} diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/parameters.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/parameters.ts new file mode 100644 index 0000000..81e85a8 --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/parameters.ts @@ -0,0 +1,47 @@ +import { Aws } from "aws-cdk-lib" +import { InstanceClass, InstanceSize, InstanceType } from "aws-cdk-lib/aws-ec2" +import {StorageType } from "aws-cdk-lib/aws-rds" + + +enum Broker { + Orion = "Orion", // Here orion refers to orion-ld broker: https://github.com/FIWARE/context.Orion-LD + Scorpio = "Scorpio" +} + +export const Parameters = { + + // FIWARE DATA SPACE CONNECTOR PARAMETERS + amazon_eks_cluster_load_balancer_dns: "k8s-kubesyst-ingressn-XXXXXXXX.elb.eu-west-1.amazonaws.com", // REPLACE WITH YOUR CLUSTER LOAD BALANCER DNS + amazon_eks_cluster_load_balancer_listener_arn: "arn:aws:elasticloadbalancing:eu-west-1:XXXXXXXXX:listener/net/k8s-kubesyst-ingressn-XXXXXXXXXX/XXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXX", + + // GARNET PARAMETERS + aws_region: "eu-west-1", // see regions in which you can deploy Garnet: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vpc-links.html#http-api-vpc-link-availability + garnet_version: "1.0.0", // Do not change + garnet_broker: Broker.Orion, // choose between enum Broker value (Orion or Scorpio) + garnet_bucket: `garnet-iot-datalake-${Aws.REGION}-${Aws.ACCOUNT_ID}`, // Default name, change only if really needed. + //GARNET IOT PARAMETERS + garnet_iot: { + shadow_prefix: "Garnet", // Do not change. + smart_data_model_url : 'https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld' + }, + // FARGATE PARAMETERS + garnet_fargate: { + fargate_desired_count: 2, + fargate_cpu: 1024, + fargate_memory_limit: 4096 + }, + // SCORPIO BROKER PARAMETERS + garnet_scorpio: { + image_context_broker: 'public.ecr.aws/scorpiobroker/all-in-one-runner:java-sqs-testsqs', // Link to ECR Public gallery of Scorpio Broker image. + rds_instance_type: InstanceType.of( InstanceClass.BURSTABLE4_GRAVITON, InstanceSize.SMALL), // see https://aws.amazon.com/rds/instance-types/ + rds_storage_type: StorageType.GP3, // see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html + dbname: 'scorpio' + }, + // ORION-LD PARAMETERS + garnet_orion: { + image_context_broker: 'fiware/orion-ld:1.5.0-PRE-1455', // Link to ECR Public gallery of Orion image. + docdb_instance_type: InstanceType.of( InstanceClass.BURSTABLE4_GRAVITON, InstanceSize.MEDIUM), // https://docs.aws.amazon.com/documentdb/latest/developerguide/db-instance-classes.html + docdb_nb_instances: 2 + } + +} \ No newline at end of file diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/test/stf-core.test.ts b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/test/stf-core.test.ts new file mode 100644 index 0000000..2c3c74b --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/test/stf-core.test.ts @@ -0,0 +1,17 @@ +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as Garnet from '../lib/garnet-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/garnet-stack.ts +test('SQS Queue Created', () => { +// const app = new cdk.App(); +// // WHEN +// const stack = new Garnet.GarnetStack(app, 'MyTestStack'); +// // THEN +// const template = Template.fromStack(stack); + +// template.hasResourceProperties('AWS::SQS::Queue', { +// VisibilityTimeout: 300 +// }); +}); diff --git a/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/tsconfig.json b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/tsconfig.json new file mode 100644 index 0000000..9f8e8be --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/aws-garnet-iot-module/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": [ + "es2018" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/examples/aws-garnet/scenario-1-deployment/podLogs/podLogsPlaceholder.txt b/examples/aws-garnet/scenario-1-deployment/podLogs/podLogsPlaceholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/examples/aws-garnet/scenario-1-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml b/examples/aws-garnet/scenario-1-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml new file mode 100644 index 0000000..a8f213e --- /dev/null +++ b/examples/aws-garnet/scenario-1-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario1.yaml @@ -0,0 +1,1770 @@ +# should argo-cd applications be created? +argoApplications: false + + +#Sub-Chart configuration + +activation-service: + # Enable the deployment of application: activation-service + deploymentEnabled: true + + activation-service: + ## Configuration of activation service execution + activationService: + # -- Number of (gunicorn) workers that should be created + workers: 1 + # -- Maximum header size in bytes + maxHeaderSize: 32768 + # -- Log Level + logLevel: "debug" + + ## Add Ingress or OpenShift Route + route: + enabled: false + + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ips-as.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ips-as.dsba.aws.fiware.io + secretName: as-ips-dsba-tls + + ## CCS config + ccs: + endpoint: "http://ips-dsc-credentials-config-service:8080/" + id: "ips-activation-service" + credentials: + - type: "VerifiableCredential" + trustedParticipantsLists: [ + "https://tir.dsba.fiware.dev" + ] + trustedIssuersLists: [ + "http://ips-dsc-trusted-issuers-list:8080" + ] + - type: "IpsActivationService" + trustedParticipantsLists: [ + "https://tir.dsba.fiware.dev" + ] + trustedIssuersLists: [ + "http://ips-dsc-trusted-issuers-list:8080" + ] + + ## AS config + config: + + # DB + db: + # -- Use sqlite in-memory database + useMemory: true + # -- Enable tracking of modifications + modTracking: false + # -- Enable SQL logging to stderr + echo: true + + # Configuration for additional API keys to protect certain endpoints + apikeys: + # Config for Trusted-Issuers-List flow + issuer: + # Header name + headerName: "AS-API-KEY" + # API key (auto-generated if left empty) + apiKey: "77ab4a67-ea3c-4348-98bd-2e9f0304bfb8" + # Enable for /issuer endpoint (API key will be required) + enabledIssuer: true + + issuer: + clientId: "ips-activation-service" + providerId: "did:web:ips.dsba.aws.fiware.io:did" + tilUri: "http://ips-dsc-trusted-issuers-list:8080" + verifierUri: "https://ips-verifier.dsba.aws.fiware.io" + samedevicePath: "/api/v1/samedevice" + jwksPath: "/.well-known/jwks" + algorithms: + - "ES256" + roles: + createRole: "CREATE_ISSUER" + updateRole: "UPDATE_ISSUER" + deleteRole: "DELETE_ISSUER" + +credentials-config-service: + # Enable the deployment of application: credentials-config-service + deploymentEnabled: true + + credentials-config-service: + + # Database config + database: + persistence: true + host: mysql-ips + name: ccs + + # Should use Secret in production environment + username: root + password: "dbPassword" + +dsba-pdp: + # Enable the deployment of application: dsba-pdp + deploymentEnabled: true + + dsba-pdp: + + # DB + db: + enabled: false + migrate: + enabled: false + + deployment: + # Log level + logLevel: DEBUG + + # iSHARE config + ishare: + existingSecret: ips-dsc-vcwaltid-tls-sec + + clientId: did:web:ips.dsba.aws.fiware.io:did + + # Initial list of fingerprints for trusted CAs. This will be overwritten + # after the first update from the trust anchor. + trustedFingerprints: + - D2F62092F982CF783D4632BD86FA86C3FBFDB2D8C8A58BC6809163FCF5CD030B + + ar: + id: "did:web:ips.dsba.aws.fiware.io:did" + delegationPath: "/ar/delegation" + tokenPath: "/oauth2/token" + url: "https://ar-ips.dsba.aws.fiware.io" + + trustAnchor: + id: "EU.EORI.FIWARESATELLITE" + tokenPath: "/token" + trustedListPath: "/trusted_list" + url: "https://tir.dsba.fiware.dev" + + # Verifier + trustedVerifiers: + - https://ips-verifier.dsba.aws.fiware.io/.well-known/jwks + + # Provider DID + providerId: "did:web:ips.dsba.aws.fiware.io:did" + + # ENVs + additionalEnvVars: + - name: ISHARE_CERTIFICATE_PATH + value: /iShare/tls.crt + - name: ISHARE_KEY_PATH + value: /iShare/tls.key + +kong: + # Enable the deployment of application: kong + deploymentEnabled: true + + kong: + replicaCount: 1 + + proxy: + enabled: true + tls: + enabled: false + + # Provide Ingress or Route config here + ingress: + enabled: true + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + #ingressClassName: nginx + tls: kong-ips-dsba-tls + hostname: ips-kong.dsba.aws.fiware.io + route: + enabled: false + + # Provide the kong.yml configuration (either as existing CM, secret or directly in the values.yaml) + dblessConfig: + configMap: "" + secret: "" + config: | + _format_version: "2.1" + _transform: true + + consumers: + - username: token-consumer + keyauth_credentials: + - tags: + - token-key + - tir-key + + services: + - host: "ips-dsc-orion" + name: "ips" + port: 1026 + protocol: http + + routes: + - name: ips + paths: + - /ips + strip_path: true + + plugins: + - name: pep-plugin + config: + pathprefix: "/ips" + authorizationendpointtype: ExtAuthz + authorizationendpointaddress: http://ips-dsc-dsba-pdp:8080/authz + + - name: request-transformer + config: + remove: + headers: + - Authorization + - authorization + +mongodb: + # Enable the deployment of application: mongodb + deploymentEnabled: true + + mongodb: + + # DB Authorization + auth: + enabled: true + # Should use a Secret on production deployments + rootPassword: "dbPassword" + + # Required for permissions to PVC + podSecurityContext: + enabled: true + fsGroup: 1001 + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsGroup: 0 + runAsNonRoot: true + + # Set resources + resources: + limits: + cpu: 200m + memory: 512Mi + + persistence: + enabled: true + size: 8Gi + +mysql: + # Enable the deployment of application: mysql + deploymentEnabled: true + + mysql: + fullnameOverride: mysql-ips + auth: + # Should use a Secret on production deployments + rootPassword: "dbPassword" + password: "dbPassword" + +orion-ld: + # Enable the deployment of application: orion-ld + deploymentEnabled: true + + orion: + + broker: + db: + auth: + user: root + password: "dbPassword" + mech: "SCRAM-SHA-1" + hosts: + - ips-dsc-mongodb + + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + entities: + - name: deliveryorder_happypets001.json + data: | + { + "id": "urn:ngsi-ld:DELIVERYORDER:HAPPYPETS001", + "type": "DELIVERYORDER", + "issuer": { + "type": "Property", + "value": "Happy Pets" + }, + "destinee": { + "type": "Property", + "value": "Happy Pets customer via IPS" + }, + "deliveryAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "Customer Strasse 23" + } + }, + "originAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "HappyPets Strasse 15" + } + }, + "pda": { + "type": "Property", + "value": "2021-10-03" + }, + "pta": { + "type": "Property", + "value": "14:00:00" + }, + "eda": { + "type": "Property", + "value": "2021-10-02" + }, + "eta": { + "type": "Property", + "value": "14:00:00" + }, + "@context": [ + "https://schema.lab.fiware.org/ld/context" + ] + } + + - name: deliveryorder_happypets002.json + data: | + { + "id": "urn:ngsi-ld:DELIVERYORDER:HAPPYPETS002", + "type": "DELIVERYORDER", + "issuer": { + "type": "Property", + "value": "Happy Pets" + }, + "destinee": { + "type": "Property", + "value": "Happy Pets 2nd customer via IPS" + }, + "deliveryAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Hamburg", + "addressLocality": "Hamburg", + "postalCode": "23456", + "streetAddress": "Customer Str. 19" + } + }, + "originAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "HappyPets Strasse 15" + } + }, + "pda": { + "type": "Property", + "value": "2021-11-12" + }, + "pta": { + "type": "Property", + "value": "11:00:00" + }, + "eda": { + "type": "Property", + "value": "2021-11-12" + }, + "eta": { + "type": "Property", + "value": "11:00:00" + }, + "@context": [ + "https://schema.lab.fiware.org/ld/context" + ] + } + +postgres: + # Enable the deployment of application: postgres + deploymentEnabled: true + + postgresql: + + fullnameOverride: postgresql-ips + + auth: + # Should use a Secret for PWs on production deployments + # Credentials for Keycloak DB + username: keycloak + password: "dbPassword" + enablePostgresUser: true + + # Credentials for postgres admin user + postgresPassword: "dbRootPassword" + + # Init DB + primary: + initdb: + scripts: + create.sh: | + psql postgresql://postgres:${POSTGRES_POSTGRES_PASSWORD}@localhost:5432 -c "CREATE DATABASE keycloak_ips;" + +trusted-issuers-list: + # Enable the deployment of application: trusted-issuers-list + deploymentEnabled: true + + trusted-issuers-list: + + # Ingress + ingress: + til: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: til-ips.dsba.aws.fiware.io + tls: + - hosts: + - til-ips.dsba.aws.fiware.io + secretName: til-ips-dsba-til-tls + tir: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: tir-ips.dsba.aws.fiware.io + tls: + - hosts: + - tir-ips.dsba.aws.fiware.io + secretName: til-ips-dsba-tir-tls + + # Database config + database: + persistence: true + host: mysql-ips + name: til + + # Should use Secret in production environment + username: root + password: "dbPassword" + + # Init data + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + issuers: + - name: mp_create + issuer: + did: "did:web:marketplace.dsba.fiware.dev:did" + credentials: + - validFor: + from: "2022-07-21T17:32:28Z" + to: "2040-07-21T17:32:28Z" + credentialsType: "IpsActivationService" + claims: + - name: "roles" + allowedValues: + - - names: + - "CREATE_ISSUER" + target: "did:web:ips.dsba.aws.fiware.io:did" + - validFor: + from: "2022-07-21T17:32:28Z" + to: "2040-07-21T17:32:28Z" + credentialsType: "VerifiableCredential" + +vcwaltid: + # Enable the deployment of application: vcwaltid + deploymentEnabled: true + + # Organisation DID + did: did:web:ips.dsba.aws.fiware.io:did + ingress: + enabled: true + host: ips.dsba.aws.fiware.io + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + tls: + enabled: true + route: + enabled: false + + # Walt-id config + vcwaltid: + + # Persistence + persistence: + enabled: true + pvc: + size: 1Gi + + # List of templates to be created + templates: + GaiaXParticipantCredential.json: | + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://registry.lab.dsba.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": [ + "VerifiableCredential" + ], + "id": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "issuer": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "issuanceDate": "2023-03-21T12:00:00.148Z", + "credentialSubject": { + "id": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "type": "gx:LegalParticipant", + "gx:legalName": "dsba compliant participant", + "gx:legalRegistrationNumber": { + "gx:vatID": "MYVATID" + }, + "gx:headquarterAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx-terms-and-conditions:gaiaxTermsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700" + } + } + NaturalPersonCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "familyName": "Happy", + "firstName": "User", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "LegalPersonCredential"] + } + MarketplaceUserCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "email": "normal-user@fiware.org", + "familyName": "IPS", + "firstName": "employee", + "lastName": "IPS", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["MarketplaceUserCredential"] + } + EmployeeCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "email": "normal-user@fiware.org", + "familyName": "IPS", + "firstName": "employee", + "lastName": "IPS", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["EmployeeCredential"] + } + +verifier: + # Enable the deployment of application: verifier + deploymentEnabled: true + + vcverifier: + + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ips-verifier.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ips-verifier.dsba.aws.fiware.io + secretName: verifier-ips-dsba-tls + + deployment: + + # Logging + logging: + level: DEBUG + pathsToSkip: + - "/health" + + # Walt-id config + ssikit: + auditorUrl: http://ips-dsc-vcwaltid:7003 + + # Verifier config + verifier: + # URL endpoint of data space trusted issuers registry + tirAddress: https://tir.dsba.fiware.dev/v3/issuers + # DID of organisation + did: did:web:ips.dsba.aws.fiware.io:did + + # Config service + configRepo: + configEndpoint: http://ips-dsc-credentials-config-service:8080/ + + +keyrock: + # Enable the deployment of application: keyrock + deploymentEnabled: true + + keyrock: + fullnameOverride: keyrock-ips + + # DB config + db: + user: root + password: "dbPassword" + host: mysql-ips + + # Admin user to be created + admin: + user: admin + password: "admin" + email: admin@fiware.org + + # External hostname of Keyrock + host: https://ar-ips.dsba.aws.fiware.io + + # Ingress + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ar-ips.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ar-ips.dsba.aws.fiware.io + secretName: ar-ips-dsba-tls + + ## Theme configuration for Keyrock + theme: + ## -- Enable theme + enabled: false + + ## Configuration of Authorisation Registry (AR) + authorisationRegistry: + # -- Enable usage of authorisation registry + enabled: true + # -- Identifier (EORI) of AR + identifier: "did:web:ips.dsba.aws.fiware.io:did" + # -- URL of AR + url: "internal" + + ## Configuration of iSHARE Satellite + satellite: + # -- Enable usage of satellite + enabled: true + # -- Identifier (EORI) of satellite + identifier: "EU.EORI.FIWARESATELLITE" + # -- URL of satellite + url: "https://tir.dsba.fiware.dev" + # -- Token endpoint of satellite + tokenEndpoint: "https://tir.dsba.fiware.dev/token" + # -- Parties endpoint of satellite + partiesEndpoint: "https://tir.dsba.fiware.dev/parties" + + ## -- Configuration of local key and certificate for validation and generation of tokens + token: + # -- Enable storage of local key and certificate + enabled: false + + # ENV variables for Keyrock + additionalEnvVars: + - name: IDM_TITLE + value: "IPS AR" + - name: IDM_DEBUG + value: "true" + - name: DEBUG + value: "*" + - name: IDM_DB_NAME + value: ar_idm_ips + - name: IDM_DB_SEED + value: "true" + - name: IDM_SERVER_MAX_HEADER_SIZE + value: "32768" + - name: IDM_PR_CLIENT_ID + value: "did:web:ips.dsba.aws.fiware.io:did" + - name: IDM_PR_CLIENT_KEY + valueFrom: + secretKeyRef: + name: ips-dsc-vcwaltid-tls-sec + key: tls.key + - name: IDM_PR_CLIENT_CRT + valueFrom: + secretKeyRef: + name: ips-dsc-vcwaltid-tls-sec + key: tls.crt + + # Init data + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + command: + - /bin/sh + - /scripts/create.sh + volumeMount: + name: scripts + mountPath: /scripts + env: + - name: DB_PASSWORD + value: "dbPassword" + scriptData: + create.sh: |- + mysql -h mysql-ips -u root -p$DB_PASSWORD ar_idm_ips <IPS Keycloak", + "enabled": true, + "attributes": { + "frontendUrl": "https://ips-kc.dsba.aws.fiware.io" + }, + "sslRequired": "none", + "roles": { + "realm": [ + { + "name": "user", + "description": "User privileges", + "composite": false, + "clientRole": false, + "containerId": "fiware-server", + "attributes": {} + } + ], + "client": { + "did:web:onboarding.dsba.fiware.dev:did": [ + { + "name": "LEGAL_REPRESENTATIVE", + "description": "Is allowed to register participants", + "clientRole": true + }, + { + "name": "EMPLOYEE", + "description": "Is allowed to see participants", + "clientRole": true + } + ], + "did:web:marketplace.dsba.fiware.dev:did": [ + { + "name": "customer", + "description": "Is allowed to buy.", + "clientRole": true + }, + { + "name": "seller", + "description": "Is allowed to offer.", + "clientRole": true + } + ], + "did:web:ips.dsba.aws.fiware.io:did": [ + { + "name": "STANDARD_CUSTOMER", + "description": "User to access IPS with read access", + "clientRole": true + }, + { + "name": "GOLD_CUSTOMER", + "description": "User to access IPS with read/write access", + "clientRole": true + } + ] + } + }, + "groups": [ + { + "name": "admin", + "path": "/admin", + "realmRoles": [ + "user" + ] + }, + { + "name": "consumer", + "path": "/consumer", + "realmRoles": [ + "user" + ] + } + ], + "users": [ + { + "username": "the-lear", + "enabled": true, + "email": "lear@ips.org", + "credentials": [ + { + "type": "password", + "value": "the-lear" + } + ], + "clientRoles": { + "did:web:onboarding.dsba.fiware.dev:did": [ + "LEGAL_REPRESENTATIVE", + "EMPLOYEE" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/admin", + "/consumer" + ] + }, + { + "username": "legal-representative", + "enabled": true, + "email": "legal-representative@ips.org", + "firstName": "Legal", + "lastName": "IPSEmployee", + "credentials": [ + { + "type": "password", + "value": "legal-representative" + } + ], + "clientRoles": { + "did:web:marketplace.dsba.fiware.dev:did" : [ + "customer", + "seller" + ], + "did:web:onboarding.dsba.fiware.dev:did": [ + "LEGAL_REPRESENTATIVE" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/admin", + "/consumer" + ] + }, + { + "username": "standard-employee", + "enabled": true, + "email": "standard-employee@ips.org", + "credentials": [ + { + "type": "password", + "value": "standard-employee" + } + ], + "clientRoles": { + "did:web:onboarding.dsba.fiware.dev:did": [ + "EMPLOYEE" + ], + "did:web:ips.dsba.aws.fiware.io:did": [ + "GOLD_CUSTOMER" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/consumer" + ] + } + ], + "clients": [ + { + "clientId": "did:web:ips.dsba.aws.fiware.io:did", + "enabled": true, + "description": "Client for internal users", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_EmployeeCredential": "ldp_vc,jwt_vc_json", + "EmployeeCredential_claims": "email,firstName,familyName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:marketplace.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect to the marketplace", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_MarketplaceUserCredential": "ldp_vc,jwt_vc_json", + "MarketplaceUserCredential_claims": "email,firstName,lastName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:onboarding.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect the onboarding service at portal.dsba.fiware.dev", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_NaturalPersonCredential": "ldp_vc,jwt_vc_json", + "vctypes_GaiaXParticipantCredential": "ldp_vc,jwt_vc_json", + "vc_subjectDid": "did:web:packetdelivery.dsba.fiware.dev:did", + "vc_gx:legalName": "Packet Delivery Company Inc.", + "GaiaXParticipantCredential_claims": "subjectDid,gx:legalName", + "NaturalPersonCredential_claims": "email,firstName,familyName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:marketplace.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect to the marketplace", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_MarketplaceUserCredential": "ldp_vc,jwt_vc_json", + "MarketplaceUserCredential_claims": "email,firstName,lastName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + } + ], + "clientScopes": [ + { + "name": "fiware-scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "fiware-scope-object", + "protocol": "openid-connect", + "protocolMapper": "oidc-script-based-protocol-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "multivalued": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "fiware-scope-object", + "script": "/**\n * Available variables: \n * user - the current user\n * realm - the current realm\n * token - the current token\n * userSession - the current userSession\n * keycloakSession - the current userSession\n */\n\nvar ArrayList = Java.type(\"java.util.ArrayList\");\nvar fiware_scope = new ArrayList();\n\nvar forEach = Array.prototype.forEach;\n\nvar fiware_service;\nvar fiware_servicepath;\nvar fiware_entry;\nvar roles = '';\n\nvar orion_client = realm.getClientByClientId('orion-pep');\n\nfiware_service = user.getFirstAttribute('fiware-service');\nfiware_servicepath = user.getFirstAttribute('fiware-servicepath');\nif (fiware_service !== null && fiware_servicepath !== null) {\n\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n\n var roleModels = user.getClientRoleMappings(orion_client);\n if (roleModels.size() > 0) {\n forEach.call(\n user.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = {};\n}\n\nforEach.call(\n user.getGroups().toArray(),\n function (group) {\n\n fiware_service = group.getFirstAttribute('fiware-service');\n fiware_servicepath = group.getFirstAttribute('fiware-servicepath');\n if (fiware_service !== null && fiware_servicepath !== null) {\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n\n var roleModels = group.getClientRoleMappings(orion_client);\n if (roleModels.size() > 0) {\n forEach.call(\n group.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = {};\n } else if (group.getParentId() !== null) {\n fiware_service = group.getParent().getFirstAttribute('fiware-service');\n fiware_servicepath = group.getParent().getFirstAttribute('fiware-servicepath');\n\n if (fiware_service !== null && fiware_servicepath !== null) {\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n var subroleModels = group.getClientRoleMappings(orion_client);\n if (subroleModels.size() > 0) {\n forEach.call(\n group.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = '';\n }\n }\n }\n);\n\nexports = fiware_scope;" + } + } + ] + }, + { + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "roles", + "role_list", + "email", + "web-origins", + "profile" + ], + "defaultOptionalClientScopes": [ + "microprofile-jwt", + "phone", + "address", + "offline_access" + ] + } + + + + diff --git a/examples/aws-garnet/scenario-2-deployment/README.md b/examples/aws-garnet/scenario-2-deployment/README.md new file mode 100644 index 0000000..9a4de0d --- /dev/null +++ b/examples/aws-garnet/scenario-2-deployment/README.md @@ -0,0 +1,112 @@ +# Integration with AWS Garnet Framework + +## 2/ Existing AWS Garnet Framework deployment in the AWS Account with a Context Broker on AWS ECS Fargate +For this scenario, it is recommended that a modified version of the Helm Chart for the Data Spaces Connector is deployed to a Kubernetes Cluster in the service Amazon Elastic Kubernetes Service ([AWS EKS](https://aws.amazon.com/eks/)). +In this case, considering that your environment for the AWS Garnet Framework was set up following the [official AWS GitHub Repository](https://github.com/aws-samples/aws-stf-core), the FIWARE Orion-LD Context Broker is already hosted as an Amazon Elastic Container Service ([AWS ECS](https://aws.amazon.com/ecs/)) task in an AWS Fargate cluster and the integration to the Data Spaces Connector will be performed by deploying only this modified Helm Chart available [in this reference](./yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml). + +
+ +![Target Architecture for extending the deployment of an existing AWS Garnet Framework](../static-assets/garnet-ds-connector-scenario2.png) + +
+ +### IPS Service Provider Deployment in Amazon EKS +This section covers the setup of the prerequisites of the IPS Service Provider examples of this repository, available in [this reference](../service-provider-ips/README.md). + +#### Changes to the original Helm chart +[The edited version of the IPS Service Provider example Helm Chart](./yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml) contains 3 main differences for this scenario where an existing Context Broker is already deployed and must only by extended by the additional building blocks of the Data Spaces Connector: + +* Disable the deployment of the MongoDB database + +```shell +mongodb: + # Disable the deployment of application: mongodb + deploymentEnabled: false +``` + +* Disable the deployment of the Orion-LD Context Broker + +```shell +orion-ld: + # Disable the deployment of application: orion-ld + deploymentEnabled: false +``` + +* Replace the host for the Kong proxy to point it to AWS Garnet Framework's Unified API based on API Gateway. The value for the host parameter can be found in your [AWS Cloud Formation](https://console.aws.amazon.com/cloudformation/home) Stack named `Garnet` > Outputs tab > `GarnetEndpoint` > Value. + +```shell + # Provide the kong.yml configuration (either as existing CM, secret or directly in the values.yaml) + dblessConfig: + configMap: "" + secret: "" + config: | + _format_version: "2.1" + _transform: true + + consumers: + - username: token-consumer + keyauth_credentials: + - tags: + - token-key + - tir-key + + #TODO - Replace here with the AWS Garnet Framework Unified API endpoint AND REMOVE THIS LINE + services: + - host: "xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com" + name: "ips" + port: 443 + protocol: http +``` + +#### Helm Chart install steps + +* IPS Kubernetes namespace creation + +```shell +kubectl create namespace ips +``` + +* Add FIWARE Data Space Connector Remote repository + +```shell +helm repo add dsc https://fiware-ops.github.io/data-space-connector/ +``` + +* Install the Helm Chart using the provided file `./yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml` [available in this repository](./yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml) + +```shell +helm install -n ips -f ./yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml ips-dsc dsc/data-space-connector +``` + +## Other Resources - Troubleshooting +Once the Data Space Connector is deployed via the Helm chart in your cluster, additional scripts are also provided in this [repository](../scripts/) to help any troubleshooting of your connector deployment. +Two main scripts are provided: + +* 1/ Save all pods logs from a EKS cluster namespace using `kubectl` to your local deployment machine under `./podLogs/` [in this repository structure](./podLogs/) +The script `kubectlLogsFromNamespace.sh` [link](../scripts/kubectlLogsFromNamespace.sh) runs a local process in your deployment machine to poll logs from all currently running pods under a namespace from your cluster and save locally for further analysis. +It can be manually modified to change the desired namespace to be analyzed + +```shell +#!/bin/bash + +NAMESPACE="ips" +``` + +and the corresponding polling period by changing the values in the script file before running it + +```shell + # Sleep for a few seconds before checking for new logs and pods again + sleep 3 +done +``` + +* 2/ Delete all currently running pods from an EKS cluster namespace using `kubectl` +The script `deleteAllPodsFromNamespace.sh` [link](../scripts/deleteAllPodsFromNamespace.sh) runs a local process in your deployment machine to force delete long-lived running pods from a failed deployment. +It can be manually modified to change the desired namespace to be analyzed + +```shell +#!/bin/bash + +# Set the fixed namespace +namespace="ips" +``` \ No newline at end of file diff --git a/examples/aws-garnet/scenario-2-deployment/podLogs/podLogsPlaceholder.txt b/examples/aws-garnet/scenario-2-deployment/podLogs/podLogsPlaceholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/examples/aws-garnet/scenario-2-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml b/examples/aws-garnet/scenario-2-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml new file mode 100644 index 0000000..e8ca6fb --- /dev/null +++ b/examples/aws-garnet/scenario-2-deployment/yaml/values-dsc-awl-load-balancer-controller-scenario2.yaml @@ -0,0 +1,1770 @@ +# should argo-cd applications be created? +argoApplications: false + + +#Sub-Chart configuration + +activation-service: + # Enable the deployment of application: activation-service + deploymentEnabled: true + + activation-service: + ## Configuration of activation service execution + activationService: + # -- Number of (gunicorn) workers that should be created + workers: 1 + # -- Maximum header size in bytes + maxHeaderSize: 32768 + # -- Log Level + logLevel: "debug" + + ## Add Ingress or OpenShift Route + route: + enabled: false + + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ips-as.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ips-as.dsba.aws.fiware.io + secretName: as-ips-dsba-tls + + ## CCS config + ccs: + endpoint: "http://ips-dsc-credentials-config-service:8080/" + id: "ips-activation-service" + credentials: + - type: "VerifiableCredential" + trustedParticipantsLists: [ + "https://tir.dsba.fiware.dev" + ] + trustedIssuersLists: [ + "http://ips-dsc-trusted-issuers-list:8080" + ] + - type: "IpsActivationService" + trustedParticipantsLists: [ + "https://tir.dsba.fiware.dev" + ] + trustedIssuersLists: [ + "http://ips-dsc-trusted-issuers-list:8080" + ] + + ## AS config + config: + + # DB + db: + # -- Use sqlite in-memory database + useMemory: true + # -- Enable tracking of modifications + modTracking: false + # -- Enable SQL logging to stderr + echo: true + + # Configuration for additional API keys to protect certain endpoints + apikeys: + # Config for Trusted-Issuers-List flow + issuer: + # Header name + headerName: "AS-API-KEY" + # API key (auto-generated if left empty) + apiKey: "77ab4a67-ea3c-4348-98bd-2e9f0304bfb8" + # Enable for /issuer endpoint (API key will be required) + enabledIssuer: true + + issuer: + clientId: "ips-activation-service" + providerId: "did:web:ips.dsba.aws.fiware.io:did" + tilUri: "http://ips-dsc-trusted-issuers-list:8080" + verifierUri: "https://ips-verifier.dsba.aws.fiware.io" + samedevicePath: "/api/v1/samedevice" + jwksPath: "/.well-known/jwks" + algorithms: + - "ES256" + roles: + createRole: "CREATE_ISSUER" + updateRole: "UPDATE_ISSUER" + deleteRole: "DELETE_ISSUER" + +credentials-config-service: + # Enable the deployment of application: credentials-config-service + deploymentEnabled: true + + credentials-config-service: + + # Database config + database: + persistence: true + host: mysql-ips + name: ccs + + # Should use Secret in production environment + username: root + password: "dbPassword" + +dsba-pdp: + # Enable the deployment of application: dsba-pdp + deploymentEnabled: true + + dsba-pdp: + + # DB + db: + enabled: false + migrate: + enabled: false + + deployment: + # Log level + logLevel: DEBUG + + # iSHARE config + ishare: + existingSecret: ips-dsc-vcwaltid-tls-sec + + clientId: did:web:ips.dsba.aws.fiware.io:did + + # Initial list of fingerprints for trusted CAs. This will be overwritten + # after the first update from the trust anchor. + trustedFingerprints: + - D2F62092F982CF783D4632BD86FA86C3FBFDB2D8C8A58BC6809163FCF5CD030B + + ar: + id: "did:web:ips.dsba.aws.fiware.io:did" + delegationPath: "/ar/delegation" + tokenPath: "/oauth2/token" + url: "https://ar-ips.dsba.aws.fiware.io" + + trustAnchor: + id: "EU.EORI.FIWARESATELLITE" + tokenPath: "/token" + trustedListPath: "/trusted_list" + url: "https://tir.dsba.fiware.dev" + + # Verifier + trustedVerifiers: + - https://ips-verifier.dsba.aws.fiware.io/.well-known/jwks + + # Provider DID + providerId: "did:web:ips.dsba.aws.fiware.io:did" + + # ENVs + additionalEnvVars: + - name: ISHARE_CERTIFICATE_PATH + value: /iShare/tls.crt + - name: ISHARE_KEY_PATH + value: /iShare/tls.key + +kong: + # Enable the deployment of application: kong + deploymentEnabled: true + + kong: + replicaCount: 1 + + proxy: + enabled: true + tls: + enabled: false + + # Provide Ingress or Route config here + ingress: + enabled: true + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + #ingressClassName: nginx + tls: kong-ips-dsba-tls + hostname: ips-kong.dsba.aws.fiware.io + route: + enabled: false + + # Provide the kong.yml configuration (either as existing CM, secret or directly in the values.yaml) + dblessConfig: + configMap: "" + secret: "" + config: | + _format_version: "2.1" + _transform: true + + consumers: + - username: token-consumer + keyauth_credentials: + - tags: + - token-key + - tir-key + + services: + - host: "psr8tihlz1.execute-api.eu-west-1.amazonaws.com" + name: "ips" + port: 443 + protocol: http + + routes: + - name: ips + paths: + - /ips + strip_path: true + + plugins: + - name: pep-plugin + config: + pathprefix: "/ips" + authorizationendpointtype: ExtAuthz + authorizationendpointaddress: http://ips-dsc-dsba-pdp:8080/authz + + - name: request-transformer + config: + remove: + headers: + - Authorization + - authorization + +mongodb: + # Enable the deployment of application: mongodb + deploymentEnabled: false + + mongodb: + + # DB Authorization + auth: + enabled: true + # Should use a Secret on production deployments + rootPassword: "dbPassword" + + # Required for permissions to PVC + podSecurityContext: + enabled: true + fsGroup: 1001 + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsGroup: 0 + runAsNonRoot: true + + # Set resources + resources: + limits: + cpu: 200m + memory: 512Mi + + persistence: + enabled: true + size: 8Gi + +mysql: + # Enable the deployment of application: mysql + deploymentEnabled: true + + mysql: + fullnameOverride: mysql-ips + auth: + # Should use a Secret on production deployments + rootPassword: "dbPassword" + password: "dbPassword" + +orion-ld: + # Enable the deployment of application: orion-ld + deploymentEnabled: false + + orion: + + broker: + db: + auth: + user: root + password: "dbPassword" + mech: "SCRAM-SHA-1" + hosts: + - ips-dsc-mongodb + + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + entities: + - name: deliveryorder_happypets001.json + data: | + { + "id": "urn:ngsi-ld:DELIVERYORDER:HAPPYPETS001", + "type": "DELIVERYORDER", + "issuer": { + "type": "Property", + "value": "Happy Pets" + }, + "destinee": { + "type": "Property", + "value": "Happy Pets customer via IPS" + }, + "deliveryAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "Customer Strasse 23" + } + }, + "originAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "HappyPets Strasse 15" + } + }, + "pda": { + "type": "Property", + "value": "2021-10-03" + }, + "pta": { + "type": "Property", + "value": "14:00:00" + }, + "eda": { + "type": "Property", + "value": "2021-10-02" + }, + "eta": { + "type": "Property", + "value": "14:00:00" + }, + "@context": [ + "https://schema.lab.fiware.org/ld/context" + ] + } + + - name: deliveryorder_happypets002.json + data: | + { + "id": "urn:ngsi-ld:DELIVERYORDER:HAPPYPETS002", + "type": "DELIVERYORDER", + "issuer": { + "type": "Property", + "value": "Happy Pets" + }, + "destinee": { + "type": "Property", + "value": "Happy Pets 2nd customer via IPS" + }, + "deliveryAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Hamburg", + "addressLocality": "Hamburg", + "postalCode": "23456", + "streetAddress": "Customer Str. 19" + } + }, + "originAddress": { + "type": "Property", + "value": { + "addressCountry": "DE", + "addressRegion": "Berlin", + "addressLocality": "Berlin", + "postalCode": "12345", + "streetAddress": "HappyPets Strasse 15" + } + }, + "pda": { + "type": "Property", + "value": "2021-11-12" + }, + "pta": { + "type": "Property", + "value": "11:00:00" + }, + "eda": { + "type": "Property", + "value": "2021-11-12" + }, + "eta": { + "type": "Property", + "value": "11:00:00" + }, + "@context": [ + "https://schema.lab.fiware.org/ld/context" + ] + } + +postgres: + # Enable the deployment of application: postgres + deploymentEnabled: true + + postgresql: + + fullnameOverride: postgresql-ips + + auth: + # Should use a Secret for PWs on production deployments + # Credentials for Keycloak DB + username: keycloak + password: "dbPassword" + enablePostgresUser: true + + # Credentials for postgres admin user + postgresPassword: "dbRootPassword" + + # Init DB + primary: + initdb: + scripts: + create.sh: | + psql postgresql://postgres:${POSTGRES_POSTGRES_PASSWORD}@localhost:5432 -c "CREATE DATABASE keycloak_ips;" + +trusted-issuers-list: + # Enable the deployment of application: trusted-issuers-list + deploymentEnabled: true + + trusted-issuers-list: + + # Ingress + ingress: + til: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: til-ips.dsba.aws.fiware.io + tls: + - hosts: + - til-ips.dsba.aws.fiware.io + secretName: til-ips-dsba-til-tls + tir: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: tir-ips.dsba.aws.fiware.io + tls: + - hosts: + - tir-ips.dsba.aws.fiware.io + secretName: til-ips-dsba-tir-tls + + # Database config + database: + persistence: true + host: mysql-ips + name: til + + # Should use Secret in production environment + username: root + password: "dbPassword" + + # Init data + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + issuers: + - name: mp_create + issuer: + did: "did:web:marketplace.dsba.fiware.dev:did" + credentials: + - validFor: + from: "2022-07-21T17:32:28Z" + to: "2040-07-21T17:32:28Z" + credentialsType: "IpsActivationService" + claims: + - name: "roles" + allowedValues: + - - names: + - "CREATE_ISSUER" + target: "did:web:ips.dsba.aws.fiware.io:did" + - validFor: + from: "2022-07-21T17:32:28Z" + to: "2040-07-21T17:32:28Z" + credentialsType: "VerifiableCredential" + +vcwaltid: + # Enable the deployment of application: vcwaltid + deploymentEnabled: true + + # Organisation DID + did: did:web:ips.dsba.aws.fiware.io:did + ingress: + enabled: true + host: ips.dsba.aws.fiware.io + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + tls: + enabled: true + route: + enabled: false + + # Walt-id config + vcwaltid: + + # Persistence + persistence: + enabled: true + pvc: + size: 1Gi + + # List of templates to be created + templates: + GaiaXParticipantCredential.json: | + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://registry.lab.dsba.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": [ + "VerifiableCredential" + ], + "id": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "issuer": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "issuanceDate": "2023-03-21T12:00:00.148Z", + "credentialSubject": { + "id": "did:web:raw.githubusercontent.com:egavard:payload-sign:master", + "type": "gx:LegalParticipant", + "gx:legalName": "dsba compliant participant", + "gx:legalRegistrationNumber": { + "gx:vatID": "MYVATID" + }, + "gx:headquarterAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx-terms-and-conditions:gaiaxTermsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700" + } + } + NaturalPersonCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "familyName": "Happy", + "firstName": "User", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "LegalPersonCredential"] + } + MarketplaceUserCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "email": "normal-user@fiware.org", + "familyName": "IPS", + "firstName": "employee", + "lastName": "IPS", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["MarketplaceUserCredential"] + } + EmployeeCredential.json: | + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/FIWARE-Ops/tech-x-challenge/main/schema.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "type": "gx:NaturalParticipant", + "email": "normal-user@fiware.org", + "familyName": "IPS", + "firstName": "employee", + "lastName": "IPS", + "roles": [{ + "names": ["LEGAL_REPRESENTATIVE"], + "target": "did:web:onboarding" + }] + }, + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["EmployeeCredential"] + } + +verifier: + # Enable the deployment of application: verifier + deploymentEnabled: true + + vcverifier: + + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ips-verifier.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ips-verifier.dsba.aws.fiware.io + secretName: verifier-ips-dsba-tls + + deployment: + + # Logging + logging: + level: DEBUG + pathsToSkip: + - "/health" + + # Walt-id config + ssikit: + auditorUrl: http://ips-dsc-vcwaltid:7003 + + # Verifier config + verifier: + # URL endpoint of data space trusted issuers registry + tirAddress: https://tir.dsba.fiware.dev/v3/issuers + # DID of organisation + did: did:web:ips.dsba.aws.fiware.io:did + + # Config service + configRepo: + configEndpoint: http://ips-dsc-credentials-config-service:8080/ + + +keyrock: + # Enable the deployment of application: keyrock + deploymentEnabled: true + + keyrock: + fullnameOverride: keyrock-ips + + # DB config + db: + user: root + password: "dbPassword" + host: mysql-ips + + # Admin user to be created + admin: + user: admin + password: "admin" + email: admin@fiware.org + + # External hostname of Keyrock + host: https://ar-ips.dsba.aws.fiware.io + + # Ingress + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-fiware-eks + kubernetes.io/ingress.class: nginx + hosts: + - host: ar-ips.dsba.aws.fiware.io + paths: + - / + tls: + - hosts: + - ar-ips.dsba.aws.fiware.io + secretName: ar-ips-dsba-tls + + ## Theme configuration for Keyrock + theme: + ## -- Enable theme + enabled: false + + ## Configuration of Authorisation Registry (AR) + authorisationRegistry: + # -- Enable usage of authorisation registry + enabled: true + # -- Identifier (EORI) of AR + identifier: "did:web:ips.dsba.aws.fiware.io:did" + # -- URL of AR + url: "internal" + + ## Configuration of iSHARE Satellite + satellite: + # -- Enable usage of satellite + enabled: true + # -- Identifier (EORI) of satellite + identifier: "EU.EORI.FIWARESATELLITE" + # -- URL of satellite + url: "https://tir.dsba.fiware.dev" + # -- Token endpoint of satellite + tokenEndpoint: "https://tir.dsba.fiware.dev/token" + # -- Parties endpoint of satellite + partiesEndpoint: "https://tir.dsba.fiware.dev/parties" + + ## -- Configuration of local key and certificate for validation and generation of tokens + token: + # -- Enable storage of local key and certificate + enabled: false + + # ENV variables for Keyrock + additionalEnvVars: + - name: IDM_TITLE + value: "IPS AR" + - name: IDM_DEBUG + value: "true" + - name: DEBUG + value: "*" + - name: IDM_DB_NAME + value: ar_idm_ips + - name: IDM_DB_SEED + value: "true" + - name: IDM_SERVER_MAX_HEADER_SIZE + value: "32768" + - name: IDM_PR_CLIENT_ID + value: "did:web:ips.dsba.aws.fiware.io:did" + - name: IDM_PR_CLIENT_KEY + valueFrom: + secretKeyRef: + name: ips-dsc-vcwaltid-tls-sec + key: tls.key + - name: IDM_PR_CLIENT_CRT + valueFrom: + secretKeyRef: + name: ips-dsc-vcwaltid-tls-sec + key: tls.crt + + # Init data + initData: + initEnabled: true + hook: post-install + backoffLimit: 6 + command: + - /bin/sh + - /scripts/create.sh + volumeMount: + name: scripts + mountPath: /scripts + env: + - name: DB_PASSWORD + value: "dbPassword" + scriptData: + create.sh: |- + mysql -h mysql-ips -u root -p$DB_PASSWORD ar_idm_ips <IPS Keycloak", + "enabled": true, + "attributes": { + "frontendUrl": "https://ips-kc.dsba.aws.fiware.io" + }, + "sslRequired": "none", + "roles": { + "realm": [ + { + "name": "user", + "description": "User privileges", + "composite": false, + "clientRole": false, + "containerId": "fiware-server", + "attributes": {} + } + ], + "client": { + "did:web:onboarding.dsba.fiware.dev:did": [ + { + "name": "LEGAL_REPRESENTATIVE", + "description": "Is allowed to register participants", + "clientRole": true + }, + { + "name": "EMPLOYEE", + "description": "Is allowed to see participants", + "clientRole": true + } + ], + "did:web:marketplace.dsba.fiware.dev:did": [ + { + "name": "customer", + "description": "Is allowed to buy.", + "clientRole": true + }, + { + "name": "seller", + "description": "Is allowed to offer.", + "clientRole": true + } + ], + "did:web:ips.dsba.aws.fiware.io:did": [ + { + "name": "STANDARD_CUSTOMER", + "description": "User to access IPS with read access", + "clientRole": true + }, + { + "name": "GOLD_CUSTOMER", + "description": "User to access IPS with read/write access", + "clientRole": true + } + ] + } + }, + "groups": [ + { + "name": "admin", + "path": "/admin", + "realmRoles": [ + "user" + ] + }, + { + "name": "consumer", + "path": "/consumer", + "realmRoles": [ + "user" + ] + } + ], + "users": [ + { + "username": "the-lear", + "enabled": true, + "email": "lear@ips.org", + "credentials": [ + { + "type": "password", + "value": "the-lear" + } + ], + "clientRoles": { + "did:web:onboarding.dsba.fiware.dev:did": [ + "LEGAL_REPRESENTATIVE", + "EMPLOYEE" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/admin", + "/consumer" + ] + }, + { + "username": "legal-representative", + "enabled": true, + "email": "legal-representative@ips.org", + "firstName": "Legal", + "lastName": "IPSEmployee", + "credentials": [ + { + "type": "password", + "value": "legal-representative" + } + ], + "clientRoles": { + "did:web:marketplace.dsba.fiware.dev:did" : [ + "customer", + "seller" + ], + "did:web:onboarding.dsba.fiware.dev:did": [ + "LEGAL_REPRESENTATIVE" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/admin", + "/consumer" + ] + }, + { + "username": "standard-employee", + "enabled": true, + "email": "standard-employee@ips.org", + "credentials": [ + { + "type": "password", + "value": "standard-employee" + } + ], + "clientRoles": { + "did:web:onboarding.dsba.fiware.dev:did": [ + "EMPLOYEE" + ], + "did:web:ips.dsba.aws.fiware.io:did": [ + "GOLD_CUSTOMER" + ], + "account": [ + "view-profile", + "manage-account" + ] + }, + "groups": [ + "/consumer" + ] + } + ], + "clients": [ + { + "clientId": "did:web:ips.dsba.aws.fiware.io:did", + "enabled": true, + "description": "Client for internal users", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_EmployeeCredential": "ldp_vc,jwt_vc_json", + "EmployeeCredential_claims": "email,firstName,familyName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:marketplace.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect to the marketplace", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_MarketplaceUserCredential": "ldp_vc,jwt_vc_json", + "MarketplaceUserCredential_claims": "email,firstName,lastName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:onboarding.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect the onboarding service at portal.dsba.fiware.dev", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_NaturalPersonCredential": "ldp_vc,jwt_vc_json", + "vctypes_GaiaXParticipantCredential": "ldp_vc,jwt_vc_json", + "vc_subjectDid": "did:web:packetdelivery.dsba.fiware.dev:did", + "vc_gx:legalName": "Packet Delivery Company Inc.", + "GaiaXParticipantCredential_claims": "subjectDid,gx:legalName", + "NaturalPersonCredential_claims": "email,firstName,familyName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + }, + { + "clientId": "did:web:marketplace.dsba.fiware.dev:did", + "enabled": true, + "description": "Client to connect to the marketplace", + "surrogateAuthRequired": false, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "defaultRoles": [], + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "SIOP-2", + "attributes": { + "client.secret.creation.time": "1675260539", + "expiryInMin": "3600", + "vctypes_MarketplaceUserCredential": "ldp_vc,jwt_vc_json", + "MarketplaceUserCredential_claims": "email,firstName,lastName,roles" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [], + "optionalClientScopes": [] + } + ], + "clientScopes": [ + { + "name": "fiware-scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "fiware-scope-object", + "protocol": "openid-connect", + "protocolMapper": "oidc-script-based-protocol-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "multivalued": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "fiware-scope-object", + "script": "/**\n * Available variables: \n * user - the current user\n * realm - the current realm\n * token - the current token\n * userSession - the current userSession\n * keycloakSession - the current userSession\n */\n\nvar ArrayList = Java.type(\"java.util.ArrayList\");\nvar fiware_scope = new ArrayList();\n\nvar forEach = Array.prototype.forEach;\n\nvar fiware_service;\nvar fiware_servicepath;\nvar fiware_entry;\nvar roles = '';\n\nvar orion_client = realm.getClientByClientId('orion-pep');\n\nfiware_service = user.getFirstAttribute('fiware-service');\nfiware_servicepath = user.getFirstAttribute('fiware-servicepath');\nif (fiware_service !== null && fiware_servicepath !== null) {\n\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n\n var roleModels = user.getClientRoleMappings(orion_client);\n if (roleModels.size() > 0) {\n forEach.call(\n user.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = {};\n}\n\nforEach.call(\n user.getGroups().toArray(),\n function (group) {\n\n fiware_service = group.getFirstAttribute('fiware-service');\n fiware_servicepath = group.getFirstAttribute('fiware-servicepath');\n if (fiware_service !== null && fiware_servicepath !== null) {\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n\n var roleModels = group.getClientRoleMappings(orion_client);\n if (roleModels.size() > 0) {\n forEach.call(\n group.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = {};\n } else if (group.getParentId() !== null) {\n fiware_service = group.getParent().getFirstAttribute('fiware-service');\n fiware_servicepath = group.getParent().getFirstAttribute('fiware-servicepath');\n\n if (fiware_service !== null && fiware_servicepath !== null) {\n fiware_entry = {\n \"fiware-service\": fiware_service,\n \"fiware-servicepath\": fiware_servicepath\n };\n var subroleModels = group.getClientRoleMappings(orion_client);\n if (subroleModels.size() > 0) {\n forEach.call(\n group.getClientRoleMappings(orion_client).toArray(),\n function (role) {\n roles = roles + role.getName() + \",\";\n }\n );\n roles = roles.substring(0, roles.length - 1);\n fiware_entry[\"orion-roles\"] = roles;\n roles = '';\n }\n\n fiware_scope.add(JSON.stringify(fiware_entry));\n fiware_entry = '';\n }\n }\n }\n);\n\nexports = fiware_scope;" + } + } + ] + }, + { + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "roles", + "role_list", + "email", + "web-origins", + "profile" + ], + "defaultOptionalClientScopes": [ + "microprofile-jwt", + "phone", + "address", + "offline_access" + ] + } + + + + diff --git a/examples/aws-garnet/scripts/deleteAllPodsFromNamespace.sh b/examples/aws-garnet/scripts/deleteAllPodsFromNamespace.sh new file mode 100755 index 0000000..3cdf8ce --- /dev/null +++ b/examples/aws-garnet/scripts/deleteAllPodsFromNamespace.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Set the fixed namespace +namespace="ips" + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo "kubectl not found. Please install kubectl before running this script." + exit 1 +fi + +# Confirm with the user before proceeding +read -p "This will delete all pods in the '$namespace' namespace. Are you sure? (y/n): " confirm +if [[ $confirm != "y" ]]; then + echo "Operation canceled." + exit 0 +fi + +# Delete all pods in the specified namespace +kubectl delete pods --namespace="$namespace" --all + +echo "Deleting pods in the '$namespace' namespace..." diff --git a/examples/aws-garnet/scripts/eks-cluster-fargateProfiler.sh b/examples/aws-garnet/scripts/eks-cluster-fargateProfiler.sh new file mode 100755 index 0000000..55f246b --- /dev/null +++ b/examples/aws-garnet/scripts/eks-cluster-fargateProfiler.sh @@ -0,0 +1,44 @@ +cat << EOF > ./yaml/eks-cluster-3az.yaml +# A simple example of ClusterConfig object: +--- +apiVersion: eksctl.io/v1alpha5 +kind: ClusterConfig + +metadata: + name: ${ekscluster_name} + region: ${AWS_REGION} + version: "${eks_version}" + +vpc: + id: ${vpc_ID} + subnets: + private: + PrivateSubnet01: + az: ${AWS_REGION}a + id: ${PrivateSubnet01} + PrivateSubnet02: + az: ${AWS_REGION}b + id: ${PrivateSubnet02} + PrivateSubnet03: + az: ${AWS_REGION}c + id: ${PrivateSubnet03} + +secretsEncryption: + keyARN: ${MASTER_ARN} + +fargateProfiles: + - name: eks-fp + selectors: + - namespace: ips + - namespace: kube-system + subnets: + - ${PrivateSubnet01} + - ${PrivateSubnet02} + - ${PrivateSubnet03} + +cloudWatch: + clusterLogging: + enableTypes: + ["api", "audit", "authenticator", "controllerManager", "scheduler"] + +EOF \ No newline at end of file diff --git a/examples/aws-garnet/scripts/kubectlLogsFromNamespace.sh b/examples/aws-garnet/scripts/kubectlLogsFromNamespace.sh new file mode 100755 index 0000000..91c85d1 --- /dev/null +++ b/examples/aws-garnet/scripts/kubectlLogsFromNamespace.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +NAMESPACE="ips" + +# Create a directory for pod logs if it doesn't exist +mkdir -p ./podLogs + +# Associative array to store the last log timestamps for each pod +declare -A lastLogTimestamps + +while true; do + # Get the list of pods in the namespace + PODS=$(kubectl get pods -n $NAMESPACE --no-headers -o custom-columns=":metadata.name") + + for POD in $PODS; do + echo "Updating logs for pod: $POD" + + # Get the last timestamp we fetched logs up to + lastTimestamp="${lastLogTimestamps[$POD]}" + + # Use 'kubectl logs' with '--since-time' to get logs since the last retrieval + logs=$(kubectl logs -n $NAMESPACE $POD --since-time="$lastTimestamp") + + if [[ -z "$logs" ]]; then + echo "No new logs found for pod $POD" + else + # Append the new logs to the pod's log file + echo "$logs" >> "./podLogs/$POD.log" + echo "----------------------------------" >> "./podLogs/$POD.log" + + # Update the last log timestamp for this pod + lastLogTimestamps[$POD]=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + fi + done + + # Sleep for a few seconds before checking for new logs and pods again + sleep 3 +done diff --git a/examples/aws-garnet/static-assets/garnet-ds-connector-scenario1.png b/examples/aws-garnet/static-assets/garnet-ds-connector-scenario1.png new file mode 100644 index 0000000..a2b4f9d Binary files /dev/null and b/examples/aws-garnet/static-assets/garnet-ds-connector-scenario1.png differ diff --git a/examples/aws-garnet/static-assets/garnet-ds-connector-scenario2.png b/examples/aws-garnet/static-assets/garnet-ds-connector-scenario2.png new file mode 100644 index 0000000..dcc6f0c Binary files /dev/null and b/examples/aws-garnet/static-assets/garnet-ds-connector-scenario2.png differ diff --git a/examples/aws-garnet/yaml/aws-load-balancer-controller-service-account.yaml b/examples/aws-garnet/yaml/aws-load-balancer-controller-service-account.yaml new file mode 100644 index 0000000..def69d5 --- /dev/null +++ b/examples/aws-garnet/yaml/aws-load-balancer-controller-service-account.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: aws-load-balancer-controller + name: aws-load-balancer-controller + namespace: kube-system + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam:::role/AmazonEKSLoadBalancerControllerRole diff --git a/examples/aws-garnet/yaml/eks-vpc-3az.yaml b/examples/aws-garnet/yaml/eks-vpc-3az.yaml new file mode 100644 index 0000000..3fb6017 --- /dev/null +++ b/examples/aws-garnet/yaml/eks-vpc-3az.yaml @@ -0,0 +1,621 @@ +--- +AWSTemplateFormatVersion: "2010-09-09" +Description: "Amazon EKS Sample VPC - 3 AZ, Private 3 subnets, Public 3 subnets, 1 IGW, 3 NATGateways, Public RT, 3 Private RT, Security Group for ControlPlane " + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Worker Network Configuration" + Parameters: + - VpcBlock + - AvailabilityZoneA + - AvailabilityZoneB + - AvailabilityZoneC + - PublicSubnet01Block + - PublicSubnet02Block + - PublicSubnet03Block + - PrivateSubnet01Block + - PrivateSubnet02Block + - PrivateSubnet03Block + - TGWSubnet01Block + - TGWSubnet02Block + - TGWSubnet03Block + +Parameters: + VpcBlock: + Type: String + Default: 10.11.0.0/16 + Description: The CIDR range for the VPC. This should be a valid private (RFC 1918) CIDR range. + + AvailabilityZoneA: + Description: "Choose AZ1 for your VPC." + Type: AWS::EC2::AvailabilityZone::Name + Default: "eu-west-1a" + + AvailabilityZoneB: + Description: "Choose AZ2 for your VPC." + Type: AWS::EC2::AvailabilityZone::Name + Default: "eu-west-1b" + + AvailabilityZoneC: + Description: "Choose AZ1 for your VPC." + Type: AWS::EC2::AvailabilityZone::Name + Default: "eu-west-1c" + + PublicSubnet01Block: + Type: String + Default: 10.11.0.0/20 + Description: CidrBlock for public subnet 01 within the VPC + + PublicSubnet02Block: + Type: String + Default: 10.11.16.0/20 + Description: CidrBlock for public subnet 02 within the VPC + + PublicSubnet03Block: + Type: String + Default: 10.11.32.0/20 + Description: CidrBlock for public subnet 03 within the VPC + + PrivateSubnet01Block: + Type: String + Default: 10.11.48.0/20 + Description: CidrBlock for private subnet 01 within the VPC + + PrivateSubnet02Block: + Type: String + Default: 10.11.64.0/20 + Description: CidrBlock for private subnet 02 within the VPC + + PrivateSubnet03Block: + Type: String + Default: 10.11.80.0/20 + Description: CidrBlock for private subnet 03 within the VPC + + TGWSubnet01Block: + Type: String + Default: 10.11.251.0/24 + Description: CidrBlock for TGW subnet 01 within the VPC + + TGWSubnet02Block: + Type: String + Default: 10.11.252.0/24 + Description: CidrBlock for TGW subnet 02 within the VPC + + TGWSubnet03Block: + Type: String + Default: 10.11.253.0/24 + Description: CidrBlock for TGW subnet 03 within the VPC + +Resources: + ##################### + # Create-VPC : VPC # + ##################### + + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcBlock + EnableDnsSupport: true + EnableDnsHostnames: true + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}" + + ######################################################## + # Create-InternetGateway: + ######################################################## + + InternetGateway: + Type: "AWS::EC2::InternetGateway" + + ######################################################## + # Attach - VPC Gateway + ######################################################## + + VPCGatewayAttachment: + Type: "AWS::EC2::VPCGatewayAttachment" + Properties: + InternetGatewayId: !Ref InternetGateway + VpcId: !Ref VPC + + ######################################################## + # Create-Public-Subnet: PublicSubnet01,02,03,04 + ######################################################## + + PublicSubnet01: + Type: AWS::EC2::Subnet + Metadata: + Comment: Public Subnet 01 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PublicSubnet01Block + AvailabilityZone: !Ref AvailabilityZoneA + MapPublicIpOnLaunch: "true" + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PublicSubnet01" + - Key: kubernetes.io/role/elb + Value: 1 + + PublicSubnet02: + Type: AWS::EC2::Subnet + Metadata: + Comment: Public Subnet 02 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PublicSubnet02Block + AvailabilityZone: !Ref AvailabilityZoneB + MapPublicIpOnLaunch: "true" + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PublicSubnet02" + - Key: kubernetes.io/role/elb + Value: 1 + + PublicSubnet03: + Type: AWS::EC2::Subnet + Metadata: + Comment: Public Subnet 03 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PublicSubnet03Block + AvailabilityZone: !Ref AvailabilityZoneC + MapPublicIpOnLaunch: "true" + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PublicSubnet03" + - Key: kubernetes.io/role/elb + Value: 1 + + ##################################################################### + # Create-Public-RouteTable: + ##################################################################### + + PublicRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: Public Subnets + - Key: Network + Value: PublicRT + + ################################################################################################ + # Associate-Public-RouteTable: VPC_Private_Subnet_a,b Accsociate VPC_Private_RouteTable # + ################################################################################################ + PublicSubnet01RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet01 + RouteTableId: !Ref PublicRouteTable + + PublicSubnet02RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet02 + RouteTableId: !Ref PublicRouteTable + + PublicSubnet03RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet03 + RouteTableId: !Ref PublicRouteTable + + ################################################################################################ + # Create Public Routing Table + ################################################################################################ + PublicRoute: + DependsOn: VPCGatewayAttachment + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PublicRouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + ################################################################################################ + # Create-NATGateway: NATGATEWAY01,02,03 + ################################################################################################ + NatGateway01: + DependsOn: + - NatGatewayEIP1 + - PublicSubnet01 + - VPCGatewayAttachment + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt "NatGatewayEIP1.AllocationId" + SubnetId: !Ref PublicSubnet01 + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-NatGatewayAZ1" + + NatGateway02: + DependsOn: + - NatGatewayEIP2 + - PublicSubnet02 + - VPCGatewayAttachment + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt "NatGatewayEIP2.AllocationId" + SubnetId: !Ref PublicSubnet02 + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-NatGatewayAZ2" + + NatGateway03: + DependsOn: + - NatGatewayEIP3 + - PublicSubnet03 + - VPCGatewayAttachment + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt "NatGatewayEIP3.AllocationId" + SubnetId: !Ref PublicSubnet03 + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-NatGatewayAZ3" + + NatGatewayEIP1: + DependsOn: + - VPCGatewayAttachment + Type: "AWS::EC2::EIP" + Properties: + Domain: vpc + + NatGatewayEIP2: + DependsOn: + - VPCGatewayAttachment + Type: "AWS::EC2::EIP" + Properties: + Domain: vpc + + NatGatewayEIP3: + DependsOn: + - VPCGatewayAttachment + Type: "AWS::EC2::EIP" + Properties: + Domain: vpc + + ######################################################## + # Create-Security-Group : ControlPlane + ######################################################## + ControlPlaneSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Cluster communication with worker nodes + VpcId: !Ref VPC + + ######################################################## + # Create-Security-Group : Session Manager + ######################################################## + SSMSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Open-up ports for HTTP/S from All network + GroupName: SSMSG + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + CidrIp: 0.0.0.0/0 + FromPort: "80" + ToPort: "80" + - IpProtocol: tcp + CidrIp: 0.0.0.0/0 + FromPort: "443" + ToPort: "443" + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-SSMSG" + ######################################################## + # Create-Private-Subnet: PrivateSubnet01,02 + ######################################################## + + PrivateSubnet01: + Type: AWS::EC2::Subnet + Metadata: + Comment: Private Subnet 01 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet01Block + AvailabilityZone: !Ref AvailabilityZoneA + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PrivateSubnet01" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + PrivateSubnet02: + Type: AWS::EC2::Subnet + Metadata: + Comment: Private Subnet 02 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet02Block + AvailabilityZone: !Ref AvailabilityZoneB + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PrivateSubnet02" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + PrivateSubnet03: + Type: AWS::EC2::Subnet + Metadata: + Comment: Private Subnet 03 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet03Block + AvailabilityZone: !Ref AvailabilityZoneC + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-PrivateSubnet03" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + ##################################################################### + # Create-Private-RouteTable: PrivateRT01,02 + ##################################################################### + PrivateRouteTable01: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: Private Subnet AZ1 + - Key: Network + Value: PrivateRT01 + + PrivateRouteTable02: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: Private Subnet AZ2 + - Key: Network + Value: PrivateRT02 + + PrivateRouteTable03: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: Private Subnet AZ3 + - Key: Network + Value: PrivateRT03 + + ################################################################################################ + # Associate-Private-RouteTable: VPC_Private_Subnet_a,b Accsociate VPC_Private_RouteTable # + ################################################################################################ + + PrivateSubnet01RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet01 + RouteTableId: !Ref PrivateRouteTable01 + + PrivateSubnet02RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet02 + RouteTableId: !Ref PrivateRouteTable02 + + PrivateSubnet03RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet03 + RouteTableId: !Ref PrivateRouteTable03 + + ################################################################################################ + # Add Prviate Routing Table + ################################################################################################ + + PrivateRoute01: + DependsOn: + - VPCGatewayAttachment + - NatGateway01 + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable01 + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway01 + + PrivateRoute02: + DependsOn: + - VPCGatewayAttachment + - NatGateway02 + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable02 + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway02 + + PrivateRoute03: + DependsOn: + - VPCGatewayAttachment + - NatGateway03 + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable03 + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway03 + ######################################################## + # Create-TGW-Subnet: TGWSubnet01,02,03 + ######################################################## + + TGWSubnet01: + Type: AWS::EC2::Subnet + Metadata: + Comment: TGW Subnet 01 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref TGWSubnet01Block + AvailabilityZone: !Ref AvailabilityZoneA + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-TGWSubnet01" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + TGWSubnet02: + Type: AWS::EC2::Subnet + Metadata: + Comment: TGW Subnet 02 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref TGWSubnet02Block + AvailabilityZone: !Ref AvailabilityZoneB + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-TGWSubnet02" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + TGWSubnet03: + Type: AWS::EC2::Subnet + Metadata: + Comment: TGW Subnet 03 + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref TGWSubnet03Block + AvailabilityZone: !Ref AvailabilityZoneC + Tags: + - Key: Name + Value: !Sub "${AWS::StackName}-TGWSubnet03" + - Key: kubernetes.io/role/internal-elb + Value: 1 + + ##################################################################### + # Create-TGW-RouteTable: TGWRT01,02,03,04 + ##################################################################### + TGWRouteTable01: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: TGW Subnet AZ1 + - Key: Network + Value: TGWRT01 + + TGWRouteTable02: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: TGW Subnet AZ2 + - Key: Network + Value: TGWRT02 + + TGWRouteTable03: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: TGW Subnet AZ3 + - Key: Network + Value: TGWRT03 + + ################################################################################################ + # Associate-TGW-RouteTable: VPC_TGW_Subnet_a,b Accsociate VPC_TGW_RouteTable # + ################################################################################################ + + TGWSubnet01RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref TGWSubnet01 + RouteTableId: !Ref TGWRouteTable01 + + TGWSubnet02RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref TGWSubnet02 + RouteTableId: !Ref TGWRouteTable02 + + TGWSubnet03RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref TGWSubnet03 + RouteTableId: !Ref TGWRouteTable03 + + ###################################################################### + # Create-System-Manager-Endpoint: Create VPC SystemManager Endpoint # + ###################################################################### + + SSMEndpoint: + Type: AWS::EC2::VPCEndpoint + Properties: + VpcId: !Ref VPC + ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" + VpcEndpointType: Interface + PrivateDnsEnabled: True + SubnetIds: + - Ref: PrivateSubnet01 + - Ref: PrivateSubnet02 + - Ref: PrivateSubnet03 + SecurityGroupIds: + - Ref: SSMSG + + SSMMEndpoint: + Type: AWS::EC2::VPCEndpoint + Properties: + VpcId: !Ref VPC + ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" + VpcEndpointType: Interface + PrivateDnsEnabled: True + SubnetIds: + - Ref: PrivateSubnet01 + - Ref: PrivateSubnet02 + - Ref: PrivateSubnet03 + SecurityGroupIds: + - Ref: SSMSG + +Outputs: + VpcId: + Description: The VPC Id + Value: !Ref VPC + + PublicSubnet01: + Description: PublicSubnet01 ID in the VPC + Value: !Ref PublicSubnet01 + + PublicSubnet02: + Description: PublicSubnet02 ID in the VPC + Value: !Ref PublicSubnet02 + + PublicSubnet03: + Description: PublicSubnet03 ID in the VPC + Value: !Ref PublicSubnet03 + + PrivateSubnet01: + Description: PrivateSubnet01 ID in the VPC + Value: !Ref PrivateSubnet01 + + PrivateSubnet02: + Description: PrivateSubnet02 ID in the VPC + Value: !Ref PrivateSubnet02 + + PrivateSubnet03: + Description: PrivateSubnet03 ID in the VPC + Value: !Ref PrivateSubnet03 + + SecurityGroups: + Description: Security group for the cluster control plane communication with worker nodes + Value: !Join [",", [!Ref ControlPlaneSecurityGroup]] + + TGWSubnet01: + Description: TGWSubnet01 ID in the VPC + Value: !Ref TGWSubnet01 + + TGWSubnet02: + Description: TGWSubnet02 ID in the VPC + Value: !Ref TGWSubnet02 + + TGWSubnet03: + Description: TGWSubnet03 ID in the VPC + Value: !Ref TGWSubnet03 \ No newline at end of file diff --git a/examples/aws-garnet/yaml/nginx-ingress-controller.yaml b/examples/aws-garnet/yaml/nginx-ingress-controller.yaml new file mode 100644 index 0000000..17bf569 --- /dev/null +++ b/examples/aws-garnet/yaml/nginx-ingress-controller.yaml @@ -0,0 +1,653 @@ +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx + namespace: kube-system +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resourceNames: + - ingress-nginx-leader + resources: + - leases + verbs: + - get + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission + namespace: kube-system +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets + - namespaces + verbs: + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission +rules: +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: kube-system +--- +apiVersion: v1 +data: + allow-snippet-annotations: "true" + http-snippet: | + server { + listen 2443; + return 308 https://$host$request_uri; + } + proxy-real-ip-cidr: 10.11.0.0/16 + use-forwarded-headers: "true" +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-controller + namespace: kube-system +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60" + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + #service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https + service.beta.kubernetes.io/aws-load-balancer-type: nlb + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-controller + namespace: kube-system +spec: + externalTrafficPolicy: Local + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - appProtocol: http + name: http + port: 80 + protocol: TCP + targetPort: tohttps + - appProtocol: https + name: https + port: 443 + protocol: TCP + targetPort: http + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: LoadBalancer +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-controller-admission + namespace: kube-system +spec: + ports: + - appProtocol: https + name: https-webhook + port: 443 + targetPort: webhook + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-controller + namespace: kube-system +spec: + minReadySeconds: 0 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + template: + metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + spec: + containers: + - args: + - /nginx-ingress-controller + - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller + - --election-id=ingress-nginx-leader + - --controller-class=k8s.io/ingress-nginx + - --ingress-class=nginx + - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + - --validating-webhook=:8443 + - --validating-webhook-certificate=/usr/local/certificates/cert + - --validating-webhook-key=/usr/local/certificates/key + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + image: registry.k8s.io/ingress-nginx/controller:v1.8.1@sha256:e5c4824e7375fcf2a393e1c03c293b69759af37a9ca6abdb91b13d78a93da8bd + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: controller + ports: + - containerPort: 80 + name: http + protocol: TCP + - containerPort: 80 + name: https + protocol: TCP + - containerPort: 2443 + name: tohttps + protocol: TCP + - containerPort: 8443 + name: webhook + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 90Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + runAsUser: 101 + volumeMounts: + - mountPath: /usr/local/certificates/ + name: webhook-cert + readOnly: true + dnsPolicy: ClusterFirst + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: ingress-nginx + terminationGracePeriodSeconds: 300 + volumes: + - name: webhook-cert + secret: + secretName: ingress-nginx-admission +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission-create + namespace: kube-system +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission-create + spec: + containers: + - args: + - create + - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc + - --namespace=$(POD_NAMESPACE) + - --secret-name=ingress-nginx-admission + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b + imagePullPolicy: IfNotPresent + name: create + securityContext: + allowPrivilegeEscalation: false + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission-patch + namespace: kube-system +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission-patch + spec: + containers: + - args: + - patch + - --webhook-name=ingress-nginx-admission + - --namespace=$(POD_NAMESPACE) + - --patch-mutating=false + - --secret-name=ingress-nginx-admission + - --patch-failure-policy=Fail + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b + imagePullPolicy: IfNotPresent + name: patch + securityContext: + allowPrivilegeEscalation: false + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: nginx +spec: + controller: k8s.io/ingress-nginx +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: ingress-nginx + app.kubernetes.io/version: 1.8.1 + name: ingress-nginx-admission +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: ingress-nginx-controller-admission + namespace: kube-system + path: /networking/v1/ingresses + failurePolicy: Fail + matchPolicy: Equivalent + name: validate.nginx.ingress.kubernetes.io + rules: + - apiGroups: + - networking.k8s.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - ingresses + sideEffects: None