Repository: omerbsezer/Fast-Terraform Branch: main Commit: 5ff4bbac8abd Files: 179 Total size: 895.7 KB Directory structure: gitextract_h9ee0hq9/ ├── LAB00-Terraform-Install-AWS-Configuration.md ├── LAB01-Terraform-Docker-Without-Cloud.md ├── LAB02-Resources-Basic-EC2.md ├── LAB03-Variables-Locals-Output-EC2.md ├── LAB04-Meta-Arguments-IAM-User-Group-Policy.md ├── LAB05-Dynamic-Blocks-Security-Groups-EC2.md ├── LAB06-Data-Sources-EC2.md ├── LAB07-Provisioners-Null-Resources.md ├── LAB08-Modules-EC2.md ├── LAB09-Workspaces-EC2.md ├── LAB10-Templates-User-Policy.md ├── LAB11-Backend-Remote-State.md ├── LICENSE ├── README.md ├── SAMPLE01-EC2-VPC-Ubuntu-Win-SSH-RDP.md ├── SAMPLE02-Lambda-API-Gateway-Python.md ├── SAMPLE03-EC2-EBS-EFS.md ├── SAMPLE04-ECR-ECS-ELB-VPC-ECS-Service.md ├── SAMPLE05-Lambda-Container-ApiGateway-FlaskApp.md ├── SAMPLE06-EKS-ManagedNodes-Blueprint.md ├── SAMPLE07-CodeCommit-Pipeline-Build-Deploy-Lambda.md ├── SAMPLE08-S3-CloudFront-Static-WebSite.md ├── SAMPLE09-GitlabServer-on-Premise-GitlabRunner-on-EC2.md ├── SAMPLE10-MLOps-SageMaker-GitHub-Codepipeline-CodeBuild-CodeDeploy.md ├── Terraform-Cheatsheet.md ├── labs/ │ ├── backend-remote-state/ │ │ └── main.tf │ ├── basic-resource-ec2-ubuntu/ │ │ └── main.tf │ ├── data-sources/ │ │ └── main.tf │ ├── dynamic-blocks/ │ │ └── main.tf │ ├── iamuser-metaargs-count-for-foreach-map/ │ │ ├── count/ │ │ │ └── main.tf │ │ ├── for_each/ │ │ │ └── main.tf │ │ └── map/ │ │ └── main.tf │ ├── modules/ │ │ ├── main.tf │ │ ├── module1/ │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ └── module2/ │ │ ├── main.tf │ │ └── variables.tf │ ├── provisioners-nullresources/ │ │ ├── main.tf │ │ └── test-file.txt │ ├── template/ │ │ ├── main.tf │ │ └── policy.tftpl │ ├── terraform-docker-without-cloud/ │ │ └── main.tf │ ├── variables-locals-output/ │ │ ├── main.tf │ │ ├── terraform-dev.tfvars │ │ ├── terraform-prod.tfvars │ │ └── variables.tf │ └── workspace/ │ ├── main.tf │ ├── terraform-dev.tfvars │ ├── terraform-prod.tfvars │ └── variables.tf └── samples/ ├── codecommit-codepipeline-codebuild-codedeploy-lambda-container/ │ ├── lambda_bootstrap/ │ │ ├── lambda/ │ │ │ ├── Dockerfile │ │ │ ├── aws-lambda-url.py │ │ │ ├── docker-test.sh │ │ │ └── requirements.txt │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── terraform.tfvars │ │ └── variables.tf │ ├── main.tf │ ├── modules/ │ │ ├── codecommit/ │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── codepipeline/ │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── roles.tf │ │ │ ├── templates/ │ │ │ │ ├── buildspec_build.yml │ │ │ │ └── buildspec_deploy.yml │ │ │ └── variables.tf │ │ └── ecr/ │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ └── variables.tf ├── ec2-ebs-efs/ │ ├── ebs.tf │ ├── efs.tf │ └── main.tf ├── ec2-vpc-ubuntu-win-ssh-rdp/ │ └── main.tf ├── ecr-ecs-elb-vpc-ecsservice-container/ │ ├── 1_vpc.tf │ ├── 2_ecs.tf │ ├── 3_elb.tf │ ├── 4_ecs_service.tf │ ├── ecr/ │ │ └── 0_ecr.tf │ └── flask-app/ │ ├── Dockerfile │ ├── README.md │ ├── app/ │ │ ├── app.py │ │ ├── hello.py │ │ ├── init_db.py │ │ ├── schema.sql │ │ ├── static/ │ │ │ └── css/ │ │ │ └── style.css │ │ └── templates/ │ │ ├── base.html │ │ ├── create.html │ │ ├── edit.html │ │ ├── index.html │ │ └── post.html │ └── requirements.txt ├── eks-managed-node-blueprint/ │ ├── README.md │ └── main.tf ├── gitlabserver-on-premise-runner-on-EC2/ │ ├── docker-compose.yml │ ├── main.tf │ └── test-gitlab-runner/ │ ├── docker-windows/ │ │ └── Dockerfile │ ├── gitlab-ci.yml │ ├── requirements.txt │ ├── src/ │ │ ├── __init__.py │ │ └── main.py │ └── test/ │ ├── __init__.py │ └── test_main.py ├── lambda-container-apigateway-flaskapp/ │ ├── 1_lambda.tf │ ├── 2_api_gateway.tf │ ├── ecr/ │ │ └── 0_ecr.tf │ └── flask-app-serverless/ │ ├── Dockerfile │ ├── README.md │ ├── app/ │ │ ├── app.py │ │ ├── hello.py │ │ ├── init_db.py │ │ ├── schema.sql │ │ ├── static/ │ │ │ └── css/ │ │ │ └── style.css │ │ └── templates/ │ │ ├── base.html │ │ ├── create.html │ │ ├── edit.html │ │ ├── index.html │ │ └── post.html │ └── requirements.txt ├── lambda-role-policy-apigateway-python/ │ ├── api-gateway.tf │ ├── code/ │ │ └── main.py │ └── lambda.tf ├── mlops-sagemaker-github-codepipeline-codebuild-codedeploy/ │ ├── Notebooks/ │ │ ├── SageMaker_Customer_Churn_XGB_Pipeline.ipynb │ │ ├── SageMaker_Customer_Churn_XGB_end2end.ipynb │ │ ├── assume-role.json │ │ ├── preprocess.py │ │ └── test.csv │ ├── modelbuild_pipeline/ │ │ ├── README.md │ │ ├── pipelines/ │ │ │ ├── __init__.py │ │ │ ├── __version__.py │ │ │ ├── _utils.py │ │ │ ├── customer_churn/ │ │ │ │ ├── __init__.py │ │ │ │ ├── evaluate.py │ │ │ │ ├── pipeline.py │ │ │ │ └── preprocess.py │ │ │ ├── get_pipeline_definition.py │ │ │ └── run_pipeline.py │ │ ├── setup.cfg │ │ ├── setup.py │ │ ├── tests/ │ │ │ └── test_pipelines.py │ │ └── tox.ini │ ├── modeldeploy_pipeline/ │ │ ├── README.md │ │ ├── build.py │ │ ├── endpoint-config-template.yml │ │ ├── fix_model_permission.py │ │ ├── prod-config.json │ │ ├── setup.py │ │ ├── staging-config.json │ │ └── test/ │ │ ├── test.py │ │ ├── test_buildspec.yml │ │ ├── test_buildspec_singleaccount.yml │ │ └── test_singleaccount.py │ └── terraform/ │ ├── events.tf │ ├── iam_roles.tf │ ├── main.tf │ ├── modelbuild_buildspec.yml │ ├── modelbuild_ci_pipeline.tf │ ├── modelbuild_codebuild.tf │ ├── modelbuild_hooks.tf │ ├── modeldeploy_buildspec.yml │ ├── modeldeploy_cd_pipeline.tf │ ├── modeldeploy_codebuild.tf │ ├── modeldeploy_hooks.tf │ ├── modeldeploy_testbuild.tf │ ├── s3.tf │ ├── terraform.tfvars │ └── variables.tf └── s3-cloudfront-static-website/ ├── cloudfront.tf ├── s3.tf └── website/ ├── css/ │ └── styles.css ├── error.html ├── index.html └── js/ └── scripts.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: LAB00-Terraform-Install-AWS-Configuration.md ================================================ ## LAB: Terraform Install, AWS Configuration with Terraform This scenario shows: - how to configure your Terraform with AWS ## Steps - Install Terraform: - https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli - For Windows: ``` choco install terraform ``` - Then, add Terraform app into the Environment Variables. ![image](https://user-images.githubusercontent.com/10358317/226994354-ef99ce99-c9b7-480e-ad09-36b88c6fe841.png) - Download AWS CLI: - https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html - For Windows: ``` msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi ``` ![image](https://user-images.githubusercontent.com/10358317/226995232-d88e1533-2aa0-4d6c-b201-5ab1c58d389f.png) - Create AWS Root Account: - https://repost.aws/knowledge-center/create-and-activate-aws-account - Create IAM Admin User: ![image](https://user-images.githubusercontent.com/10358317/226996766-678ae1af-1161-4d8a-9b49-4bb3915b1ba5.png) - Create AWS Access Keys. - Access keys consist of two parts: - an access key ID (for example, AKIAIOSFODNN7EXAMPLE), - a secret access key (for example, wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY). - You must use both the access key ID and secret access key together to authenticate your requests. ![image](https://user-images.githubusercontent.com/10358317/226998180-cd80ae08-a05c-479b-baad-fae9c2f094df.png) - Configure AWS with AWS CLI (use command: aws configure): ``` $ aws configure AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY Default region name [None]: eu-central-1 Default output format [None]: json ``` - After command, AWS creates: - Credentials file => C:\Users\username\.aws\credentials - Config file => C:\Users\username\.aws\config ``` # credentials file [default] aws_access_key_id = AKIAIOSFODNN7EXAMPLE aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY ``` ``` # config file [default] region = eu-central-1 output = json ``` - Now, it is your ready to run Terraform! ## Reference - Terraform Install: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli - AWS CLI Install: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html - AWS Access Keys: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html - AWS CLI Configuration: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html ================================================ FILE: LAB01-Terraform-Docker-Without-Cloud.md ================================================ ## LAB-01: Terraform Docker => Pull Docker Image, Create Docker Container on Local Machine This scenario shows: - how to use Terraform to manage Docker commands (image pull, container create, etc.) - without using any cloud, with Terraform Docker module, learning Terraform and making more practice could be easier. **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/terraform-docker-without-cloud/main.tf ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) - Install Docker on your system. - Ubuntu: https://docs.docker.com/engine/install/ubuntu/ - Windows: https://docs.docker.com/desktop/install/windows-install/ - Mac: https://docs.docker.com/desktop/install/mac-install/ ## Steps - Create main.tf and copy the code: ``` # main.tf terraform { required_providers { docker = { source = "kreuzwerker/docker" version = "~> 3.0.2" } } } provider "docker" { host = "npipe:////.//pipe//docker_engine" } resource "docker_image" "windows" { name = "mcr.microsoft.com/powershell:lts-windowsservercore-1809" keep_locally = true } # docker container run -p 80:8000 --name=tutorial -it mcr.microsoft.com/powershell:lts-windowsservercore-1809 powershell resource "docker_container" "windows" { image = docker_image.windows.image_id name = "tutorial" stdin_open = true # docker run -i tty = true # docker run -t entrypoint = ["powershell"] ports { internal = 80 external = 8000 } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/terraform-docker-without-cloud/main.tf ![image](https://user-images.githubusercontent.com/10358317/227287393-09ff08a1-9db2-4fc5-98e8-1a20c2bdf9be.png) - Run init command: ``` terraform init ``` ![image](https://user-images.githubusercontent.com/10358317/227279233-74013a80-0a71-4c0c-84b7-e9cec6c9d30f.png) - Validate file: ``` terraform validate ``` - Run plan command: ``` terraform plan ``` ![image](https://user-images.githubusercontent.com/10358317/227279536-a2f72789-36f6-4ee1-82df-a0bed834d34d.png) - Run apply command to create resources. Then, Terraform asks to confirm, write "yes": ``` terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/227281131-7463a9dc-1f61-4f48-a906-410725c0af19.png) - With "docker container ls -a", running container is viewed: ![image](https://user-images.githubusercontent.com/10358317/227280862-04483fb7-530d-4a75-ad22-21e8a5cbf49b.png) - Run following command to connect container powershell: ``` docker container exec -it tutorial powershell ``` - Now, we are in the container, to prove it, we are looking at the users in the container (ContainerAdministrator, ContainerUser) ![image](https://user-images.githubusercontent.com/10358317/227282456-4452cbe2-611c-491a-a9f7-f6bbaab28d69.png) - Before Terraform runs the container, it pulls the image: ![image](https://user-images.githubusercontent.com/10358317/227283686-a6b2ee63-8c01-4610-84c0-3f5d5f622166.png) - When "keep_locally = true" in image part, image will be kept after terraform destroy. ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/227284301-9fa06ebe-faa5-47aa-ac52-234bf6ca2c4e.png) - After destroy command, container is deleted, but image is still kept ![image](https://user-images.githubusercontent.com/10358317/227285010-450e8cc2-b3e8-4dd5-9272-65d9f69bfd18.png) - With Terraform, we can manage docker images, containers.. - More information: https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container ================================================ FILE: LAB02-Resources-Basic-EC2.md ================================================ ## LAB-02: Resources => Provision Basic EC2 (Ubuntu 22.04) This scenario shows: - how to create EC2 with Ubuntu 22.04 **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/basic-resource-ec2-ubuntu/main.tf ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Create main.tf and copy the code ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/basic-resource-ec2-ubuntu/main.tf ![image](https://user-images.githubusercontent.com/10358317/227008999-64295471-d0b3-48c8-a717-e6323c2091e9.png) - Run init command: ``` terraform init ``` ![image](https://user-images.githubusercontent.com/10358317/227001872-ef246cee-f8a9-4c66-845b-17d7ad32e8a5.png) - Init command downloads required executable files from Terraform (.terraform): ![image](https://user-images.githubusercontent.com/10358317/227002641-a15545c6-9e40-4f23-80a6-ed07b2c4b7b1.png) - Validate file: ``` terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/227002220-a6d67605-e870-40e1-9e0f-252f68c4ba0a.png) - Run plan command: ``` terraform plan ``` ![image](https://user-images.githubusercontent.com/10358317/227003057-6805d76e-75a1-440d-a288-876668fe7e27.png) ![image](https://user-images.githubusercontent.com/10358317/227003299-c02f0d18-7588-4b41-8a80-5e180ffea50e.png) - Run apply command to create resources. Then, Terraform asks to confirm, write "yes": ``` terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/227007465-7f4a1315-6f16-45af-a21b-25d896a718a7.png) - It creates the resources that are defined in the main.tf: ![image](https://user-images.githubusercontent.com/10358317/227007779-6fea1041-31c3-4f0f-b8c2-a1673bfef150.png) - After apply command, terraform creates state files: ![image](https://user-images.githubusercontent.com/10358317/227009288-9009ff78-0b24-451a-a5a5-5533b9d3c0fb.png) - On AWS, go to EC2 services: ![image](https://user-images.githubusercontent.com/10358317/227004589-8329e520-ce4a-4cf7-8eb9-a4e71f4d46a1.png) - On EC2 Dashboard: ![image](https://user-images.githubusercontent.com/10358317/227004417-47f719c4-8ccc-413d-a17d-11409e82173d.png) - Security Group Configuration is configured as default, inbound ports are defined as "all" (this is not good for security, but for now, it's ok!), egress ports are defined as "all" ![image](https://user-images.githubusercontent.com/10358317/227005971-42e763de-734c-47d7-9916-a490ee97f8ee.png) - Network Configuration is configured as default, but availability zone is defined as "eu-central-1" ![image](https://user-images.githubusercontent.com/10358317/227005316-351ea88a-dbd5-4820-a2f8-0d768437c3c7.png) - Storage Configuration is configured as default, 8GB EBS (Elastic Block Storage) is attached: ![image](https://user-images.githubusercontent.com/10358317/227005692-9721e61a-9217-45ca-a466-d4717fb91ff1.png) - Delete all resources. Then, Terraform asks to confirm, write "yes":: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229283240-60381539-93e7-4795-bdb0-7ab179cf7ed4.png) ![image](https://user-images.githubusercontent.com/10358317/229283607-5eb607f0-ad8a-46fb-9f1b-e6aee38fb4db.png) - On AWS EC2 Instances, EC2 was terminated, it'll be disappeared in max. 1 hour. Once an EC2 is terminated, that EC2 is deleted permanently: ![image](https://user-images.githubusercontent.com/10358317/227008611-4c7e0d5f-2765-47f5-9b6f-af78d4d7314f.png) - Destroy command is important to terminate the resources. - If you forget the destroy command, your EC2 runs until termination and you MUST pay the usage price of EC2. ![image](https://user-images.githubusercontent.com/10358317/227049834-12b0223c-fd10-46ca-9602-391e6267434f.png) - It is really IMPORTANT to check whether unnecessary paid services are closed or not. ![image](https://user-images.githubusercontent.com/10358317/227050101-0dd27ac2-b10f-4812-90f9-18cfbc51de80.png) ================================================ FILE: LAB03-Variables-Locals-Output-EC2.md ================================================ ## LAB-03: Variables, Locals, Output => Provision EC2s This scenario shows: - how to create EC2 using Variables, Locals and Output **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/labs/variables-locals-output ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Create main.tf and copy the code: ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { staging_env = "staging" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "${local.staging_env}-vpc-tag" } } resource "aws_subnet" "my_subnet" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/16" availability_zone = var.availability_zone tags = { Name = "${local.staging_env}-subnet-tag" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "${local.staging_env}-Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "${local.staging_env}- Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.my_subnet.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_instance" "ec2_example" { ami = var.ami instance_type = var.instance_type subnet_id = aws_subnet.my_subnet.id associate_public_ip_address = true tags = { Name = var.tag } } # output single values output "public_ip" { value = aws_instance.ec2_example.public_ip } # output single values output "public_dns" { value = aws_instance.ec2_example.public_dns } # output multiple values output "instance_ips" { value = { public_ip = aws_instance.ec2_example.public_ip private_ip = aws_instance.ec2_example.private_ip } } ``` - Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/variables-locals-output/main.tf ![image](https://user-images.githubusercontent.com/10358317/227576390-d65cfefb-ac47-4873-9b49-31774174d2d8.png) - Create variables.tf: ``` variable "instance_type" { type = string description = "EC2 Instance Type" } variable "tag" { type = string description = "The tag for the EC2 instance" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "availability_zone" { type = string description = "The project availability zone" default = "eu-central-1c" } variable "ami" { type = string description = "The project region" } ``` - Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/variables-locals-output/variables.tf ![image](https://user-images.githubusercontent.com/10358317/227576884-4a6d5052-cc5b-47e7-bd9f-30d5955bdcda.png) - Create terraform-dev.tfvars: ``` instance_type = "t2.nano" tag = "EC2 Instance for DEV" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt ``` - Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/variables-locals-output/terraform-dev.tfvars ![image](https://user-images.githubusercontent.com/10358317/227577004-107a8d30-b137-43bc-835c-74c3ce27117d.png) - Create terraform-prod.tfvars: ``` instance_type = "t2.micro" tag = "EC2 Instance for PROD" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt ``` - Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/variables-locals-output/terraform-prod.tfvars ![image](https://user-images.githubusercontent.com/10358317/227577134-e6023e7a-7d54-4406-ae8a-97bf3e89e9fe.png) - Run init command: ``` terraform init ``` ![image](https://user-images.githubusercontent.com/10358317/227567351-655c2233-a5fd-4fea-a9d2-ddcc32f12d0e.png) - Validate file: ``` terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/227567482-7592f836-5d61-46c7-8467-0bc36fc6e852.png) - Run plan command with DEV tfvar file: ``` terraform plan --var-file="terraform-dev.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/227568275-14f0bdd6-20b7-450e-8f19-383a7c36a629.png) - Run apply command to create resources, with DEV tfvar file. Then, Terraform asks to confirm, write "yes": ``` terraform apply --var-file="terraform-dev.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/227573622-460f5970-612f-49ed-bcaa-cfeecd9d2c49.png) ![image](https://user-images.githubusercontent.com/10358317/229284493-13646f69-5e8d-4dc0-aa21-6d6149b8acb3.png) - On AWS EC2 Instances: ![image](https://user-images.githubusercontent.com/10358317/227574209-11e89611-4776-4fc0-a76d-80c56a263c71.png) - On VPC Section: ![image](https://user-images.githubusercontent.com/10358317/227575010-944ff35b-47a8-4caf-81e8-46e4d7089d4a.png) - Destroy DEV Environment: ``` terraform destroy --var-file="terraform-dev.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/229284618-506f4daf-70c7-4411-b399-9e865b4d81a1.png) ![image](https://user-images.githubusercontent.com/10358317/229284719-ac78448b-7386-4fe8-afcc-8f3a6ae6edc8.png) - Update locals for PROD in main.tf: ``` .... locals { staging_env = "product" } ..... ``` ![image](https://user-images.githubusercontent.com/10358317/227581270-6cdba303-e29c-486e-abf3-831529a459b9.png) - Run plan command with PROD tfvar file: ``` terraform plan --var-file="terraform-prod.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/227578564-ff43db34-2e35-45d8-bffa-f5d132dd0967.png) - Run apply command to create resources, with PROD tfvar file. Then, Terraform asks to confirm, write "yes": ``` terraform apply --var-file="terraform-prod.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/227578878-4d3e2d4c-2878-4b90-8751-246598748da6.png) - On AWS EC2 Instances: ![image](https://user-images.githubusercontent.com/10358317/227579473-a9f883ee-28d7-4a39-9647-6371712303ea.png) - On VPC Section: ![image](https://user-images.githubusercontent.com/10358317/227579952-e70efbd3-0d42-46b7-b661-ad7b4f9ba41e.png) - Destroy PROD Environment: ``` terraform destroy --var-file="terraform-prod.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/229284826-cbaf8761-e960-44ed-867d-3b1f6e3ae88f.png) ![image](https://user-images.githubusercontent.com/10358317/229284932-f216c36c-235d-4511-8510-11f613321422.png) - On EC2 Instances, all instances are terminated: ![image](https://user-images.githubusercontent.com/10358317/227580749-328c2a30-ae0c-441f-9fa0-e40f20fa818b.png) ================================================ FILE: LAB04-Meta-Arguments-IAM-User-Group-Policy.md ================================================ ## LAB-04: Meta Arguments (Count, For_Each, Map) => Provision IAM Users, User Groups, Policies, Attachment Policy-User Group This scenario shows: - how to create IAM User, User Groups, Permission Policies, Attachment Policy-User Group - how to use Count, For_Each, Map **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/labs/iamuser-metaargs-count-for-foreach-map ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps ### Count - Create main.tf under count directory and copy the code: ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # User - User Group Attachment (With Index Count) resource "aws_iam_user_group_membership" "user1_group_attach" { user = aws_iam_user.user_example[0].name groups = [ aws_iam_group.admin_group.name, aws_iam_group.dev_group.name, ] } resource "aws_iam_user_group_membership" "user2_group_attach" { user = aws_iam_user.user_example[1].id groups = [ aws_iam_group.admin_group.name ] } resource "aws_iam_user_group_membership" "user3_group_attach" { user = aws_iam_user.user_example[2].name groups = [ aws_iam_group.dev_group.name ] } ##################################################### # User Group Definition resource "aws_iam_group" "admin_group" { name = "admin_group" } resource "aws_iam_group" "dev_group" { name = "dev_group" } ##################################################### # Policy Definition, Policy-Group Attachment data "aws_iam_policy_document" "admin_policy" { statement { effect = "Allow" actions = ["*"] resources = ["*"] } } resource "aws_iam_policy" "admin_policy" { name = "admin-policy" description = "Admin policy" policy = data.aws_iam_policy_document.admin_policy.json } data "aws_iam_policy_document" "ec2_policy" { statement { effect = "Allow" actions = ["ec2:Describe*"] resources = ["*"] } } resource "aws_iam_policy" "ec2_policy" { name = "ec2-policy" description = "EC2 policy" policy = data.aws_iam_policy_document.ec2_policy.json } ##################################################### # Policy Attachment to the Admin, Dev Group resource "aws_iam_group_policy_attachment" "admin_group_admin_policy_attach" { group = aws_iam_group.admin_group.name policy_arn = aws_iam_policy.admin_policy.arn } resource "aws_iam_group_policy_attachment" "dev_group_ec2_policy_attach" { group = aws_iam_group.dev_group.name policy_arn = aws_iam_policy.ec2_policy.arn } ##################################################### # Username Definition # With Count resource "aws_iam_user" "user_example" { count = length(var.user_names) name = var.user_names[count.index] } # count, use list variable "user_names" { description = "IAM usernames" type = list(string) default = ["username1_admin_dev", "username2_admin", "username3_dev_ec2"] } ##################################################### # With for loop output "print_the_names" { value = [for name in var.user_names : name] } ``` **File:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/iamuser-metaargs-count-for-foreach-map/count/main.tf ![image](https://user-images.githubusercontent.com/10358317/228506487-f1b0f962-9812-4c4a-a189-f88e55ae55f9.png) - Run init command: ``` terraform init ``` - Validate file: ``` terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/228507230-6a771b36-1bcb-44c5-969f-21640b041a6a.png) - Run plan command: ``` terraform plan ``` ![image](https://user-images.githubusercontent.com/10358317/229285029-7915460b-24fa-472d-bf32-515d389fdd03.png) - Run apply command: ``` terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229285059-a60d56f4-fe38-43d1-816d-e9537cedf884.png) ![image](https://user-images.githubusercontent.com/10358317/229285114-09358bae-94dc-49b1-bec4-a7951a6bd3d8.png) - On AWS > IAM > Users, users are created: ![image](https://user-images.githubusercontent.com/10358317/228508807-573a9e39-d906-4ec3-b32a-f1cd57cc5504.png) - Click on "username1_admin_dev" to see the policies: ![image](https://user-images.githubusercontent.com/10358317/228509359-07dcf4ca-9f2d-422d-9dea-c346f4341168.png) - To see user groups membership: ![image](https://user-images.githubusercontent.com/10358317/228509893-ed5fc830-94ec-4815-b1be-0811c2e0e566.png) - Users in the "admin_group": ![image](https://user-images.githubusercontent.com/10358317/228510263-8c6b2b28-20bc-4e86-82a3-8c96c1143b93.png) - EC2 Policy for "username3_dev_ec2": ![image](https://user-images.githubusercontent.com/10358317/228510983-1458f5f5-275b-4fe2-bc17-84d05bf4cd6c.png) - Destroy resources: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229285219-a0693f5b-f730-4f09-bcc4-5ddfab11b8b7.png) ### For Each - To test "for_each", main.tf under for_each. - Some differents from previous one: - not index, with name. e.g. aws_iam_user.user_example["username1_admin_dev"].name - s3 policy, attachment - for_each implementation ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # User - User Group Attachment (With Index Count) resource "aws_iam_user_group_membership" "user1_group_attach" { user = aws_iam_user.user_example["username1_admin_dev"].name groups = [ aws_iam_group.admin_group.name, aws_iam_group.dev_group.name, ] } resource "aws_iam_user_group_membership" "user2_group_attach" { user = aws_iam_user.user_example["username2_admin"].id groups = [ aws_iam_group.admin_group.name ] } resource "aws_iam_user_group_membership" "user3_group_attach" { user = aws_iam_user.user_example["username3_dev_s3"].name groups = [ aws_iam_group.dev_group.name ] } ##################################################### # User Group Definition resource "aws_iam_group" "admin_group" { name = "admin_group" } resource "aws_iam_group" "dev_group" { name = "dev_group" } ##################################################### # Policy Definition, Policy-Group Attachment data "aws_iam_policy_document" "admin_policy" { statement { effect = "Allow" actions = ["*"] resources = ["*"] } } resource "aws_iam_policy" "admin_policy" { name = "admin-policy" description = "Admin policy" policy = data.aws_iam_policy_document.admin_policy.json } data "aws_iam_policy_document" "s3_policy" { statement { effect = "Allow" actions = ["s3:*"] resources = [ "arn:aws:s3:::mybucket", "arn:aws:s3:::mybucket/*" ] } } resource "aws_iam_policy" "s3_policy" { name = "s3-policy" description = "S3 policy" policy = data.aws_iam_policy_document.s3_policy.json } ##################################################### # Policy Attachment to the Admin, Dev Group resource "aws_iam_group_policy_attachment" "admin_group_admin_policy_attach" { group = aws_iam_group.admin_group.name policy_arn = aws_iam_policy.admin_policy.arn } resource "aws_iam_group_policy_attachment" "dev_group_s3_policy_attach" { group = aws_iam_group.dev_group.name policy_arn = aws_iam_policy.s3_policy.arn } ##################################################### # With for_each resource "aws_iam_user" "user_example" { for_each = var.user_names name = each.value } # for each, use set instead of list variable "user_names" { description = "IAM usernames" type = set(string) default = ["username1_admin_dev", "username2_admin", "username3_dev_s3"] } ##################################################### # With for loop output "print_the_names" { value = [for name in var.user_names : name] } ``` **File:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/iamuser-metaargs-count-for-foreach-map/for_each/main.tf - This time, for dev groups, it creates S3 Permission Policy - Run init command: ``` terraform init ``` - Validate file: ``` terraform validate ``` - Run plan command: ``` terraform plan ``` - Run apply command: ``` terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229285279-fe30ddd9-3cec-46c1-88d5-8a504994a956.png) ![image](https://user-images.githubusercontent.com/10358317/228515164-73df4541-e651-40b9-b6d3-068797d7060b.png) On AWS, the different from previous example, s3 policy is created: ![image](https://user-images.githubusercontent.com/10358317/228515836-b26ef31f-1961-4062-ad0a-73dd40789fff.png) ![image](https://user-images.githubusercontent.com/10358317/228515699-7aabe035-2d17-4140-9c5a-121e72427810.png) - Destroy resources: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/228517742-bfe17919-ce01-4400-a2ac-dade7a041dbd.png) ### Map - You can also use map to define usernames, following yaml file shows how to use maps ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # With for_each resource "aws_iam_user" "example" { for_each = var.user_names name = each.value } # With Map variable "user_names" { description = "map" type = map(string) default = { user1 = "username1" user2 = "username2" user3 = "username3" } } # with for loop on map output "user_with_roles" { value = [for name, role in var.user_names : "${name} is the ${role}"] } ``` **File:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/iamuser-metaargs-count-for-foreach-map/map/main.tf ================================================ FILE: LAB05-Dynamic-Blocks-Security-Groups-EC2.md ================================================ ## LAB-05: Dynamic Blocks => Provision Security Groups, EC2, VPC This scenario shows: - how to create Dynamic Blocks for Security Group Definition **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/dynamic-blocks/main.tf ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With Dynamic Blocks, preventing repetition same code. ``` ... locals { ingress_rules = [{ port = 22 description = "Ingress rules for port SSH" }, { port = 80 description = "Ingress rules for port HTTP" }, { port = 443 description = "Ingress rules for port HTTPS" }] } resource "aws_security_group" "main" { ... dynamic "ingress" { for_each = local.ingress_rules content { description = ingress.value.description from_port = ingress.value.port to_port = ingress.value.port protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } ... } ... ``` ![image](https://user-images.githubusercontent.com/10358317/229287304-d23dca31-d871-4c88-a4fa-e1994ef671d0.png) - Copy the following main.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } locals { ingress_rules = [{ port = 22 description = "Ingress rules for port SSH" }, { port = 80 description = "Ingress rules for port HTTP" }, { port = 443 description = "Ingress rules for port HTTPS" }] } resource "aws_security_group" "main" { name = "resource_with_dynamic_block" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id dynamic "ingress" { for_each = local.ingress_rules content { description = ingress.value.description from_port = ingress.value.port to_port = ingress.value.port protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "AWS security group dynamic block" } } resource "aws_instance" "ubuntu2204" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.main.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Ubuntu 22.04" } } output "instance_ubuntu2204_public_ip" { value = "${aws_instance.ubuntu2204.public_ip}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/dynamic-blocks/main.tf - Run init, validate command: ``` terraform init terraform validate ``` - Run plan, apply command: ``` terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229287533-de896cdf-6189-45f1-a631-5f609be22721.png) ![image](https://user-images.githubusercontent.com/10358317/229287613-9815134c-4e36-4d70-adff-c7a272d82a31.png) - On AWS EC2 Instances: ![image](https://user-images.githubusercontent.com/10358317/229287673-ddbfa4a9-ca15-4375-a584-043cf259d344.png) - Security groups are created with Dynamic Blocks: ![image](https://user-images.githubusercontent.com/10358317/229287707-b5f215cd-0faf-4cbc-bc1d-868d96f2672d.png) - Destroy infrastructure: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229287838-c3ffcaa2-a931-4c9b-9efe-40760d76a6af.png) ![image](https://user-images.githubusercontent.com/10358317/229287904-a2ff9428-f15b-4b2e-a2fb-749d11f4e317.png) ================================================ FILE: LAB06-Data-Sources-EC2.md ================================================ ## LAB-06: Data Sources with Depends_on => Provision EC2 This scenario shows: - how to use Data Source to fetch/retrieve data (existed resource information) from AWS **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/data-sources/main.tf ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With data sources, existed resource information can be fetched/retrieved. - "filter" provide to select/filter the existed instances - "depends_on" provide to run the data block after resource created ``` ... data "aws_instance" "data_instance" { filter { name = "tag:Name" values = ["Basic Instance"] } depends_on = [ aws_instance.instance ] } output "instance_info" { value = data.aws_instance.data_instance } ... ``` ![image](https://user-images.githubusercontent.com/10358317/229291040-febb8404-4c00-48c3-b99d-ea0af0e68825.png) - Create main.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } # filter/select the existed instances # depends_on if aws_instance.instance is created data "aws_instance" "data_instance" { filter { name = "tag:Name" values = ["Basic Instance"] } depends_on = [ aws_instance.instance ] } output "instance_info" { value = data.aws_instance.data_instance } output "instance_public_ip" { value = data.aws_instance.data_instance.public_ip } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/data-sources/main.tf ![image](https://user-images.githubusercontent.com/10358317/229291093-e5febd7a-fa05-44bc-a224-00a18035b869.png) - Run init, validate command: ``` terraform init terraform validate ``` - Run plan, apply command: ``` terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229291488-831a796e-b77a-43ee-92a1-814630834907.png) ![image](https://user-images.githubusercontent.com/10358317/229291530-985497f5-87a4-41d8-8fec-3f6217d62e6d.png) - With output, details can be viewed: ![image](https://user-images.githubusercontent.com/10358317/229291636-963cfbf8-4735-4d62-bae4-c803f70a1775.png) ![image](https://user-images.githubusercontent.com/10358317/229291821-28dea44f-04cf-42ef-b436-cbdfc77bd294.png) - Destroy infrastructure: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229291943-7a61e1c5-743f-4508-928f-04d738c2bb5a.png) ![image](https://user-images.githubusercontent.com/10358317/229291973-1d789992-8a90-4709-8da1-2db5b3b79b46.png) ================================================ FILE: LAB07-Provisioners-Null-Resources.md ================================================ ## LAB-07: Provisioners (file, remote-exec), Null Resources (local-exec) => Provision Key-Pair, SSH Connection This scenario shows: - how to create file, run command using "remote-exec" provisioners on remote instance, - how to create file using provisioner "file" on remote instance, - how to create file, run command using "local-exec" on local pc, - how to create key-pairs for SSH connection. **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/provisioners-nullresources/main.tf ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - SSH key-pairs (public and private key) are used to connect remote server. Public key (xx.pub) is on the remote server, with private key, user can connect using SSH. - There are 2 ways of creating key-pairs (public and private key): - Creating them on cloud (AWS) - EC2 > Key-pairs > Create Key-Pair - Creating them on on-premise - "ssh-keygen -t rsa -b 2048" - Creating key-pairs on AWS: Go to EC2 > Key-pairs ![image](https://user-images.githubusercontent.com/10358317/228974087-b57126ab-6589-48fe-b609-1f18dc2f0c7e.png) - After creating key-pairs, public key is listed on AWS: ![image](https://user-images.githubusercontent.com/10358317/228974292-0d2d16ec-5590-4929-9b4e-8bc0215dcd60.png) - Private key (testkey.pem) is downloaded on your PC: ![image](https://user-images.githubusercontent.com/10358317/228974369-46c54f25-ea80-40dd-9c75-670ece815bf2.png) - Copy this testkey.pem into your directory on which main.tf exists. ![image](https://user-images.githubusercontent.com/10358317/228974784-de1b9be4-9083-45ec-a9ab-5a1e54aee2c5.png) - With provisioner "file", on the remote instance, new file can be created - With provisioner "remote-exec", on the remote instance, any command can be run - With provisioner "local-exec", on the local PC, any command can be run on any shell (bash, powershell) - With "null_resource", without creating any resource, any command can be run. - Provisioners in the resource only runs once while creating resource on remote instance - Provisioners in the "null_resource" run multiple times and it doesn't depend on the resource. ``` ... resource "aws_instance" "ubuntu2204" { ... provisioner "file" { source = "test-file.txt" destination = "/home/ubuntu/test-file.txt" } provisioner "file" { content = "I want to copy this string to the destination file => server.txt (using provisioner file content)" destination = "/home/ubuntu/server.txt" } provisioner "remote-exec" { inline = [ "touch hello.txt", "echo helloworld remote-exec provisioner >> hello.txt", ] } connection { type = "ssh" host = self.public_ip user = "ubuntu" private_key = file("testkey.pem") timeout = "4m" } } resource "null_resource" "example" { provisioner "local-exec" { command = "'This is test file for null resource local-exec' >> nullresource-generated.txt" interpreter = ["PowerShell", "-Command"] } } ... ``` ![image](https://user-images.githubusercontent.com/10358317/229303293-52a67375-9d3f-43f0-86a3-955ff6e6613a.png) - Create main.tf ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_security_group" "allow_ssh" { name = "allow_ssh_sg" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ubuntu2204" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Ubuntu 22.04" } provisioner "file" { source = "test-file.txt" destination = "/home/ubuntu/test-file.txt" } provisioner "file" { content = "I want to copy this string to the destination file => server.txt (using provisioner file content)" destination = "/home/ubuntu/server.txt" } provisioner "remote-exec" { inline = [ "touch hello.txt", "echo helloworld remote-exec provisioner >> hello.txt", ] } connection { type = "ssh" host = self.public_ip user = "ubuntu" private_key = file("testkey.pem") timeout = "4m" } } resource "null_resource" "example" { provisioner "local-exec" { command = "'This is test file for null resource local-exec' >> nullresource-generated.txt" interpreter = ["PowerShell", "-Command"] } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/provisioners-nullresources/main.tf - Run init, validate command: ``` terraform init terraform validate ``` - Run plan, apply command: ``` terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229302416-31b1baa5-7aac-45fa-8ca5-15120b0c6f2e.png) ![image](https://user-images.githubusercontent.com/10358317/229302498-c338ee6c-b16d-4e4a-a21b-3577c9403aed.png) - On AWS EC2 Instance: ![image](https://user-images.githubusercontent.com/10358317/229302953-73056a26-c85e-4fad-ba68-115f3fc3dcf1.png) - Make SSH connection: ``` ssh -i testkey.pem ubuntu@ ``` - Provisioners run and files were created on remote instance: ![image](https://user-images.githubusercontent.com/10358317/229302702-57df3eec-f6f9-4d5e-af1b-bfb47cc9a906.png) - On local PC, local provisioner also created file: ![image](https://user-images.githubusercontent.com/10358317/229302840-0e17e5ac-cc34-4252-9cf6-9df56a034a4a.png) - Destroy infrastructure: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229303068-c096e9f4-c6b6-45e4-b3a9-71ef0336a755.png) ![image](https://user-images.githubusercontent.com/10358317/229303188-3fd076c2-fac4-4ef2-8c7d-958c0f825640.png) - On AWS EC2 Instance: ![image](https://user-images.githubusercontent.com/10358317/229303226-adaead1f-6f6a-4138-9640-4dfce873166c.png) ================================================ FILE: LAB08-Modules-EC2.md ================================================ ## LAB-08: Modules => Provision EC2 This scenario shows: - how to create and use modules **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/labs/modules ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With modules, it helps: - organize configuration - encapsulation - re-usability - consistency - Main.tf refers modules (directories) - Each modules have variables.tf ### Module Calls (Main.tf) **Main.tf Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/modules/main.tf ``` ... module "webserver-1" { source = ".//module1" instance_type = "t2.nano" tag = "Webserver1 - Module1 - 20.04" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt } module "webserver-2" { source = ".//module2" instance_type = "t2.micro" tag = "Webserver2 - Module2 - 22.04" location = "eu-central-1" availability_zone = "eu-central-1a" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt } ``` ![image](https://user-images.githubusercontent.com/10358317/229362702-43148537-03fc-4876-9883-ccee83a63f56.png) ### Module1 **Module1 Variables.tf Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/modules/module1/variables.tf ``` variable "instance_type" { type = string description = "EC2 Instance Type" } variable "tag" { type = string description = "The tag for the EC2 instance" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "availability_zone" { type = string description = "The project availability zone" default = "eu-central-1c" } variable "ami" { type = string description = "The project region" } ``` **Module1 Main.tf Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/modules/module1/main.tf ### Module2 - Module2 variables.tf is same as module1 variables.tf **Module2 Variables.tf Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/modules/module2/variables.tf - Module2 main.tf code is different from module1 main.tf **Module2 Main.tf Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/modules/module2/main.tf ### Terraform Run - Run init, validate command: ``` terraform init terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/229363829-54d98270-7cb6-499d-8ca5-235187945e78.png) ![image](https://user-images.githubusercontent.com/10358317/229363852-4df92036-b8d1-4206-9da9-d6769237f46f.png) - Run plan, apply command: ``` terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/229364064-2ac5ad88-f93c-4cb3-a803-de43a10e5885.png) ![image](https://user-images.githubusercontent.com/10358317/229364027-755c7df6-4ecb-4fe0-9c10-ee30ecfa31c0.png) - On AWS, 2 EC2 Instances: ![image](https://user-images.githubusercontent.com/10358317/229364124-4ce9c58c-c2e2-42e6-8f00-13a8c63185a0.png) ![image](https://user-images.githubusercontent.com/10358317/229364304-23917adf-d488-4294-bc49-ce89c879817a.png) - On AWS, 2 VPCs: ![image](https://user-images.githubusercontent.com/10358317/229364188-792953e2-0d57-4560-8591-3c13203864ab.png) ![image](https://user-images.githubusercontent.com/10358317/229364233-41b910a9-d6cc-4e09-bbb7-11dd2054de56.png) - On Browser: ![image](https://user-images.githubusercontent.com/10358317/229364349-7572c383-e934-4e6b-b0be-f3ba4c098ac4.png) ![image](https://user-images.githubusercontent.com/10358317/229364366-ba334719-7789-42f7-b5e4-7cecfc7eb42a.png) - Destroy infrastructure: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229364565-c6f07625-008b-4e64-a62b-dc80ea6db4e3.png) ![image](https://user-images.githubusercontent.com/10358317/229364693-bcc9c21d-d091-49ef-b176-d71cabf813fb.png) - On AWS, EC2s are terminated: ![image](https://user-images.githubusercontent.com/10358317/229364904-22116720-7b4a-42d3-8b10-a34fc9dccafb.png) ================================================ FILE: LAB09-Workspaces-EC2.md ================================================ ## LAB-09: Workspaces => Provision EC2 with Different tfvars Files This scenario shows: - how to create, manage workspaces using EC2 and variables. **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/labs/workspace ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With workspaces, - a parallel, distinct copy of your infrastructure which you can test and verify in the development, test, and staging - like git, you are working on different workspaces (like branch) - single code but different workspaces - it creates multiple state files - Workspace commands: ``` terraform workspace help # help for workspace commands terraform workspace new [WorkspaceName] # create new workspace terraform workspace select [WorkspaceName] # change/select another workspace terraform workspace show # show current workspace terraform workspace list # list all workspaces terraform workspace delete [WorkspaceName] # delete existed workspace ``` ![image](https://user-images.githubusercontent.com/10358317/229855095-05b608f7-04aa-4603-9516-600d0c692d01.png) - We have basic main.tf file with EC2, variables: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { tag = "${terraform.workspace} EC2" } resource "aws_instance" "instance" { ami = var.ami instance_type = var.instance_type tags = { Name = local.tag } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/workspace/main.tf ![image](https://user-images.githubusercontent.com/10358317/230614196-cd007efe-388e-408b-a92e-bb064e07e0ab.png) - Variables.tf file: ``` variable "instance_type" { type = string description = "EC2 Instance Type" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "ami" { type = string description = "The project region" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/workspace/variables.tf ![image](https://user-images.githubusercontent.com/10358317/230614240-504aaebd-00a5-4d85-8ecd-73df9a9a2254.png) - For development workspace, terraform-dev.tfvars: ``` instance_type = "t2.nano" location = "eu-central-1" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/workspace/terraform-dev.tfvars ![image](https://user-images.githubusercontent.com/10358317/230614271-f5c5ccde-3d29-40ae-845f-5260060e2a5c.png) - For product workspace, terraform-prod.tfvars: ``` instance_type = "t2.micro" location = "eu-central-1" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/workspace/terraform-prod.tfvars ![image](https://user-images.githubusercontent.com/10358317/230614321-0f2ba768-cc0d-44f6-b68f-8a5c94ccb976.png) - To list current workspaces on the local: ``` terraform workspace list ``` ![image](https://user-images.githubusercontent.com/10358317/230609965-a282fba2-d318-4a24-b189-c928abdbd2cf.png) - 2 workspaces are created (DEV, PROD): ``` terraform workspace new dev terraform workspace new prod terraform workspace list ``` ![image](https://user-images.githubusercontent.com/10358317/230610099-3b8e3d91-e0a9-4a4e-bb25-8a8e15a489b5.png) - Switch to DEV workspace, show current workspace and initialize terraform: ``` terraform workspace select dev terraform workspace show terraform init ``` ![image](https://user-images.githubusercontent.com/10358317/230610877-e76ed796-6e38-4493-89d7-2dbdbd43457f.png) - Create infrastructure in DEV workspace: ``` terraform plan -var-file="terraform-dev.tfvars" # for test, dry-run terraform apply -var-file="terraform-dev.tfvars" ``` - On AWS, it creates dev EC2: ![image](https://user-images.githubusercontent.com/10358317/230612063-b66afd1e-749a-41fe-b899-c68933ad4aa8.png) - Switch to PROD workspace, create infrastructure in PROD workspace: ``` terraform workspace select prod terraform plan -var-file="terraform-prod.tfvars" # for test, dry-run terraform apply -var-file="terraform-prod.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/230612548-c8b79323-7f28-43c2-adc7-c7874ad3ed90.png) - On AWS, it creates prod EC2: ![image](https://user-images.githubusercontent.com/10358317/230612752-565fdef3-7f89-496c-b108-03d6d38008ef.png) - With workspace, 2 state files are created for each workspace: ![image](https://user-images.githubusercontent.com/10358317/230612889-dcd95404-3b0c-4b35-a3e4-a7844ca54a86.png) - Switch to DEV workspace, destroy infrastructure only in DEV workspace: ``` terraform workspace select dev terraform destroy -var-file="terraform-dev.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/230613305-4bfe6f81-e1bb-41c6-88cd-3ba9a37ee519.png) - On AWS, ONLY dev EC2 is terminated: ![image](https://user-images.githubusercontent.com/10358317/230613441-3e52a33d-6721-47bf-a72e-1b28e524aac9.png) - Switch to PROD workspace, destroy infrastructure only in PROD workspace: ``` terraform workspace select prod terraform destroy -var-file="terraform-prod.tfvars" ``` ![image](https://user-images.githubusercontent.com/10358317/230613867-c347eb16-6f5d-452c-85cf-4e54d3dfc89f.png) - On AWS, ONLY prod EC2 is terminated: ![image](https://user-images.githubusercontent.com/10358317/230613937-92d8ec9c-ffd2-48f7-9cbd-be6ca11500b5.png) ## References - https://jhooq.com/terraform-workspaces/ ================================================ FILE: LAB10-Templates-User-Policy.md ================================================ ## LAB-10: Templates => Provision IAM User, User Access Key, Policy This scenario shows: - how to use templates while creating policy **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/labs/template ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With templates, - avoid to write same code snippets multiple time, - provide to shorten the code - Create main.tf file. - This file creates IAM user, user access key, give some permission policy for EC2, S3, Lambda, DynamoDb. ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_iam_user" "newuser" { name = "New-User" # must only contain alphanumeric characters, hyphens, underscores, commas, periods, @ symbols, plus and equals signs } resource "aws_iam_access_key" "access_key" { user = aws_iam_user.newuser.name } resource "aws_iam_user_policy" "instanceManageUser_assume_role" { name = "EC2-S3-Lambda-DynamoDb-Policy" user = "${aws_iam_user.newuser.name}" policy = templatefile("${path.module}/policy.tftpl", { ec2_policies = [ "ec2:RunInstances", "ec2:StopInstances", "ec2:StartInstances", "ec2:TerminateInstances", "ec2:TerminateInstances", "ec2:Describe*", "ec2:CreateTags", "ec2:RequestSpotInstances" ], s3_policies = [ "s3:Get*", "s3:List*", "s3:Describe*", "s3-object-lambda:Get*", "s3-object-lambda:List*" ], lambda_policies = [ "lambda:Create*", "lambda:List*", "lambda:Delete*", "lambda:Get*" ], dynamodb_policies = [ "dynamodb:Describe*", "dynamodb:Update*", "dynamodb:Get*", "dynamodb:List*", "dynamodb:BatchGetItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:PartiQLSelect" ], }) } output "secret_key" { value = aws_iam_access_key.access_key.secret sensitive = true } output "access_key" { value = aws_iam_access_key.access_key.id } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/template/main.tf ![image](https://user-images.githubusercontent.com/10358317/230635156-50072817-9f9c-428a-906f-ebfe77e4f3ae.png) - Template file => Policy.tftpl: ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ${jsonencode(ec2_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(s3_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(lambda_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(dynamodb_policies)}, "Resource": "*" } ] } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/template/policy.tftpl ![image](https://user-images.githubusercontent.com/10358317/230635437-a2833841-b6be-4a2d-8c7b-a89c1d60c9ad.png) - Run init, validate command: ``` terraform init terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/230635918-74679513-1e33-4348-9b82-5b7ee631d7e5.png) - Run plan, apply command: ``` terraform plan # for dry-run terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/230636213-a285546a-7f38-4c04-bdb7-81254be09654.png) ![image](https://user-images.githubusercontent.com/10358317/230636709-d2f2115f-54f1-4e1c-9075-9e035f4ca15a.png) - On AWS IAM: ![image](https://user-images.githubusercontent.com/10358317/230636965-74714983-435a-4009-a015-1117032c5815.png) ![image](https://user-images.githubusercontent.com/10358317/230637038-235ad9fa-6f03-434b-a1df-15b38b218b19.png) - Run destroy command to delete user: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/230637447-5f1eb783-3a8f-45cc-afb6-522e27d74a9a.png) ================================================ FILE: LAB11-Backend-Remote-State.md ================================================ ## LAB-11: Backend - Remote States => Provision EC2 and Save State File on S3 This scenario shows: - how to use backend and save Terraform state file on S3 **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/backend-remote-state/ ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - With enabling remote state file using backend: - multiple user can work on the same state file - saving common state file on S3 is possible - Create S3 bucket on AWS ![image](https://user-images.githubusercontent.com/10358317/230646169-7b9a7210-bd64-4f50-acf7-12690d293490.png) ![image](https://user-images.githubusercontent.com/10358317/230646417-8c460ba7-e45b-4560-8859-d1ebfcac4812.png) - Create basic main.tf file. ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" backend "s3" { bucket = "terraform-state" key = "key/terraform.tfstate" region = "eu-central-1" } } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/labs/backend-remote-state/main.tf ![image](https://user-images.githubusercontent.com/10358317/230646618-f1200c13-eb83-4bcd-b353-a6c9a02272bd.png) - Run init, validate command: ``` terraform init terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/230646686-0b8ad133-d2e4-4ebf-8505-00eb769b1e5a.png) - Run plan, apply command: ``` terraform plan # for dry-run terraform apply ``` - On AWS S3, tfstate file is created: ![image](https://user-images.githubusercontent.com/10358317/230647235-3224ec77-2483-460c-81c6-40e9e434f869.png) - On local machine, state file is not saved now: ![image](https://user-images.githubusercontent.com/10358317/230647627-b5038f51-b34f-443c-b72a-d9c140d3d770.png) - On AWS, state file can be viewed, downloaded: ![image](https://user-images.githubusercontent.com/10358317/230647970-97d72d67-a588-40ff-a94f-3ea765dfe274.png) - With pull command, state file can be download on local machine: ``` terraform state pull > terraform.tfstate ``` ![image](https://user-images.githubusercontent.com/10358317/230648330-d89d0b53-617b-4449-a2cb-b92b163cbfdd.png) - Run destroy command: ``` terraform destroy ``` - After destroy command, all resources are deleted in state file on S3: ![image](https://user-images.githubusercontent.com/10358317/230649315-7cb1d236-145b-49ed-ad3f-60aaa01d7ca0.png) - To download updated state file: ``` terraform state pull > terraform.tfstate ``` ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 Ömer Berat Sezer 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, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. ================================================ FILE: README.md ================================================ # Fast-Terraform (with AWS) This repo covers Terraform with Hands-on LABs and Samples using AWS (comprehensive, but simple): - Resources, Data Sources, Variables, Meta Arguments, Provisioners, Dynamic Blocks, Modules, Workspaces, Templates, Remote State. - Provisioning AWS Components (EC2, EBS, EFS, IAM Roles, IAM Policies, Key-Pairs, VPC with Network Components, Lambda, ECR, ECS with Fargate, EKS with Managed Nodes, ASG, ELB, API Gateway, S3, CloudFront CodeCommit, CodePipeline, CodeBuild, CodeDeploy), use cases and details. Possible usage scenarios are aimed to update over time. Why was this repo created? - **Shows Terraform details in short with simple, clean demos and Hands-on LABs** - **Shows Terraform AWS Hands-on Samples, Use Cases** **Keywords:** Terraform, Infrastructure as Code, AWS, Cloud Provisioning # Quick Look (How-To): Terraform Hands-on LABs These LABs focus on Terraform features, help to learn Terraform: - [LAB-00: Installing Terraform, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) - [LAB-01: Terraform Docker => Pull Docker Image, Create Docker Container on Local Machine](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB01-Terraform-Docker-Without-Cloud.md) - [LAB-02: Resources => Provision Basic EC2 (Ubuntu 22.04)](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB02-Resources-Basic-EC2.md) - [LAB-03: Variables, Locals, Output => Provision EC2s](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB03-Variables-Locals-Output-EC2.md) - [LAB-04: Meta Arguments (Count, For_Each, Map) => Provision IAM Users, Groups, Policies, Attachment Policy-User](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB04-Meta-Arguments-IAM-User-Group-Policy.md) - [LAB-05: Dynamic Blocks => Provision Security Groups, EC2, VPC](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB05-Dynamic-Blocks-Security-Groups-EC2.md) - [LAB-06: Data Sources with Depends_on => Provision EC2](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB06-Data-Sources-EC2.md) - [LAB-07: Provisioners (file, remote-exec), Null Resources (local-exec) => Provision Key-Pair, SSH Connection](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB07-Provisioners-Null-Resources.md) - [LAB-08: Modules => Provision EC2](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB08-Modules-EC2.md) - [LAB-09: Workspaces => Provision EC2 with Different tfvars Files](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB09-Workspaces-EC2.md) - [LAB-10: Templates => Provision IAM User, User Access Key, Policy](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB10-Templates-User-Policy.md) - [LAB-11: Backend - Remote States => Provision EC2 and Save State File on S3](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB11-Backend-Remote-State.md) - [Terraform Cheatsheet](https://github.com/omerbsezer/Fast-Terraform/blob/main/Terraform-Cheatsheet.md) # Quick Look (How-To): AWS Terraform Hands-on Samples These samples focus on how to create and use AWS components (EC2, EBS, EFS, IAM Roles, IAM Policies, Key-Pairs, VPC with Network Components, Lambda, ECR, ECS with Fargate, EKS with Managed Nodes, ASG, ELB, API Gateway, S3, CloudFront, CodeCommit, CodePipeline, CodeBuild, CodeDeploy) with Terraform: - [SAMPLE-01: Provisioning EC2s (Windows 2019 Server, Ubuntu 20.04) on VPC (Subnet), Creating Key-Pair, Connecting Ubuntu using SSH, and Connecting Windows Using RDP](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE01-EC2-VPC-Ubuntu-Win-SSH-RDP.md) - [SAMPLE-02: Provisioning Lambda Function, API Gateway and Reaching HTML Page in Python Code From Browser](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE02-Lambda-API-Gateway-Python.md) - [SAMPLE-03: EBS (Elastic Block Storage: HDD, SDD) and EFS (Elastic File System: NFS) Configuration with EC2s (Ubuntu and Windows Instances)](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE03-EC2-EBS-EFS.md) - [SAMPLE-04: Provisioning ECR (Elastic Container Repository), Pushing Image to ECR, Provisioning ECS (Elastic Container Service), VPC (Virtual Private Cloud), ELB (Elastic Load Balancer), ECS Tasks and Service on Fargate Cluster](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE04-ECR-ECS-ELB-VPC-ECS-Service.md) - [SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE05-Lambda-Container-ApiGateway-FlaskApp.md) - [SAMPLE-06: Provisioning EKS (Elastic Kubernetes Service) with Managed Nodes using Blueprint and Modules](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE06-EKS-ManagedNodes-Blueprint.md) - [SAMPLE-07: CI/CD on AWS => Provisioning CodeCommit and CodePipeline, Triggering CodeBuild and CodeDeploy, Running on Lambda Container](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE07-CodeCommit-Pipeline-Build-Deploy-Lambda.md) - [SAMPLE-08: Provisioning S3 and CloudFront to serve Static Web Site](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE08-S3-CloudFront-Static-WebSite.md) - [SAMPLE-09: Running Gitlab Server using Docker on Local Machine and Making Connection to Provisioned Gitlab Runner on EC2 in Home Internet without Using VPN](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE09-GitlabServer-on-Premise-GitlabRunner-on-EC2.md) # Table of Contents - [Motivation](#motivation) - [What is Terraform?](#what_is_terraform) - [How Terraform Works?](#how_terrafom_works) - [Terraform File Components](#terrafom_file_components) - [Providers](#providers) - [Resources](#resources) - [Variables (tfvar)](#variables) - [Values (Locals, Outputs)](#values) - [Meta Arguments](#meta_arguments) - [Dynamic Blocks](#dynamic_blocks) - [Data Sources](#datasources) - [Provisioners (file, remote_exec, local_exec), Null Resource](#provisioners) - [Modules](#modules) - [Workspaces](#workspaces) - [Templates](#templates) - [Backends and Remote States](#backends_remote_states) - [Terraform Best Practices](#best_practice) - [AWS Terraform Hands-on Samples](#samples) - [SAMPLE-01: EC2s (Windows 2019 Server, Ubuntu 20.04), VPC, Key-Pairs for SSH, RDP connections](#ec2_vpc_key_pair_ssh_rdp) - [SAMPLE-02: Provisioning Lambda Function, API Gateway and Reaching HTML Page in Python Code From Browsers](#lambda_apigateway_python) - [SAMPLE-03: EBS (Elastic Block Storage: HDD, SDD) and EFS (Elastic File System: NFS) Configuration with EC2s (Ubuntu and Windows Instances)](#ebs_efs_ec2) - [SAMPLE-04: Provisioning ECR (Elastic Container Repository), Pushing Image to ECR, Provisioning ECS (Elastic Container Service), VPC (Virtual Private Cloud), ELB (Elastic Load Balancer), ECS Tasks and Service on Fargate Cluster](#ecr_ecs_elb_vpc_ecs_service_fargate) - [SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda](#ecr_lambda_apigateway_container) - [SAMPLE-06: Provisioning EKS (Elastic Kubernetes Service) with Managed Nodes using Blueprint and Modules](#eks_managednodes_blueprint) - [SAMPLE-07: CI/CD on AWS => Provisioning CodeCommit and CodePipeline, Triggering CodeBuild and CodeDeploy, Running on Lambda Container](#ci_cd) - [SAMPLE-08: Provisioning S3 and CloudFront to serve Static Web Site](#s3_cloudfront) - [SAMPLE-09: Running Gitlab Server using Docker on Local Machine and Making Connection to Provisioned Gitlab Runner on EC2 in Home Internet without Using VPN](#gitlabrunner) - [Details](#details) - [Terraform Cheatsheet](#cheatsheet) - [Other Useful Resources Related Terraform](#resource) - [References](#references) ## Motivation Why should we use / learn Terraform? - Terraform is cloud-agnostic and popular tool to create/provision Cloud Infrastructure resources/objects (e.g. Virtual Private Cloud, Virtual Machines, Lambda, etc.) - Manage any infrastructure - Similar to Native Infrastructure as Code (IaC): CloudFormation (AWS), Resource Manager (Azure), Google Cloud Deployment Manager (Google Cloud) - It is free, open source (https://github.com/hashicorp/terraform) and has a large community with enterprise support options. - Commands, tasks, codes turn into the IaC. - With IaC, tasks is savable, versionable, repetable and testable. - With IaC, desired configuration is defined as 'Declerative Way'. - **Agentless:** Terraform doesn’t require any software to be installed on the managed infrastructure - It has well-designed documentation: - https://developer.hashicorp.com/terraform/language - https://registry.terraform.io/providers/hashicorp/aws/latest/docs - Terraform uses a modular structure. - Terraform tracks your infrastructure with TF state file. ![image](https://user-images.githubusercontent.com/10358317/231144158-63009879-4687-492e-8f6a-5e233dab3f28.png)(ref: Redis) ## What is Terraform? - Terraform is cloud-independent provisioning tool to create Cloud infrastructure. - Creating infrastructure code with HCL (Hashicorp Language) that is similar to YAML, JSON, Python. - Terraform Basic Tutorial for AWS: - https://developer.hashicorp.com/terraform/tutorials/aws-get-started - Reference and Details: - https://developer.hashicorp.com/terraform/intro ![image](https://user-images.githubusercontent.com/10358317/231143883-a2348511-dd12-4e0f-806d-3144ac88aa4d.png)(ref: Terraform) ## How Terraform Works? - Terraform works with different providers (AWS, Google CLoud, Azure, Docker, K8s, etc.) - After creating Terraform Files (tf), terraform commands: - **init**: downloads the required executable apps dependent on providers. - **validate**: confirms the tf files. - **plan**: dry-run for the infrastructure, not actually running/provisioning the infrastructure - **apply**: runs/provisions the infrastructure - **destroy**: deletes the infrastructure - Main Commands: ``` terraform init terraform validate terraform plan # ask for confirmation (yes/no), after running command terraform apply # ask for confirmation (yes/no), after running command terraform destroy # ask for confirmation (yes/no), after running command ``` - Command Variants: ``` terraform plan --var-file="terraform-dev.tfvars" # specific variable files terraform apply -auto-approve # no ask for confirmation terraform apply --var-file="terraform-prod.tfvars" # specific variable files terraform destroy --var-file="terraform-prod.tfvars" # specific variable files ``` - Terraform Command Structure: ![image](https://user-images.githubusercontent.com/10358317/234230001-67475a51-d894-4234-8917-07d14355b205.png) - Terraform Workflow: ![image](https://user-images.githubusercontent.com/10358317/231147788-ba40b795-4050-49df-b1ad-48b273257410.png) - TF state file stores the latest status of the infrastructure after running "apply" command. - TF state file deletes the status of the infrastructure after running "destroy" command. - TF state files are stored: - on local PC - on remote cloud (AWS S3, Terraform Cloud) - Please have a look LABs and SAMPLEs to learn how Terraform works in real scenarios. ## Terraform File Components - Terraform file has different components to define infrastructure for different purposes. - Providers, - Resources, - Variables, - Values (locals, outputs), - Meta Argurments (for, for_each, map, depends_on, life_cycle), - Dynamic Blocks, - Data Sources, - Provisioners, - Workspaces, - Modules, - Templates. ### Providers - Terraform supposes for different providers (AWS, Google Cloud, Azure). - Terraform downloads required executable files from own cloud to run IaC (code) for the corresponding providers. - AWS (https://registry.terraform.io/providers/hashicorp/aws/latest/docs): ![image](https://user-images.githubusercontent.com/10358317/231405120-1d6907be-53f6-46e8-8e6f-a5dd73a0cfb5.png) - Google Cloud (GCP) (https://registry.terraform.io/providers/hashicorp/google/latest/docs): ![image](https://user-images.githubusercontent.com/10358317/231405236-73faf1e4-3ea8-45df-982e-d56f64eba2a5.png) - Azure (https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs): ![image](https://user-images.githubusercontent.com/10358317/231405457-7b90a689-96c6-434d-a319-162adc04f772.png) - Docker (https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container): ![image](https://user-images.githubusercontent.com/10358317/231437580-0dc3ce8f-bbbe-4b4e-97b7-6598e85cb59e.png) ### Resources - Resources are used to define for different cloud components and objects (e.g. EC2 instances, VPC, VPC Compoenents: Router Tables, Subnets, IGW, .., Lambda, API Gateway, S3 Buckets, etc.). - To learn the details, features of the cloud components, you should know how the cloud works, which components cloud have, how to configure the cloud components. - Syntax: - **resource {}** - e.g. resource "aws_instance" "instance" {} - e.g. resource "aws_vpc" "my_vpc" {} - e.g. resource "aws_subnet" "public" {} - e.g. resource "aws_security_group" "allow_ssh" {} ![image](https://user-images.githubusercontent.com/10358317/231420818-d0c3e679-787b-4d85-a784-6f4acdb7fa01.png) - Important part is to check the usage of the resources (shows which arguments are optional, or required) from Terraform Registry page by searching the "Object" terms like "instance", "vpc", "security groups" - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance ![image](https://user-images.githubusercontent.com/10358317/231423197-71c23a76-a55e-4cf0-bb51-d81e45b30ff1.png) - There are different parts: - **Argument References (inputs)** (some parts are optional, or required) - **Attributes References (outputs)** - **Example code snippet** to show how it uses - **Others** (e.g. timeouts, imports) - Go to LAB to learn resources: - [LAB-02: Resources => Provision Basic EC2 (Ubuntu 22.04)](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB02-Resources-Basic-EC2.md) ### Variables (tfvar) - Variables help to avoid hard coding on the infrastructure code. - The Terraform language uses the following types for its values: - **string:** a sequence of Unicode characters representing some text, like "hello". - **number:** a numeric value. The number type can represent both whole numbers like 15 and fractional values like 6.283185. - **bool:** a boolean value, either true or false. bool values can be used in conditional logic. - **list (or tuple):** a sequence of values, like ["one", "two"]. Elements in a list or tuple are identified by consecutive whole numbers, starting with zero. - **map (or object):** a group of values identified by named labels, like {name = "Mabel", age = 52}. - Strings, numbers, and bools are sometimes called primitive types. Lists/tuples and maps/objects are sometimes called complex types, structural types, or collection types. - Normally, if you define variables, after running "terraform apply" command, on the terminal, stdout requests from the user to enter variables. - But, if the "tfvar" file is defined, variables in the "tfvar" file are entered automatically in the corresponding variable fields. ![image](https://user-images.githubusercontent.com/10358317/231144889-3edd38a4-1ff9-4c82-8155-50f0089757fa.png) - Tfvar files for development ("DEV") environment: ![image](https://user-images.githubusercontent.com/10358317/231437129-29191b28-368d-4fe5-8b99-abae9986e424.png) - Tfvar files for production ("PROD") environment: ![image](https://user-images.githubusercontent.com/10358317/231145166-f032ae1e-9bbb-436a-9c0e-3c0be8fb627c.png) - Go to LAB to learn variables and tfvar file, and provisioning EC2 for different environments: - [LAB-03: Variables, Locals, Output => Provision EC2s](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB03-Variables-Locals-Output-EC2.md) ### Values (Locals, Outputs) - "Locals" are also the variables that are mostly used as place-holder variables. ![image](https://user-images.githubusercontent.com/10358317/231438120-8c6f0cb4-ea56-457f-8532-7bc2ee4bc39b.png) - "Outputs" are used to put the cloud objects' information (e.g. public IP, DNS, detailed info) out as stdout. ![image](https://user-images.githubusercontent.com/10358317/231438718-880861bb-900d-49e2-b642-eafe0437a457.png) - "Outputs" after running "terraform apply" command on the terminal stdout: ![image](https://user-images.githubusercontent.com/10358317/231439218-d8f19bdb-ed5c-48b0-ad49-1f48fbb649f8.png) - Go to LAB to learn more about variables, locals, outputs and provisioning EC2: - [LAB-03: Variables, Locals, Output => Provision EC2s](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB03-Variables-Locals-Output-EC2.md) ### Meta Arguments - Different meta arguments are used for different purposes: - **count:** the number of objects (e.g. variables, resources, etc.) - https://developer.hashicorp.com/terraform/language/meta-arguments/count - **for:** iteration over the list of objects (e.g. variables, resources, etc.) - **for_each:** iteration over the set of objects (e.g. variables, resources, etc.) - https://developer.hashicorp.com/terraform/language/meta-arguments/for_each - **depends_on:** shows the priority order of creation of the resource. If "A" should be created before "B", user should write "depends_on= A" as an argument under "B". - https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on - **life_cycle:** uses to make life cycle relationship between objects (e.g. variables, resources, etc.) - https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle - **providers:** specifies which provider configuration to use for a resource, overriding Terraform's default behavior. - https://developer.hashicorp.com/terraform/language/meta-arguments/resource-provider - Count: ![image](https://user-images.githubusercontent.com/10358317/231446996-da7cfb44-3c6a-43a2-ab89-8dd93a8318a5.png) - For_each, For: ![image](https://user-images.githubusercontent.com/10358317/231447409-b09e98e5-4ce8-4d7d-83e0-60edc6c9b88e.png) - Go to LAB to learn: - [LAB-04: Meta Arguments (Count, For_Each, Map) => Provision IAM Users, Groups, Policies, Attachment Policy-User](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB04-Meta-Arguments-IAM-User-Group-Policy.md) ### Dynamic Blocks - "Dynamic blocks" creates small code template that reduces the code repetition. - In the example below, it isn't needed to create parameters (description, from_port, to_port, protocol, cidr_blocks) for each ingress ports: ![image](https://user-images.githubusercontent.com/10358317/231145371-c9322d06-326d-44f7-95e2-e9ec6f806bfe.png) - Go to LAB to learn: - [LAB-05: Dynamic Blocks => Provision Security Groups, EC2, VPC](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB05-Dynamic-Blocks-Security-Groups-EC2.md) ### Data Sources - "Data Sources" helps to retrieve/fetch/get data/information from previously created/existed cloud objects/resources. - In the example below: - "filter" keyword is used to select/filter the existed objects (reources, instances, etc.) - "depends_on" keyword provides to run the data block after resource created. ![image](https://user-images.githubusercontent.com/10358317/231145418-c333d2df-706f-4325-8eb2-5d677fa30ce5.png) - Go to LAB to learn: - [LAB-06: Data Sources with Depends_on => Provision EC2](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB06-Data-Sources-EC2.md) ### Provisioners - "Provisioners" provides to run any commands on the remote instance/virtual machine, or on the local machine. - "Provisioners" in the resource block runs only once while creating the resource on remote instance. If the resource is created/provisioned before, "provisioner" block in the resource block doesn't run again. - With "null_resource": - Without creating any resource, - Without depending any resource, - Any commands can be run. - Provisioners in the "null_resource" run multiple times and it doesn't depend on the resource. - With provisioner "file", on the remote instance, new file can be created - With provisioner "remote-exec", on the remote instance, any command can be run - With provisioner "local-exec", on the local PC, any command can be run on any shell (bash, powershell) ![image](https://user-images.githubusercontent.com/10358317/231145525-4f97c4a1-dc6f-4323-a8ca-7041c27d00d9.png) - Go to LAB to learn about different provisioners: - [LAB-07: Provisioners (file, remote-exec), Null Resources (local-exec) => Provision Key-Pair, SSH Connection](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB07-Provisioners-Null-Resources.md) ### Modules - "Modules" helps organize configuration, encapsulation, re-usability and consistency. - "Modules" is the structure/container for multiple resources that are used together. - Each modules usually have variables.tf that is configured from the parent tf file. - Details: https://developer.hashicorp.com/terraform/language/modules - AWS modules for different components (VPC, IAM, SG, EKS, S3, Lambda, RDS, etc.) - https://registry.terraform.io/browse/modules?provider=aws ![image](https://user-images.githubusercontent.com/10358317/231145600-59034277-0781-4285-9fa8-987a1c7f6e27.png) - Go to LAB to learn: - [LAB-08: Modules => Provision EC2](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB08-Modules-EC2.md) ### Workspaces - With "Workspaces": - a parallel, distinct copy of your infrastructure which you can test and verify in the development, test, and staging, - like git, you are working on different workspaces (like branch), - single code but different workspaces, - it creates multiple state files on different workspace directories. - Workspace commands: ``` terraform workspace help # help for workspace commands terraform workspace new [WorkspaceName] # create new workspace terraform workspace select [WorkspaceName] # change/select another workspace terraform workspace show # show current workspace terraform workspace list # list all workspaces terraform workspace delete [WorkspaceName] # delete existed workspace ``` - "dev" and "prod" workspaces are created: ![image](https://user-images.githubusercontent.com/10358317/231145703-c35b657c-7e39-4d53-980f-4561fda9489d.png) - Go to LAB to learn: - [LAB-09: Workspaces => Provision EC2 with Different tfvars Files](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB09-Workspaces-EC2.md) ### Templates - With "Templates" (.tftpl files): - avoid to write same code snippets multiple times, - provide to shorten the code - In the example below, templates fields are filled with list in the resource code block: ![image](https://user-images.githubusercontent.com/10358317/231479114-e7cf013a-4b83-4288-b581-c180bbee7eee.png) - Go to LAB to learn "Templates": - [LAB-10: Templates => Provision IAM User, User Access Key, Policy](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB10-Templates-User-Policy.md) ### Backend and Remote States - With enabling remote state file using backend: - multiple user can work on the same state file - saving common state file on S3 is possible - With backend part ("s3"), state file is stored on S3: ![image](https://user-images.githubusercontent.com/10358317/231481348-2b0a35aa-f2a6-4335-87fb-fe6df31f9e73.png) - On AWS S3 Bucket, terraform.tfstate file is saved: ![image](https://user-images.githubusercontent.com/10358317/231145912-908af9fe-56ef-479e-8ee4-9c5f7f92329e.png) - Go to LAB to learn: - [LAB-11: Backend - Remote States => Provision EC2 and Save State File on S3](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB11-Backend-Remote-State.md) ## Terraform Best Practices - Don't change/edit anything on state file manually. Manipulate state file only through TF commands (e.g. terraform apply, terraform state). - Use remote state file to share the file with other users. Keep state file on the Cloud (S3, Terraform Cloud, etc.) - To prevent concurrent changes, state locking is important. Hence concurrent change from multiple user can be avoided. - S3 supports state locking and consistency via DynamoDB. - Backing up state file is also important to save the status. S3 enables versioning. Versioning state file can provide you backing up the state file. - If you use multiple environment (dev, test, staging, production), use 1 state file per environment. Terraform workspace provide multiple state files for different environments. - Use Git repositories (Github, Gitlab) to host TF codes to share other users. - Behave your Infrastructure code as like your application code. Create CI pipeline/process for your TF Code (review tf code, run automated tests). This will shift your infrastructure code high quality. - Execute Terraform only in an automated build, CD pipeline/process. This helps to run code automatically and run from one/single place. - For naming conventions: https://www.terraform-best-practices.com/naming ## AWS Terraform Hands-on Samples ### SAMPLE-01: EC2s (Windows 2019 Server, Ubuntu 20.04), VPC, Key-Pairs for SSH, RDP connections - This sample shows: - how to create Key-pairs (public and private keys) on AWS, - how to create EC2s (Ubuntu 20.04, Windows 2019 Server), - how to create Virtual Private Cloud (VPC), VPC Components (Public Subnet, Internet Gateway, Route Table) and link to each others, - how to create Security Groups (for SSH and Remote Desktop). - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ec2-vpc-ubuntu-win-ssh-rdp - **Go to the Hands-On Sample:** - [SAMPLE-01: Provisioning EC2s (Windows 2019 Server, Ubuntu 20.04) on VPC (Subnet), Creating Key-Pair, Connecting Ubuntu using SSH, and Connecting Windows Using RDP](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE01-EC2-VPC-Ubuntu-Win-SSH-RDP.md) ![image](https://user-images.githubusercontent.com/10358317/233837033-01c37232-75f8-4815-a5d6-e3f73139963e.png) ### SAMPLE-02: Provisioning Lambda Function, API Gateway and Reaching HTML Page in Python Code From Browsers - This sample shows: - how to create Lambda function with Python code, - how to create lambda role, policy, policy-role attachment, lambda api gateway permission, zipping code, - how to create api-gateway resource and method definition, lambda - api gateway connection, deploying api gateway, api-gateway deployment URL as output - details on AWS Lambda, API-Gateway, IAM. - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-role-policy-apigateway-python - **Go to the Hands-On Sample:** - [SAMPLE-02: Provisioning Lambda Function, API Gateway and Reaching HTML Page in Python Code From Browser](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE02-Lambda-API-Gateway-Python.md) ![image](https://user-images.githubusercontent.com/10358317/233837058-7909658d-b06d-49e3-8f81-d56ccf295609.png) ### SAMPLE-03: EBS (Elastic Block Storage: HDD, SDD) and EFS (Elastic File System: NFS) Configuration with EC2s (Ubuntu and Windows Instances) - This sample shows: - how to create EBS, mount on Ubuntu and Windows Instances, - how to create EFS, mount on Ubuntu Instance, - how to provision VPC, subnet, IGW, route table, security group. - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ec2-ebs-efs - **Go to the Hands-On Sample:** - [SAMPLE-03: EBS (Elastic Block Storage: HDD, SDD) and EFS (Elastic File System: NFS) Configuration with EC2s (Ubuntu and Windows Instances)](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE03-EC2-EBS-EFS.md) ![image](https://user-images.githubusercontent.com/10358317/230903321-5bca3385-9564-44f1-bde8-fe1c873c870a.png) ### SAMPLE-04: Provisioning ECR (Elastic Container Repository), Pushing Image to ECR, Provisioning ECS (Elastic Container Service), VPC (Virtual Private Cloud), ELB (Elastic Load Balancer), ECS Tasks and Service on Fargate Cluster - This sample shows: - how to create Flask-app Docker image, - how to provision ECR and push to image to this ECR, - how to provision VPC, Internet Gateway, Route Table, 3 Public Subnets, - how to provision ALB (Application Load Balancer), Listener, Target Group, - how to provision ECS Fargate Cluster, Task and Service (running container as Service). - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ecr-ecs-elb-vpc-ecsservice-container - **Go to the Hands-On Sample:** - [SAMPLE-04: Provisioning ECR (Elastic Container Repository), Pushing Image to ECR, Provisioning ECS (Elastic Container Service), VPC (Virtual Private Cloud), ELB (Elastic Load Balancer), ECS Tasks and Service on Fargate Cluster](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE04-ECR-ECS-ELB-VPC-ECS-Service.md) ![ecr-ecs](https://user-images.githubusercontent.com/10358317/232244927-7d819c66-328a-4dd5-b3e1-18b2c7fd92aa.png) ### SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda - This sample shows: - how to create Flask-app-serverless image to run on Lambda, - how to create ECR and to push image to ECR, - how to create Lambda function, Lambda role, policy, policy-role attachment, Lambda API Gateway permission, - how to create API Gateway resource and method definition, Lambda - API Gateway connection, deploying API Gateway. - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-container-apigateway-flaskapp - **Go to the Hands-On Sample:** - [SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE05-Lambda-Container-ApiGateway-FlaskApp.md) ![image](https://user-images.githubusercontent.com/10358317/233119966-9800d18c-8d0c-40de-9c1d-d14726743e5a.png) ![image](https://user-images.githubusercontent.com/10358317/233121075-ab2ac298-71b2-467d-8068-834f16b0f3c7.png) ### SAMPLE-06: Provisioning EKS (Elastic Kubernetes Service) with Managed Nodes using Blueprint and Modules - This sample shows: - how to create EKS cluster with managed nodes using BluePrints and Modules. - EKS Blueprint is used to provision EKS cluster with managed nodes easily. - EKS Blueprint is used from: - https://github.com/aws-ia/terraform-aws-eks-blueprints - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/eks-managed-node-blueprint - **Go to the Hands-On Sample:** - [SAMPLE-06: Provisioning EKS (Elastic Kubernetes Service) with Managed Nodes using Blueprint and Modules](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE06-EKS-ManagedNodes-Blueprint.md) ![image](https://user-images.githubusercontent.com/10358317/233836920-20d2b13b-7cb2-42c0-bb3f-70780f5f107d.png) ![image](https://user-images.githubusercontent.com/10358317/233836945-dfb85038-f5be-40a8-abe7-15b1334b854d.png) ### SAMPLE-07: CI/CD on AWS => Provisioning CodeCommit and CodePipeline, Triggering CodeBuild and CodeDeploy, Running on Lambda Container - This sample shows: - how to create code repository using CodeCommit, - how to create pipeline with CodePipeline, create S3 bucket to store Artifacts for codepipeline stages' connection (source, build, deploy), - how to create builder with CodeBuild ('buildspec_build.yaml'), build the source code, create a Docker image, - how to create ECR (Elastic Container Repository) and push the build image into the ECR, - how to create Lambda Function (by CodeBuild automatically) and run/deploy container on Lambda ('buildspec_deploy.yaml'). - Source code is pulled from: - https://github.com/aws-samples/codepipeline-for-lambda-using-terraform - Some of the fields are updated. - It works with 'hashicorp/aws ~> 4.15.1', 'terraform >= 0.15' - **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container - **Go to the Hands-On Sample:** - [SAMPLE-07: CI/CD on AWS => Provisioning CodeCommit and CodePipeline, Triggering CodeBuild and CodeDeploy, Running on Lambda Container](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE07-CodeCommit-Pipeline-Build-Deploy-Lambda.md) ![image](https://user-images.githubusercontent.com/10358317/233652299-66b39788-66ee-4a5e-b8e0-ece418fe98e3.png) ### SAMPLE-08: Provisioning S3 and CloudFront to serve Static Web Site - This sample shows: - how to create S3 Bucket, - how to to copy the website to S3 Bucket, - how to configure S3 bucket policy, - how to create CloudFront distribution to refer S3 Static Web Site, - how to configure CloudFront (default_cache_behavior, ordered_cache_behavior, ttl, price_class, restrictions, viewer_certificate). - **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/s3-cloudfront-static-website/ - **Go to the Hands-On Sample:** - [SAMPLE-08: Provisioning S3 and CloudFront to serve Static Web Site](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE08-S3-CloudFront-Static-WebSite.md) ![image](https://user-images.githubusercontent.com/10358317/234044290-e14650ed-93b4-4c49-8891-edeb959ffacb.png) ### SAMPLE-09: Running Gitlab Server using Docker on Local Machine and Making Connection to Provisioned Gitlab Runner on EC2 in Home Internet without Using VPN - This sample shows: - how to run Gitlab Server using Docker on WSL2 on-premise, - how to redirect external traffic to docker container port (Gitlab server), - how to configure on-premise PC network configuration, - how to run EC2 and install docker, gitlab-runner on EC2, - how to register Gitlab runner on EC2 to Gitlab Server on-premise (in Home), - how to run job on EC2 and returns artifacts to Gitlab Server on-premise (in Home). - **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/gitlabserver-on-premise-runner-on-EC2/ - **Go to the Hands-On Sample:** - [SAMPLE-09: Running Gitlab Server using Docker on Local Machine and Making Connection to Provisioned Gitlab Runner on EC2 in Home Internet without Using VPN](https://github.com/omerbsezer/Fast-Terraform/blob/main/SAMPLE09-GitlabServer-on-Premise-GitlabRunner-on-EC2.md) ## Details - To validate the Terraform files: - "terraform validate" - For dry-run: - "terraform plan" - For formatting: - "terraform fmt" - For debugging: - **Bash:** export TF_LOG="DEBUG" - **PowerShell:** $env:TF_LOG="DEBUG" - For debug logging: - **Bash:** export TF_LOG_PATH="tmp/terraform.log" - **PowerShell:** $env:TF_LOG_PATH="C:\tmp\terraform.log" ## Terraform Cheatsheet - [Terraform Cheatsheet](https://github.com/omerbsezer/Fast-Terraform/blob/main/Terraform-Cheatsheet.md) ## Other Useful Resources Related Terraform - **AWS Samples (Advanced):** - https://github.com/aws-samples - **AWS Samples with Terraform (Advanced):** - https://github.com/orgs/aws-samples/repositories?q=Terraform&type=all&language=&sort= - **AWS Integration and Automation (Advanced):** - https://github.com/aws-ia - **Reference Guide: Terraform Registry Documents** - https://registry.terraform.io/providers/hashicorp/aws/latest/docs ## References - Redis: https://developer.redis.com/create/aws/terraform/ - https://developer.hashicorp.com/terraform/intro - https://github.com/aws-samples - https://github.com/orgs/aws-samples/repositories?q=Terraform&type=all&language=&sort= - https://github.com/aws-ia/terraform-aws-eks-blueprints - https://github.com/aws-ia ================================================ FILE: SAMPLE01-EC2-VPC-Ubuntu-Win-SSH-RDP.md ================================================ ## SAMPLE-01: Provisioning EC2s (Windows 2019 Server, Ubuntu 20.04) on VPC (Subnet), Creating Key-Pair, Connecting Ubuntu using SSH, and Connecting Windows Using RDP This sample shows: - how to create Key-pairs (public and private keys) on AWS. - how to create EC2s (Ubuntu 20.04, Windows 2019 Server). - how to create Virtual Private Cloud (VPC), VPC Components (Public Subnet, Internet Gateway, Route Table) and link to each others. - how to create Security Groups (for SSH and Remote Desktop). **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ec2-vpc-ubuntu-win-ssh-rdp ![1 VPC-IG-EC2](https://github.com/user-attachments/assets/29c5a207-bc35-43f1-8c4e-75d77acf77c1) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - SSH key-pairs (public and private key) are used to connect remote server. Public key (xx.pub) is on the remote server, with private key, user can connect using SSH. - There are 2 ways of creating key-pairs (public and private key): - Creating them on cloud (AWS) - EC2 > Key-pairs > Create Key-Pair - Creating them on on-premise - "ssh-keygen -t rsa -b 2048" - Creating key-pairs on AWS: Go to EC2 > Key-pairs ![image](https://user-images.githubusercontent.com/10358317/228974087-b57126ab-6589-48fe-b609-1f18dc2f0c7e.png) - After creating key-pairs, public key is listed on AWS: ![image](https://user-images.githubusercontent.com/10358317/228974292-0d2d16ec-5590-4929-9b4e-8bc0215dcd60.png) - Private key (testkey.pem) is downloaded on your PC: ![image](https://user-images.githubusercontent.com/10358317/228974369-46c54f25-ea80-40dd-9c75-670ece815bf2.png) - Copy this testkey.pem into your directory on which main.tf exists. ![image](https://user-images.githubusercontent.com/10358317/228974784-de1b9be4-9083-45ec-a9ab-5a1e54aee2c5.png) - Create main.tf under count directory and copy the code: ``` # main.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_security_group" "allow_ssh" { name = "allow_ssh_sg" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP Apache Server ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for RDP ingress { from_port = 3389 to_port = 3389 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for ping ingress { from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["10.0.0.0/16"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ubuntu2004" { ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true user_data = <<-EOF #! /bin/bash sudo apt-get update sudo apt-get install -y apache2 sudo systemctl start apache2 sudo systemctl enable apache2 echo "

Deployed via Terraform from $(hostname -f)

" | sudo tee /var/www/html/index.html EOF tags = { Name = "Ubuntu 20.04" } } resource "aws_instance" "win2019" { ami = "ami-02c2da541ae36c6fc" # Windows 2019 Server eu-central-1 Frankfurt instance_type = "t2.micro" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Win 2019 Server" } } output "instance_ubuntu2004_public_ip" { value = "${aws_instance.ubuntu2004.public_ip}" } output "instance_win2019_public_ip" { value = "${aws_instance.win2019.public_ip}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ec2-vpc-ubuntu-win-ssh-rdp/main.tf ![image](https://user-images.githubusercontent.com/10358317/228973324-4bc1c6ad-1099-4f56-8e6f-c002f719d9d4.png) - Run init command: ``` terraform init ``` ![image](https://user-images.githubusercontent.com/10358317/228975022-6ed0663d-dca8-427b-8a9b-7cce25522117.png) - Validate file: ``` terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/228975088-077a0562-ee85-4ad2-8cc9-2f4ccd1c4c94.png) - Run plan command: ``` terraform plan ``` - Run apply command to create resources. Then, Terraform asks to confirm, write "yes": ``` terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/228975493-a28ece3e-b00d-436e-aab1-de7b679378f3.png) ![image](https://user-images.githubusercontent.com/10358317/229282068-a4f293cf-cde7-4b6b-9e9e-48bd40822aae.png) - On AWS EC2 > Instances, Ubuntu 20.04: ![image](https://user-images.githubusercontent.com/10358317/228975962-72c0fbe9-e1dc-482c-8edc-573013424953.png) - Security groups (SSG), for SSH (port 22), RDP (port 3389), HTTP (80), ICMP (for ping): ![image](https://user-images.githubusercontent.com/10358317/228976204-c4993141-c8ad-4110-8f05-c1fb1e2c13c2.png) ![image](https://user-images.githubusercontent.com/10358317/228976348-f07e284e-be34-4898-9b81-92a756910a56.png) - On AWS EC2 > Instances, Window 2019 Server: ![image](https://user-images.githubusercontent.com/10358317/228976557-7951b759-73fd-46a6-b325-08389e52b86f.png) - Windows has same SSG like Ubuntu. - Storage, Elastic Block Storage default: ![image](https://user-images.githubusercontent.com/10358317/228976823-4e80c3e5-8ec6-4e4b-b5d2-aa064982954d.png) - On AWS VPC (Virtual Private Cloud) Service: ![image](https://user-images.githubusercontent.com/10358317/228977082-f0634c8e-8701-45e2-aa22-f0b7dc1e3400.png) - While installing Ubuntu20.04, userdata is used to install Apache Server on it. With SSG Port 80, using public IP, we can see the index.html, like hosting server: ![image](https://user-images.githubusercontent.com/10358317/228978767-aee0b725-eff2-40b2-8a30-93f40c15a052.png) - SSH to Ubuntu 20.04 (ssh -i testkey.pem ubuntu@): ![image](https://user-images.githubusercontent.com/10358317/228977324-3393ae14-85d8-48ba-9133-bc7a6f1ef73b.png) - Run: ``` sudo apt install net-tools ifconfig ``` - Private IP can be seen: ![image](https://user-images.githubusercontent.com/10358317/228977710-610437ee-c1c2-4a7e-82e7-ac07d38aed62.png) - Make remote connection to Windows (RDP): ![image](https://user-images.githubusercontent.com/10358317/228977887-14d82ad9-41ff-43eb-bdaf-2c3e007df506.png) - Download RDP App: ![image](https://user-images.githubusercontent.com/10358317/228978036-79956ff6-755e-4205-aa17-4ac45f8e6642.png) - To get password, upload testkey.pem file: ![image](https://user-images.githubusercontent.com/10358317/228978295-f822464a-a448-434f-a22e-5f623620d69e.png) ![image](https://user-images.githubusercontent.com/10358317/228978459-8fabb789-eec4-4582-b0e2-4b44d0e2de43.png) - Now, we reach Windows using RDP: ![image](https://user-images.githubusercontent.com/10358317/229282800-7ee0cd35-fd46-4ece-9b04-45fa5c785fef.png) - Pinging to Ubuntu20.04 from Windows: ![image](https://user-images.githubusercontent.com/10358317/228979379-1ed193a4-c5e4-4526-8a8e-0ecb2cf1a534.png) - Opening firewall rules to ping from Ubuntu to Windows: - Windows Defender -> Advance Settings -> Inbound Rules -> File and Printer Sharing (Echo Request - ICMPv4 - In) -> Right Click (Enable) ![image](https://user-images.githubusercontent.com/10358317/228980207-1f699349-9d09-4304-ad1f-d66187a0b9e1.png) - Pinging to Windows 2019 Server from Ubuntu20.04: ![image](https://user-images.githubusercontent.com/10358317/228980821-98c1b545-c1c1-41ca-a53c-f5049641c9df.png) - Viewing Ubuntu CPU, RAM: ![image](https://user-images.githubusercontent.com/10358317/228981485-341444fb-f806-49fe-9a6c-c7edc0d25f17.png) ![image](https://user-images.githubusercontent.com/10358317/228981584-9823ad8d-169f-4ef2-b4bc-61852b95393b.png) - Destroy infrastructure: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/229281383-6c8e01ea-af3a-4477-9be6-a10096318333.png) ![image](https://user-images.githubusercontent.com/10358317/229281239-b1713771-2960-4746-a1bd-f7543913bd66.png) - Be sure that instances are terminated. Because if they works, we pay the fee of them: ![image](https://user-images.githubusercontent.com/10358317/228982134-2a648783-0f11-47e9-865d-1dc92dffdd0f.png) - We can also monitor the CPU, Disk, Network Usage on AWS EC2: ![image](https://user-images.githubusercontent.com/10358317/228983384-7b4a0e36-0605-4f33-901f-2a0ebd79c4f6.png) ================================================ FILE: SAMPLE02-Lambda-API-Gateway-Python.md ================================================ ## SAMPLE-02: Provisioning Lambda Function, API Gateway and Reaching HTML Page in Python Code From Browser This sample shows: - how to create Lambda function with Python code, - how to create Lambda Role, Policy, Policy-Role attachment, Lambda API-gateway permission, uploading code, - how to create API-gateway resource and method definition, Lambda-API-gateway connection, deploying API-gateway, - details on AWS Lambda, API-Gateway, IAM ![3 Lambda-TF](https://github.com/user-attachments/assets/554cfb9b-44a3-4eb0-b278-a3ae5c25ffbe) There are 3 main parts: - lambda.tf: It includes lambda function, lambda role, policy, policy-role attachment, lambda api gateway permission, zipping code - api-gateway.tf: It includes api-gateway resource and method definition, lambda - api gateway connection, deploying api gateway, api-gateway deployment URL as output - code/main.py: It includes basic lambda handler with basic HTML code, and REST API response. **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-role-policy-apigateway-python ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Create lambda.tf: - **Terraform Configuration:** Specifies the AWS provider version and Terraform version. - **IAM Role and Policy:** Creates an IAM role for the Lambda function and attaches a policy that allows the Lambda function to write logs to CloudWatch. - **Zipping Lambda Code:** Prepares the Python code by zipping it into a format that Lambda can execute. - **Lambda Function:** Creates a Lambda function using the specified IAM role, code, and runtime. - **API Gateway Integration:** Grants API Gateway permission to invoke the Lambda function. ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Create IAM Role for lambda resource "aws_iam_role" "lambda_role" { name = "aws_lambda_role" assume_role_policy = <

Hello Website running on Lambda! Deployed via Terraform

""" response ={ "statusCode": 200, "body": content, "headers": {"Content-Type": "text/html",}, } return response ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-role-policy-apigateway-python/code/main.py ![image](https://user-images.githubusercontent.com/10358317/230722767-2f89b91b-eab3-4348-b405-7ca773bee803.png) - Run init, validate command: ``` terraform init terraform validate ``` ![image](https://user-images.githubusercontent.com/10358317/230722951-8516b9fd-2b03-4391-b91f-a338c207b669.png) - Run plan, apply command: ``` terraform plan # for dry-run terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/230723022-f5443b26-7b50-4834-ad2a-a8f1f277ecbb.png) ![image](https://user-images.githubusercontent.com/10358317/230723143-a4cd31a1-a48a-4ed9-92aa-1ddc4ecafb2a.png) - On AWS Lambda: ![image](https://user-images.githubusercontent.com/10358317/230723176-fe26cd48-b8cc-463f-9bba-cb8573514e64.png) ![image](https://user-images.githubusercontent.com/10358317/230723214-0e056c50-567e-4cca-bc67-73328301b24c.png) - Create a test by clicking "Test" for lambda function: ![image](https://user-images.githubusercontent.com/10358317/230723292-5496d0f0-977d-46e4-96ee-239808e56284.png) - Status code 200, OK is returned successfully: ![image](https://user-images.githubusercontent.com/10358317/230723342-bb606c2f-b9d6-47d0-93b6-e935e00481e1.png) - Execution Role is created, seen on Lambda ![image](https://user-images.githubusercontent.com/10358317/230723943-f9cafb0e-a03a-4c40-a55e-792c5416c8ed.png) - Role on IAM: ![image](https://user-images.githubusercontent.com/10358317/230724180-0829397f-fc53-4199-8eb5-8c206237d771.png) - Policy on IAM: ![image](https://user-images.githubusercontent.com/10358317/230724019-dfd4936c-85a0-42a3-b3f3-fcae018ca8e6.png) - On AWS Lambda: With Lambda permission, API Gateway can invoke Lambda ![image](https://user-images.githubusercontent.com/10358317/230724344-09c46001-af57-4637-ab46-3df38cfd5b27.png) - Lambda is triggered by API-Gateway: ![image](https://user-images.githubusercontent.com/10358317/230723832-95739c09-ec36-4eb6-b1a4-5acbd675ea27.png) - On AWS API-Gateway: ![image](https://user-images.githubusercontent.com/10358317/230723491-4f377afc-8fc9-4ab9-ba99-41970e205207.png) ![image](https://user-images.githubusercontent.com/10358317/230723560-b5f814e0-c1e2-4961-99e1-4f846de3797e.png) - By clicking "Test" to test api-gateway ![image](https://user-images.githubusercontent.com/10358317/230723599-0a7f38e9-2f64-4dde-8da2-e19c3ceccd7c.png) ![image](https://user-images.githubusercontent.com/10358317/230723628-76d57abe-4943-4876-bbaa-b2e1021476e5.png) - HTML page that runs on Lambda Function using API-Gateway can be reached. With API-Gateway, lambda function gets DNS and traffic from internet comes to the Lambda function. ![image](https://user-images.githubusercontent.com/10358317/230723675-38fa5f25-c7ef-46e9-8eba-5e5691b47c88.png) - Run destroy command: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/230724579-86f8228a-efcb-4ae8-bcc3-009fa54a5d14.png) - On AWS Lambda, function is deleted: ![image](https://user-images.githubusercontent.com/10358317/230724707-4e5070cf-a801-4b3e-9aa7-560b279a48a5.png) ## References - https://www.tecracer.com/blog/2021/08/iam-what-happens-when-you-assume-a-role.html - https://medium.com/paul-zhao-projects/serverless-applications-with-aws-lambda-and-api-gateway-using-terraform-37d3de435d21 - https://github.com/rahulwagh/Terraform-Topics/tree/master/aws-lambda ================================================ FILE: SAMPLE03-EC2-EBS-EFS.md ================================================ ## SAMPLE-03: EBS (Elastic Block Store: HDD, SDD) and EFS (Elastic File System: NFS) Configuration with EC2s (Ubuntu and Windows Instances) This sample shows: - how to provision EBS, mount on Ubuntu and Windows Instances. - how to provision EFS, mount on Ubuntu Instance. - EFS is not supported on Windows Instance: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/AmazonEFS.html - how to provision VPC, subnet, IGW, route table, security group. ![aws-ebs-efs](https://github.com/user-attachments/assets/dc4efaf8-9d16-4a83-aeeb-9c0a082a4903) There are 3 main parts: - **main.tf**: It includes 2 EC2 (Ubuntu, Windows), VPC, subnet, IGW, route table, security group implementation. - **efs.tf**: It includes EFS configuration for Ubuntu EC2. - **ebs.tf**: It includes EBS configuration for both Ubuntu and Windows EC2s. ![image](https://user-images.githubusercontent.com/10358317/230903321-5bca3385-9564-44f1-bde8-fe1c873c870a.png) **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ec2-ebs-efs # Table of Contents - [Key-Pair](#keypair) - [VPC, EC2, SG Implementation](#vpc) - [EBS Implementation](#ebs) - [EFS Implementation](#efs) - [Terraform Run](#run) - [EBS Final Setup and Test on Ubuntu](#ebsfinalubuntu) - [EFS Final Setup and Test on Ubuntu](#efsfinalubuntu) - [EBS Final Setup and Test on Windows](#ebsfinalwindows) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps ### Key-Pair SSH key-pairs (public and private key) are used to connect remote server. Public key (xx.pub) is on the remote server, with private key, user can connect using SSH. - There are 2 ways of creating key-pairs (public and private key): - Creating them on cloud (AWS) - EC2 > Key-pairs > Create Key-Pair - Creating them on on-premise - "ssh-keygen -t rsa -b 2048" - Creating key-pairs on AWS: Go to EC2 > Key-pairs ![image](https://user-images.githubusercontent.com/10358317/228974087-b57126ab-6589-48fe-b609-1f18dc2f0c7e.png) - After creating key-pairs, public key is listed on AWS: ![image](https://user-images.githubusercontent.com/10358317/228974292-0d2d16ec-5590-4929-9b4e-8bc0215dcd60.png) - Private key (testkey.pem) is downloaded on your PC: ![image](https://user-images.githubusercontent.com/10358317/228974369-46c54f25-ea80-40dd-9c75-670ece815bf2.png) - Copy this testkey.pem into your directory on which main.tf exists. ![image](https://user-images.githubusercontent.com/10358317/228974784-de1b9be4-9083-45ec-a9ab-5a1e54aee2c5.png) ### VPC, EC2, SG Implementation - Create main.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.81.0" } } required_version = ">= 1.10.2" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_security_group" "sg_config" { name = "allow_ssh_sg" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP Apache Server ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for RDP ingress { from_port = 3389 to_port = 3389 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for ping ingress { from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["10.0.0.0/16"] } # EFS mount target, important to connect with NFS file system, it must be added. ingress { from_port = 2049 to_port = 2049 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ubuntu2004" { ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.sg_config.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Ubuntu 20.04" } } resource "aws_instance" "win2019" { ami = "ami-02c2da541ae36c6fc" # Windows 2019 Server eu-central-1 Frankfurt instance_type = "t2.micro" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Win 2019 Server" } } output "instance_ubuntu2004_public_ip" { value = "${aws_instance.ubuntu2004.public_ip}" } output "instance_win2019_public_ip" { value = "${aws_instance.win2019.public_ip}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ec2-ebs-efs/main.tf ![image](https://user-images.githubusercontent.com/10358317/228973324-4bc1c6ad-1099-4f56-8e6f-c002f719d9d4.png) ### EBS Implementation - Create ebs.tf: ``` # Creating EBS for Ubuntu # details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ebs_volume resource "aws_ebs_volume" "ebs_ubuntu" { availability_zone = "eu-central-1c" size = 20 # The size of the drive in GiBs. type= "gp2" # default gp2. others: standard, gp2, gp3, io1, io2, sc1 or st1 } # EBS-Ubuntu attachment resource "aws_volume_attachment" "ubuntu2004_ebs_ubuntu" { device_name = "/dev/sdh" volume_id = "${aws_ebs_volume.ebs_ubuntu.id}" instance_id = "${aws_instance.ubuntu2004.id}" } # Creating EBS for Windows # details: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ebs_volume resource "aws_ebs_volume" "ebs_windows" { availability_zone = "eu-central-1c" size = 15 # The size of the drive in GiBs. type= "gp2" # default gp2. others: standard, gp2, gp3, io1, io2, sc1 or st1 } # EBS-Windows attachment resource "aws_volume_attachment" "win2019_ebs_windows" { device_name = "/dev/sdg" volume_id = "${aws_ebs_volume.ebs_windows.id}" instance_id = "${aws_instance.win2019.id}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ec2-ebs-efs/ebs.tf ![image](https://user-images.githubusercontent.com/10358317/230891729-cda1f5a7-0d7e-4763-ad26-709b23596ad5.png) ### EFS Implementation - Create efs.tf: ``` # Creating Amazon EFS File system # Amazon EFS supports two lifecycle policies. Transition into IA and Transition out of IA resource "aws_efs_file_system" "efs" { lifecycle_policy { transition_to_ia = "AFTER_30_DAYS" } tags = { Name = "efs-example" } } # Creating the EFS access point for AWS EFS File system resource "aws_efs_access_point" "access_point" { file_system_id = aws_efs_file_system.efs.id } # Creating the AWS EFS System policy to transition files into and out of the file system. # The EFS System Policy allows clients to mount, read and perform, write operations on File system # The communication of client and EFS is set using aws:secureTransport Option resource "aws_efs_file_system_policy" "policy" { file_system_id = aws_efs_file_system.efs.id policy = < - Run init, validate, plan, apply commands: ``` terraform init terraform validate terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/230892963-1699dbe3-5b7e-43a2-a2a0-bfd71e463c41.png) ![image](https://user-images.githubusercontent.com/10358317/230893093-c7e6f1dd-f5f0-4b57-9c67-b56d6a872fbb.png) ### EBS Final Setup and Test on Ubuntu - On AWS EC2: ![image](https://user-images.githubusercontent.com/10358317/230893309-620f3483-4056-4685-ada9-e49f88e8cc64.png) - On AWS EC2 Volumes, EC2 have default root volumes (8GB, 30GB). - New addition volumes (15GB, 20GB) are added as a second volume of EC2s. ![image](https://user-images.githubusercontent.com/10358317/230893508-3ca0ae47-0738-46af-b8b6-2e59ce022034.png) - Connect to the Ubuntu via SSH: ``` ssh -i .\testkey.pem ubuntu@3.69.53.254 lsblk ``` ![image](https://user-images.githubusercontent.com/10358317/230894465-f33c048f-78aa-47f0-8c5b-878cd8a4368c.png) - Formatting disk, mounting EBS: ``` sudo file -s /dev/xvdh # “/dev/xvdf: data“, it means your volume is empty. #format, Format the volume to the ext4 or xfs sudo mkfs -t ext4 /dev/xvdh # prefer ext4 # sudo mkfs -t xfs /dev/xvdh # mounting EBS sudo mkdir newvolume sudo mount /dev/xvdh newvolume cd newvolume df -h . ``` ![image](https://user-images.githubusercontent.com/10358317/230894735-e13ebfb7-fb47-4d98-ae11-7e2358294c92.png) ### EFS Final Setup and Test on Ubuntu - Go to EFS Service, new EFS filesystem: ![image](https://user-images.githubusercontent.com/10358317/230894988-0a461c41-e8e2-4dfb-a44e-34b5a88f973d.png) - Click "Attach" to attach EFS to EC2: ![image](https://user-images.githubusercontent.com/10358317/230895234-e9497503-0345-4cb3-9332-9b62202d7e37.png) - Copy the command: ![image](https://user-images.githubusercontent.com/10358317/230895361-1d45f9fa-cc76-4261-9cc0-460e7d704182.png) - Mount the EFS: ``` e.g. sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-0adf50bc47d99ff95.efs.eu-central-1.amazonaws.com:/ efs ``` - After mounting EFS, EFS mounted directory can be seen in the list. - Although creating 2 x 200MB files, root part's size does not change. It shows that EFS is mounted successfully, files are created on EFS part. ![image](https://user-images.githubusercontent.com/10358317/230896271-ded8a2f6-cfb8-4d18-a29d-fba4277126cc.png) ### EBS Final Setup and Test on Windows - Connect Windows with RDP, get password using pem key: ![image](https://user-images.githubusercontent.com/10358317/230897189-9d7fcb1a-0129-4b6a-a06e-64715e645476.png) ![image](https://user-images.githubusercontent.com/10358317/230897253-09e44ee7-ca2c-4720-ab25-f912213a15f1.png) - For mounting disk on Windows: - Folow: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ebs-using-volumes.html, open disk management utility - Make "Online" the disk: ![image](https://user-images.githubusercontent.com/10358317/230897820-eefb2070-d7a4-4d0b-b466-06441ef7e44b.png) - Make "Initialize" the disk, with MBR: ![image](https://user-images.githubusercontent.com/10358317/230898135-6f8d7012-10b3-4e4b-bf29-27619166782d.png) - New Simple Volume: ![image](https://user-images.githubusercontent.com/10358317/230898370-40f2b976-3a7c-4b7b-ae3e-0897b0c3a277.png) - Final configuration: ![image](https://user-images.githubusercontent.com/10358317/230898488-a35862a9-d171-40b5-8f1d-4f97a986346a.png) - New EBS disk is mounted successfully: ![image](https://user-images.githubusercontent.com/10358317/230898597-ca1d7ffe-80ba-45cb-9465-1463436392ae.png) - Destroy infrastructure: ``` terraform destroy ``` - It is easy to manage 17 resources: ![image](https://user-images.githubusercontent.com/10358317/230901325-0a1cd3c1-2080-4749-a084-f370c17b95de.png) ## References: - https://adamtheautomator.com/terraform-efs/ - https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ebs-using-volumes.html ================================================ FILE: SAMPLE04-ECR-ECS-ELB-VPC-ECS-Service.md ================================================ ## SAMPLE-04: Provisioning ECR (Elastic Container Repository), Pushing Image to ECR, Provisioning ECS (Elastic Container Service), VPC (Virtual Private Cloud), ELB (Elastic Load Balancer), ECS Tasks and Service on Fargate Cluster This sample shows: - how to create Flask-app Docker image, - how to provision ECR and push to image to this ECR, - how to provision VPC, Internet Gateway, Route Table, 3 Public Subnets, - how to provision ALB (Application Load Balancer), Listener, Target Group, - how to provision ECS Fargate Cluster, Task and Service (running container as Service) There are 5 main parts: - **0_ecr.tf**: includes private ECR code - **1_vpc.tf**: includes VPC, IGW, Route Table, Subnets code - **2_ecs.tf**: includes ECS Cluster, Task Definition, Role and Policy code - **3_elb.tf**: includes to ALB, Listener, Target Group, Security Group code - **4_ecs_service.tf**: includes ECS Fargate Service code with linking to loadbalancer, subnets, task definition. ![ecr-ecs](https://user-images.githubusercontent.com/10358317/232244927-7d819c66-328a-4dd5-b3e1-18b2c7fd92aa.png) **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ecr-ecs-elb-vpc-ecsservice-container **ECS Pricing:** - For the ECS Cluster: - **Free** - For the ECS on 1 EC2 Instance (e.g. m5.large => 2 vCPU, 8 GB RAM): - **Per Hour:** $0.096 - **Per Day:** $2.304 - **For 30 days:** $69.12 - Please have look for instance pricing: https://aws.amazon.com/ec2/pricing/on-demand/ - For the Fargate: - AWS Fargate pricing is calculated based on the **vCPU and memory** resources used from the time you start to download your container image until the ECS Task (Container) terminate. - e.g. 2 x (1vCPU, 4GB RAM) on Linux: - **Per Hour:** 2 x ($0,0665) = $0.133 - **Per Day:** $3,18 - **Per 30 Days:** $95.67 - e.g. 2 x (1vCPU, 4GB RAM) on Win: - **Per Hour:** 2 x ($0,199) = $0.398 - **Per Day:** $9.55 - **Per 30 Days:** $286.56 - Please have look for fargate pricing: https://aws.amazon.com/fargate/pricing/ # Table of Contents - [Flask App Docker Image Creation](#app) - [Creating ECR (Elastic Container Repository), Pushing Image into ECR](#ecr) - [Creating VPC (Virtual Private Cloud)](#vpc) - [Creating ECS (Elastic Container Service)](#ecs) - [Creating ELB (Elastic Load Balancer)](#elb) - [Creating ECS Service](#ecsservice) - [Demo: Terraform Run](#run) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps ### Flask App Docker Image Creation - We have Flask-App to run on AWS ECS. To build image, please have a look: - https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app ### Creating ECR (Elastic Container Repository), Pushing Image into ECR - Create 0_ecr.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Creating Elastic Container Repository for application resource "aws_ecr_repository" "flask_app" { name = "flask-app" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ecr-ecs-elb-vpc-ecsservice-container/ecr/0_ecr.tf ![image](https://user-images.githubusercontent.com/10358317/232241506-8abd69f6-8802-434e-976f-0420a226fa3f.png) ``` cd /ecr terraform init terraform plan terraform apply ``` - On AWS ECR: ![image](https://user-images.githubusercontent.com/10358317/232226427-e8a9883e-298d-4268-b553-960f31994933.png) - To see the pushing docker commands, click "View Push Commands" ``` aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin .dkr.ecr.eu-central-1.amazonaws.com docker tag flask-app:latest .ecr.eu-central-1.amazonaws.com/flask-app:latest docker push .dkr.ecr.eu-central-1.amazonaws.com/flask-app:latest ``` - Image was pushed: ![image](https://user-images.githubusercontent.com/10358317/232226726-bffcca9f-4654-44bc-86dc-02a8d2d17c38.png) - On AWS ECR: ![image](https://user-images.githubusercontent.com/10358317/232226806-aa709e35-25de-4110-ad5c-92949cc1ebe8.png) ### Creating VPC (Virtual Private Cloud) - Create 1_vpc.tf: ``` # Internet Access -> IGW -> Route Table -> Subnets terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public_subnet_a" { availability_zone = "eu-central-1a" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" tags = { Name = "Public Subnet A" } } resource "aws_subnet" "public_subnet_b" { availability_zone = "eu-central-1b" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.1.0/24" tags = { Name = "Public Subnet B" } } resource "aws_subnet" "public_subnet_c" { availability_zone = "eu-central-1c" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.2.0/24" tags = { Name = "Public Subnet C" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "route_table" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "route_table_association1" { subnet_id = aws_subnet.public_subnet_a.id route_table_id = aws_route_table.route_table.id } resource "aws_route_table_association" "route_table_association2" { subnet_id = aws_subnet.public_subnet_b.id route_table_id = aws_route_table.route_table.id } resource "aws_route_table_association" "route_table_association3" { subnet_id = aws_subnet.public_subnet_c.id route_table_id = aws_route_table.route_table.id } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ecr-ecs-elb-vpc-ecsservice-container/1_vpc.tf ![image](https://user-images.githubusercontent.com/10358317/232241321-50a2fe1f-4fb2-4c82-b785-02bf1b326c63.png) ### Creating ECS (Elastic Container Service) - Create 2_ecs.tf: ``` # Getting data existed ECR data "aws_ecr_repository" "flask_app" { name = "flask-app" } # Creating ECS Cluster resource "aws_ecs_cluster" "my_cluster" { name = "my-cluster" # Naming the cluster } # Creating ECS Task resource "aws_ecs_task_definition" "flask_app_task" { family = "flask-app-task" container_definitions = < - Create 3_elb.tf: ``` # Internet Access -> IGW -> LB Security Groups -> Application Load Balancer (Listener 80) -> Target Groups -> ECS Service -> ECS SG -> Tasks on each subnets # Creating Load Balancer (LB) resource "aws_alb" "application_load_balancer" { name = "test-lb-tf" # Naming our load balancer load_balancer_type = "application" subnets = [ "${aws_subnet.public_subnet_a.id}", "${aws_subnet.public_subnet_b.id}", "${aws_subnet.public_subnet_c.id}" ] # Referencing the security group security_groups = ["${aws_security_group.load_balancer_security_group.id}"] } # Creating a security group for LB resource "aws_security_group" "load_balancer_security_group" { vpc_id = aws_vpc.my_vpc.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] # Allowing traffic in from all sources } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # Creating LB Target Group resource "aws_lb_target_group" "target_group" { name = "target-group" port = 80 protocol = "HTTP" target_type = "ip" vpc_id = "${aws_vpc.my_vpc.id}" } # Creating LB Listener resource "aws_lb_listener" "listener" { load_balancer_arn = "${aws_alb.application_load_balancer.arn}" # Referencing our load balancer port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = "${aws_lb_target_group.target_group.arn}" # Referencing our target group } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ecr-ecs-elb-vpc-ecsservice-container/3_elb.tf ![image](https://user-images.githubusercontent.com/10358317/232241821-b859d54f-69ae-40ba-8034-b35fc5309fe1.png) ### Creating ECS Service - Create 4_ecs_service.tf: ``` # Creating ECS Service resource "aws_ecs_service" "my_first_service" { name = "my-first-service" # Naming our first service cluster = "${aws_ecs_cluster.my_cluster.id}" # Referencing our created Cluster task_definition = "${aws_ecs_task_definition.flask_app_task.arn}" # Referencing the task our service will spin up launch_type = "FARGATE" desired_count = 3 # Setting the number of containers to 3 load_balancer { target_group_arn = "${aws_lb_target_group.target_group.arn}" # Referencing our target group container_name = "${aws_ecs_task_definition.flask_app_task.family}" container_port = 5000 # Specifying the container port } network_configuration { subnets = ["${aws_subnet.public_subnet_a.id}", "${aws_subnet.public_subnet_b.id}", "${aws_subnet.public_subnet_c.id}"] assign_public_ip = true # Providing our containers with public IPs security_groups = ["${aws_security_group.service_security_group.id}"] # Setting the security group } } # Creating SG for ECS Container Service, referencing the load balancer security group resource "aws_security_group" "service_security_group" { vpc_id = aws_vpc.my_vpc.id ingress { from_port = 0 to_port = 0 protocol = "-1" # Only allowing traffic in from the load balancer security group security_groups = ["${aws_security_group.load_balancer_security_group.id}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } #Log the load balancer app URL output "app_url" { value = aws_alb.application_load_balancer.dns_name } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/ecr-ecs-elb-vpc-ecsservice-container/4_ecs_service.tf ![image](https://user-images.githubusercontent.com/10358317/232241887-e71759c4-6a09-4cb3-9f18-f0dc36e08eda.png) ### Demo: Terraform Run - Run: ``` terraform init terraform validate terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/232227578-21f28564-d960-45ea-af0d-3c4b3bddbc91.png) ![image](https://user-images.githubusercontent.com/10358317/232227661-5ea1a161-03f0-4a76-b5c1-c9d3c1610333.png) - On AWS ECS Cluster: ![image](https://user-images.githubusercontent.com/10358317/232227690-14ca0e5d-43bf-443e-b228-338081f17eb8.png) - On AWS ECS Service (service runs on the cluster): ![image](https://user-images.githubusercontent.com/10358317/232227740-b0216d39-d141-4174-a506-61fc46245f02.png) - ECS Service Tasks (task includes running 3 containers): ![image](https://user-images.githubusercontent.com/10358317/232227792-8f3bcd80-edcb-4893-bb5e-25432d87f38f.png) - Running Container Details (CPU, Memory Usage, Network, Environment Variable, Volume Configuration, Logs): ![image](https://user-images.githubusercontent.com/10358317/232227895-07a3475a-c62a-4966-a35b-807fb4e136a6.png) - On AWS EC2 > LoadBalancer: ![image](https://user-images.githubusercontent.com/10358317/232228088-74870060-9cbb-46e6-9f28-6162ff310e2e.png) - Target Groups: ![image](https://user-images.githubusercontent.com/10358317/232228412-d53c99d9-ec75-44ab-a5dc-c5b9e77cd638.png) - On AWS VPC: ![image](https://user-images.githubusercontent.com/10358317/232229182-87c75449-0bd5-4048-833c-d0e9c3dc41c0.png) - When go to the output of the ELB DNS: http://test-lb-tf-634023821.eu-central-1.elb.amazonaws.com/ ![image](https://user-images.githubusercontent.com/10358317/232228546-8c9beb79-2133-438e-a05c-a36beef7b3f5.png) - LB redirects 3 different containers. Each container has own SQLlite. So, each refresh to LB page, it shows 3 different page: - Container 0: ![image](https://user-images.githubusercontent.com/10358317/232228846-a442f536-e127-448e-a699-350be155c5bb.png) - Container 1: ![image](https://user-images.githubusercontent.com/10358317/232228931-3370f751-fe9a-43e9-b75b-ec98b0a3693b.png) - Container 2: ![image](https://user-images.githubusercontent.com/10358317/232228879-fc9ca3fb-6d0c-4be6-a484-b6ac76375dd2.png) - Destroy the infra: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/232229470-6fd4dc95-bb63-4bda-860a-1fecc14a4fa5.png) - Delete the ECR Repo: ``` cd ecr terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/232229541-913de839-4df6-4d5b-881b-56be12801334.png) ================================================ FILE: SAMPLE05-Lambda-Container-ApiGateway-FlaskApp.md ================================================ ## SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda This sample shows: - how to create Flask-app-serverless image to run on Lambda, - how to create ECR and to push image to ECR, - how to create Lambda function, Lambda role, policy, policy-role attachment, Lambda API Gateway permission - how to create API Gateway resource and method definition, Lambda - API Gateway connection, deploying API Gateway There are 3 main parts: - **0_ecr.tf**: includes private ECR code. - **1_lambda.tf**: includes lambda function, lambda role, policy, policy-role attachment, lambda api gateway permission code. - **2_api_gateway.tf**: includes api-gateway resource and method definition, lambda - api gateway connection, deploying api gateway code. ![image](https://user-images.githubusercontent.com/10358317/233119705-ba6544e0-dbfc-49f5-9a65-c20b82f7bae1.png) **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-container-apigateway-flaskapp # Table of Contents - [Flask App Docker Image Creation](#app) - [Creating ECR (Elastic Container Repository), Pushing Image into ECR](#ecr) - [Creating Lambda](#lambda) - [Creating API Gateway](#apigateway) - [Demo: Terraform Run](#run) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps ### Flask App Docker Image Creation - We have Flask-App to run on AWS ECS. To build image, please have a look: - https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-container-apigateway-flaskapp/flask-app-serverless ### Creating ECR (Elastic Container Repository), Pushing Image into ECR - Create 0_ecr.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Creating Elastic Container Repository for application resource "aws_ecr_repository" "flask_app_serverless" { name = "flask-app-serverless" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-container-apigateway-flaskapp/ecr/0_ecr.tf ``` cd /ecr terraform init terraform plan terraform apply ``` - On AWS ECR: ![image](https://user-images.githubusercontent.com/10358317/232346390-161b1cf6-c22c-476c-ba75-4fb2501f39ca.png) - To see the pushing docker commands, click "View Push Commands" ``` aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin .dkr.ecr.eu-central-1.amazonaws.com docker tag flask-app-serverless:latest .ecr.eu-central-1.amazonaws.com/flask-app-serverless:latest docker push .dkr.ecr.eu-central-1.amazonaws.com/flask-app-serverless:latest ``` - Image on AWS ECR: ![image](https://user-images.githubusercontent.com/10358317/232346479-48ecccd2-7b47-437f-a19a-dc25cbe79de0.png) ### Creating Lambda - Difference between Lambda function code and Lambda container: Defining the image on the Lambda Function. ``` # Getting data existed ECR data "aws_ecr_repository" "flask_app_serverless" { name = "flask-app-serverless" } # Lambda Function, in terraform ${path.module} is the current directory. resource "aws_lambda_function" "lambda_function" { function_name = "Lambda-Function" role = aws_iam_role.lambda_role.arn # tag is required, "source image ... is not valid" error will pop up image_uri = "${data.aws_ecr_repository.flask_app_serverless.repository_url}:latest" # lambda image on ECR package_type = "Image" depends_on = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role] } ``` ![image](https://user-images.githubusercontent.com/10358317/232440083-86117379-a961-49bd-b1a6-55a4dea4685f.png) - Create 1_lambda.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Create IAM Role for lambda resource "aws_iam_role" "lambda_role" { name = "aws_lambda_role" assume_role_policy = < - Create 2_api_gateway.tf: ``` # Create API Gateway with Rest API type resource "aws_api_gateway_rest_api" "example" { name = "Serverless" description = "Serverless Application using Terraform" } resource "aws_api_gateway_resource" "proxy" { rest_api_id = aws_api_gateway_rest_api.example.id parent_id = aws_api_gateway_rest_api.example.root_resource_id path_part = "{proxy+}" # with proxy, this resource will match any request path } resource "aws_api_gateway_method" "proxy" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_resource.proxy.id http_method = "ANY" # with ANY, it allows any request method to be used, all incoming requests will match this resource authorization = "NONE" } # API Gateway - Lambda Connection resource "aws_api_gateway_integration" "lambda" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_method.proxy.resource_id http_method = aws_api_gateway_method.proxy.http_method integration_http_method = "POST" type = "AWS_PROXY" # With AWS_PROXY, it causes API gateway to call into the API of another AWS service uri = aws_lambda_function.lambda_function.invoke_arn } # The proxy resource cannot match an empty path at the root of the API. # To handle that, a similar configuration must be applied to the root resource that is built in to the REST API object resource "aws_api_gateway_method" "proxy_root" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_rest_api.example.root_resource_id http_method = "ANY" authorization = "NONE" } resource "aws_api_gateway_integration" "lambda_root" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_method.proxy_root.resource_id http_method = aws_api_gateway_method.proxy_root.http_method integration_http_method = "POST" type = "AWS_PROXY" # With AWS_PROXY, it causes API gateway to call into the API of another AWS service uri = aws_lambda_function.lambda_function.invoke_arn } # Deploy API Gateway resource "aws_api_gateway_deployment" "example" { depends_on = [ aws_api_gateway_integration.lambda, aws_api_gateway_integration.lambda_root, ] rest_api_id = aws_api_gateway_rest_api.example.id stage_name = "test" } # Output to the URL output "base_url" { value = aws_api_gateway_deployment.example.invoke_url } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-container-apigateway-flaskapp/2_api_gateway.tf ![image](https://user-images.githubusercontent.com/10358317/232439028-4659f2ab-27aa-4f7a-843e-355bcd53037b.png) ### Demo: Terraform Run - Run: ``` terraform init terraform validate terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/232347038-4b86203f-a61c-4e31-9d3c-086b789b53e2.png) - On AWS Lambda, new Lambda function is created: ![image](https://user-images.githubusercontent.com/10358317/232347067-959a2fd3-f151-4cf8-91e8-6d1f092b5062.png) - This time, Container is running on the Lambda: ![image](https://user-images.githubusercontent.com/10358317/232347172-1f4f0a52-0dc5-406d-9ba1-babc5c71c73d.png) - On AWS API-Gateway, new API is created: ![image](https://user-images.githubusercontent.com/10358317/232347258-ba322926-e628-4a62-9cf6-7f95c5a7c72e.png) - Flask App is running in container, container is running on Lambda: ![image](https://user-images.githubusercontent.com/10358317/232347302-da720c4a-da30-4ea7-a567-06b2d10686c3.png) - Edit posts with "Edit" button, new post with "New Post", Delete post with "Delete Post". - Copied DB file into the "/tmp/" directory to get write permission. Lambda allows only the files under "/tmp/" to have write permission. ![image](https://user-images.githubusercontent.com/10358317/232347487-b61b8383-93e0-4a92-a56c-035b7ed1d0c7.png) ![image](https://user-images.githubusercontent.com/10358317/232347570-82dc83e3-a869-488f-ba51-2446e555b6d2.png) - CloudWatch automatically logs the Lambda. If there is a debug issue, view the log groups under the Cloud Watch. ![image](https://user-images.githubusercontent.com/10358317/232348787-fda0e125-c6ed-4504-9e37-138501412838.png) - Destroy the infra: ``` terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/232347666-f2464a4d-85a7-49d2-bee4-c6062d886c22.png) - Delete the ECR Repo: ``` cd ecr terraform destroy ``` ![image](https://user-images.githubusercontent.com/10358317/232347728-ea8c3c13-4b22-4be3-bdba-b33a43023208.png) ================================================ FILE: SAMPLE06-EKS-ManagedNodes-Blueprint.md ================================================ ## SAMPLE-06: Provisioning EKS (Elastic Kubernetes Service) with Managed Nodes using Blueprint and Modules This sample shows: - how to create EKS cluster with managed nodes using BluePrints and Modules. **Notes:** - EKS Blueprint is used to provision EKS cluster with managed nodes easily. - TF file creates 65 Resources, it takes ~30 mins to provision cluster. - EKS Blueprint is used from: - https://github.com/aws-ia/terraform-aws-eks-blueprints - Gameserver example is updated and run: - https://github.com/aws-ia/terraform-aws-eks-blueprints/tree/main/examples/agones-game-controller There are 1 main part: - **main.tf**: includes: - EKS module: 2 managed nodes, 1 ASG for managed nodes, 1 Elastic IP, 3 SGs - EKS addon module: metrics_server, cluster_autoscaler - VPC module: 1 VPC, 6 Subnets (3 Public, 3 Private), 3 Route Tables, 1 IGW, 1 NAT, 1 NACL **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/eks-managed-node-blueprint **EKS Pricing:** - For the Cluster (Managed Control Plane): - **Per Hour:** $0.10 per for each Amazon EKS cluster - **Per Day:** $2.4 - **For 30 days:** $72 - For the 1 Worker Node Linux (e.g. m5.large => 2 vCPU, 8 GB RAM): - **Per Hour:** $0.096 - **Per Day:** $2.304 - **For 30 days:** $69.12 - Please have look for instance pricing: https://aws.amazon.com/ec2/pricing/on-demand/ - For the Fargate: - AWS Fargate pricing is calculated based on the **vCPU and memory** resources used from the time you start to download your container image until the EKS Pod terminate. - e.g. 2 x (1vCPU, 4GB RAM) on Linux: - **Per Hour:** 2 x ($0,0665) = $0.133 - **Per Day:** $3,18 - **Per 30 Days:** $95.67 - e.g. 2 x (1vCPU, 4GB RAM) on Win: - **Per Hour:** 2 x ($0,199) = $0.398 - **Per Day:** $9.55 - **Per 30 Days:** $286.56 - Please have look for fargate pricing: https://aws.amazon.com/fargate/pricing/ - https://cloudonaut.io/versus/docker-containers/eks-fargate-vs-eks-managed-node-group/ - https://cloudonaut.io/versus/docker-containers/ecs-fargate-vs-eks-managed-node-group/ ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Create main.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = ">= 4.47" } kubernetes = { source = "hashicorp/kubernetes" version = ">= 2.10" } helm = { source = "hashicorp/helm" version = ">= 2.4.1" } } required_version = ">= 1.2.0" } provider "aws" { region = local.region } provider "kubernetes" { host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) token = data.aws_eks_cluster_auth.this.token } provider "helm" { kubernetes { host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) token = data.aws_eks_cluster_auth.this.token } } data "aws_eks_cluster_auth" "this" { name = module.eks.cluster_name } data "aws_availability_zones" "available" {} locals { name = basename(path.cwd) region = "eu-central-1" cluster_version = "1.24" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) tags = { Blueprint = local.name GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" } } ################################################################################ # Cluster ################################################################################ #tfsec:ignore:aws-eks-enable-control-plane-logging module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 19.12" cluster_name = local.name cluster_version = local.cluster_version cluster_endpoint_public_access = true # EKS Addons cluster_addons = { coredns = {} kube-proxy = {} vpc-cni = {} } vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets eks_managed_node_groups = { initial = { instance_types = ["m5.large"] min_size = 1 max_size = 3 desired_size = 2 } } tags = local.tags } ################################################################################ # Kubernetes Addons ################################################################################ module "eks_blueprints_kubernetes_addons" { source = "github.com/aws-ia/terraform-aws-eks-blueprints/modules/kubernetes-addons" eks_cluster_id = module.eks.cluster_name eks_cluster_endpoint = module.eks.cluster_endpoint eks_oidc_provider = module.eks.oidc_provider eks_cluster_version = module.eks.cluster_version # Add-ons enable_metrics_server = true enable_cluster_autoscaler = true eks_worker_security_group_id = module.eks.cluster_security_group_id tags = local.tags } ################################################################################ # Supporting Resources ################################################################################ module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 4.0" name = local.name cidr = local.vpc_cidr azs = local.azs private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] enable_nat_gateway = true single_nat_gateway = true public_subnet_tags = { "kubernetes.io/role/elb" = 1 } private_subnet_tags = { "kubernetes.io/role/internal-elb" = 1 } tags = local.tags } output "configure_kubectl" { description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" value = "aws eks --region ${local.region} update-kubeconfig --name ${module.eks.cluster_name}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/eks-managed-node-blueprint/main.tf ![image](https://user-images.githubusercontent.com/10358317/233113882-6b81b0ca-d35a-42da-b1c0-4b5236375ddf.png) ### Demo: Terraform Run - Run: ``` terraform init terraform validate terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/233094869-ac12920c-d536-476a-b283-306c1846a3d7.png) - Run to add cluster into your kubeconfig: ``` aws eks --region eu-central-1 update-kubeconfig --name eks-managed-node ``` ![image](https://user-images.githubusercontent.com/10358317/233095399-80e169b6-de30-446e-84c2-7745655179a3.png) - To see nodes, pods ``` kubectl get nodes -o wide kubectl get pods -o wide --all-namespaces ``` ![image](https://user-images.githubusercontent.com/10358317/233095954-d3745626-eebc-483a-b298-c6d829ce7979.png) - Create test pod (nginx) ``` kubectl run my-nginx --image=nginx kubectl get pods -o wide --all-namespaces ``` ![image](https://user-images.githubusercontent.com/10358317/233096322-d9738e2e-5ea5-4187-8763-d9cabd27ffd7.png) ![image](https://user-images.githubusercontent.com/10358317/233096504-a991ed50-5a85-4f46-bc33-b795237f26b4.png) - Enter the pod using sh: ``` kubectl exec -it my-nginx -- sh ``` ![image](https://user-images.githubusercontent.com/10358317/233097613-f81a3773-97cc-4795-aab0-82795417b270.png) - On AWS EKS: ![image](https://user-images.githubusercontent.com/10358317/233097928-5db41f20-f491-491d-bf09-08c38df7ec66.png) - Cluster Pod: ![image](https://user-images.githubusercontent.com/10358317/233098383-57785983-298a-41d6-8bdb-54f6f80eff47.png) - Cluster Deployments: ![image](https://user-images.githubusercontent.com/10358317/233098742-86803d33-1565-4577-aee5-ef86f74b7717.png) - By using kubectl, details can be viewed: ![image](https://user-images.githubusercontent.com/10358317/233099053-5087c41a-2366-42c8-8b0f-235edb7c8325.png) - Cluster Nodes: ![image](https://user-images.githubusercontent.com/10358317/233099628-31296830-ef25-4d1b-b582-92f6cf936600.png) - Because of the min_size=1, one of the managed node was terminated automatically. ``` eks_managed_node_groups = { initial = { instance_types = ["m5.large"] min_size = 1 max_size = 3 desired_size = 2 } } ``` - On AWS EKS: ![image](https://user-images.githubusercontent.com/10358317/233104836-bdc1a075-e27b-45b0-8fb2-262e73814f47.png) - API Services: ![image](https://user-images.githubusercontent.com/10358317/233100299-05267ceb-559b-4c11-9876-d427192b50bc.png) - All details about the K8s can be viewed on AWS GUI: ![image](https://user-images.githubusercontent.com/10358317/233100875-4b47facd-a02e-4f93-9431-68556e3d7c52.png) - On AWS VPC (1 VPC, 3 public subnets, 3 private subnets): ![image](https://user-images.githubusercontent.com/10358317/233101406-d1ec1fe1-0359-4b24-9d66-c3e45aa2a911.png) - On AWS EC2 Instance: ![image](https://user-images.githubusercontent.com/10358317/233101685-89e5780e-57d9-4720-bb16-ece80092060b.png) - On AWS Elastic IP: ![image](https://user-images.githubusercontent.com/10358317/233102192-efce23f0-b2f0-48ec-9530-14210f6c6525.png) - In addition, these are created: 3 Route Tables, 1 IGW, 1 NAT, 1 Elastic IP, 1 NACL, 1 ASG for managed nodes ![image](https://user-images.githubusercontent.com/10358317/233104088-376aa429-3502-4347-8cdd-a176ed3bdc74.png) - To clean up your environment, destroy the Terraform modules in reverse order. - Destroy the Kubernetes Add-ons, EKS cluster with Node groups and VPC: ``` terraform destroy -target="module.eks_blueprints_kubernetes_addons" -auto-approve ``` ![image](https://user-images.githubusercontent.com/10358317/233106155-b4e43456-2852-4476-bb05-8e734abb3ba4.png) - Destroy EKS (managed node) (takes ~12-15 mins): ``` terraform destroy -target="module.eks" -auto-approve ``` ![image](https://user-images.githubusercontent.com/10358317/233111468-092cff32-9555-4814-8a80-10678b590d12.png) - Destroy VPC: ``` terraform destroy -target="module.vpc" -auto-approve ``` ![image](https://user-images.githubusercontent.com/10358317/233110739-72c833a1-6347-4e9a-be33-5f4d93f7990f.png) - Finally, destroy any additional resources that are not in the above modules ``` terraform destroy -auto-approve ``` ## References - https://github.com/aws-ia/terraform-aws-eks-blueprints - https://github.com/aws-ia/terraform-aws-eks-blueprints/tree/main/examples/agones-game-controller ================================================ FILE: SAMPLE07-CodeCommit-Pipeline-Build-Deploy-Lambda.md ================================================ ## SAMPLE-07: CI/CD on AWS => Provisioning CodeCommit and CodePipeline, Triggering CodeBuild and CodeDeploy on Lambda Container This sample shows: - how to create code repository using CodeCommit, - how to create pipeline with CodePipeline, create S3 bucket to store Artifacts for codepipeline stages' connection (source, build, deploy), - how to create builder with CodeBuild ('buildspec_build.yaml'), build the source code, create a Docker image, - how to create ECR (Elastic Container Repository) and push the build image into the ECR, - how to create Lambda Function (by CodeBuild automatically) and run/deploy container on Lambda ('buildspec_deploy.yaml'). **Notes:** - Source code is pulled from: - https://github.com/aws-samples/codepipeline-for-lambda-using-terraform - Some of the fields are updated. - It works with 'hashicorp/aws ~> 4.15.1', 'terraform >= 0.15' **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container ![image](https://user-images.githubusercontent.com/10358317/233652299-66b39788-66ee-4a5e-b8e0-ece418fe98e3.png) # Table of Contents - [Main Part](#main_part) - [CodeCommit Module](#codecommit) - [CodePipeline Module](#codepipeline) - [ECR Module](#ecr) - [Lambda Part](#lambda) - [Demo: Terraform Run](#run) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Code: ![image](https://user-images.githubusercontent.com/10358317/233652134-e5c732c0-1c88-4244-b321-b8f777476c85.png) ### Main Part - Create main.tf: ``` locals { env_namespace = join("_", [var.org_name, var.team_name, var.project_id, var.env["dev"]]) general_namespace = join("_", [var.org_name, var.team_name, var.project_id]) #s3 bucket naming based on best practices: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html s3_bucket_namespace = join("-", [var.org_name, var.team_name, var.project_id, var.env["dev"]]) } data "aws_caller_identity" "current" {} module "codepipeline" { source = "./modules/codepipeline" general_namespace = local.general_namespace env_namespace = local.env_namespace s3_bucket_namespace = local.s3_bucket_namespace codecommit_repo = module.codecommit.codecommit_configs.repository_name codecommit_branch = module.codecommit.codecommit_configs.default_branch codebuild_image = var.codebuild_image codebuild_type = var.codebuild_type codebuild_compute_type = var.codebuild_compute_type ecr_repo_arn = module.ecr.ecr_configs.ecr_repo_arn build_args = [ { name = "REPO_URI" value = module.ecr.ecr_configs.ecr_repo_url }, { name = "REPO_ARN" value = module.ecr.ecr_configs.ecr_repo_arn }, { name = "TERRAFORM_VERSION" value = var.terraform_ver }, { name = "ENV_NAMESPACE" value = local.env_namespace }, { name = "AWS_ACCOUNT_ID" value = data.aws_caller_identity.current.account_id } ] } module "codecommit" { source = "./modules/codecommit" general_namespace = local.general_namespace env_namespace = local.env_namespace codecommit_branch = var.codecommit_branch } module "ecr" { source = "./modules/ecr" general_namespace = local.general_namespace env_namespace = local.env_namespace } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/main.tf ![image](https://user-images.githubusercontent.com/10358317/233831575-895408b8-0929-4125-9716-96675f3878f0.png) - Create output.tf: ``` output "codepipeline" { value = module.codepipeline.codepipeline_configs.codepipeline } output "codecommit" { value = module.codecommit.codecommit_configs.clone_repository_url } output "ecrrepo" { value = module.ecr.ecr_configs.ecr_repo_url } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/outputs.tf ![image](https://user-images.githubusercontent.com/10358317/233831613-28d0dc5d-2299-4862-84e2-ce5a4d220c13.png) - Create providers.tf: ``` provider "aws" { region = "eu-central-1" } terraform { required_version = ">= 0.15" required_providers { aws = { source = "hashicorp/aws" version = "~> 4.15.1" # Will allow installation of 4.15.1 and 4.15.10 but not 4.16.0 # Get error when using 4.16.0 } } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/providers.tf ![image](https://user-images.githubusercontent.com/10358317/233831633-549163df-5e40-4f6d-a025-f6c70e726a41.png) - Create terraform.tfvars: ``` org_name = "org" team_name = "awsteam" project_id = "lambda" region = "eu-central-1" env = { "dev" = "dev" "qa" = "qa" } codebuild_compute_type = "BUILD_GENERAL1_MEDIUM" codebuild_image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" codebuild_type = "LINUX_CONTAINER" codecommit_branch = "master" ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/terraform.tfvars ![image](https://user-images.githubusercontent.com/10358317/233831672-ad9901a6-3706-4527-a301-f8d0659c1805.png) - Create variables.tf: ``` variable "org_name" { description = "Your Organization name" type = string } variable "team_name" { description = "Your Team name" type = string } variable "project_id" { description = "Your Project ID" type = string } variable "env" { description = "Your deployment environment" type = map(any) default = { "dev" = "dev" } } variable "region" { description = "Your AWS Region" type = string } variable "codebuild_type" { description = "Your CodeBuild Project Type" type = string } variable "codebuild_image" { description = "Your CodeBuild Project Image" type = string } variable "codebuild_compute_type" { description = "Your CodeBuild Project Compute Type" type = string } variable "codecommit_branch" { description = "Your CodeCommit Branch" type = string } variable "terraform_ver" { description = "Terraform Version number for passing it to codebuild" default = "1.2.2" type = string } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/variables.tf ![image](https://user-images.githubusercontent.com/10358317/233831701-4d1d1357-ea2e-400d-b53d-da08acf9cd12.png) ### CodeCommit Module - Create main.tf: ``` data "aws_caller_identity" "current" {} resource "aws_codecommit_repository" "codecommit_repo" { repository_name = "${var.general_namespace}_code_repo" default_branch = "${var.codecommit_branch}" description = "Application repo for lambda ${var.general_namespace}" } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/codecommit/main.tf ![image](https://user-images.githubusercontent.com/10358317/233831728-26ce5e78-3738-4be6-bf1b-8d67bd605817.png) - Create outputs.tf: ``` output "codecommit_configs" { value = { repository_name = aws_codecommit_repository.codecommit_repo.repository_name default_branch = aws_codecommit_repository.codecommit_repo.default_branch clone_repository_url = aws_codecommit_repository.codecommit_repo.clone_url_http } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/codecommit/outputs.tf ![image](https://user-images.githubusercontent.com/10358317/233831757-a363b671-be88-4ac3-a5a8-13cd2e870491.png) - Create variables.tf: ``` variable "codecommit_branch" { type = string default = "master" } variable "general_namespace" { type = string } variable "env_namespace" { type = string } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/codecommit/variables.tf ![image](https://user-images.githubusercontent.com/10358317/233831783-fdec8f23-5d03-44a4-8b1a-824581a21af0.png) ### CodePipeline Module - Create main.tf ``` locals { projects = ["build", "deploy"] } resource "aws_s3_bucket" "codepipeline_bucket" { bucket = "${var.s3_bucket_namespace}-codepipeline-bucket" } resource "aws_s3_bucket_acl" "codepipeline_bucket_acl" { bucket = aws_s3_bucket.codepipeline_bucket.id acl = "private" } resource "aws_codebuild_project" "project" { count = length(local.projects) name = "${var.env_namespace}_${local.projects[count.index]}" #name = "${var.org}_${var.name}_${var.attribute}_${var.env["dev"]}_codebuild_docker_build" build_timeout = "5" #The default is 60 minutes. service_role = aws_iam_role.lambda_codebuild_role.arn artifacts { type = "CODEPIPELINE" } environment { compute_type = var.codebuild_compute_type image = var.codebuild_image type = var.codebuild_type #compute_type = "BUILD_GENERAL1_MEDIUM" #image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" #type = "LINUX_CONTAINER" image_pull_credentials_type = "CODEBUILD" privileged_mode = true dynamic "environment_variable" { for_each = var.build_args content { name = environment_variable.value.name value = environment_variable.value.value } } } source { type = "CODEPIPELINE" buildspec = file("${path.module}/templates/buildspec_${local.projects[count.index]}.yml") #buildspec = file("${path.module}/stage1-buildspec.yml") } source_version = "master" tags = { env = var.env_namespace } } resource "aws_codepipeline" "codepipeline" { name = "${var.env_namespace}_pipeline" role_arn = aws_iam_role.lambda_codepipeline_role.arn artifact_store { location = aws_s3_bucket.codepipeline_bucket.bucket type = "S3" } stage { name = "Source" action { name = "Source" category = "Source" owner = "AWS" provider = "CodeCommit" version = "1" output_artifacts = [ "source_output"] configuration = { #BranchName = aws_codecommit_repository.lambda_codecommit_repo.default_branch BranchName = var.codecommit_branch RepositoryName = var.codecommit_repo #RepositoryName = aws_codecommit_repository.lambda_codecommit_repo.repository_name } } } stage { name = "Build" action { name = "Docker_Build" category = "Build" owner = "AWS" provider = "CodeBuild" input_artifacts = [ "source_output"] version = "1" configuration = { ProjectName = aws_codebuild_project.project[0].name } } } stage { name = "Deploy" action { name = "Deploy_Docker" category = "Build" owner = "AWS" provider = "CodeBuild" input_artifacts = [ "source_output"] version = "1" configuration = { ProjectName = aws_codebuild_project.project[1].name } } } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/codepipeline/main.tf ![image](https://user-images.githubusercontent.com/10358317/233831984-3bcab992-17a0-413b-bf1e-f38289a959a2.png) ![image](https://user-images.githubusercontent.com/10358317/233832006-949a9c52-d266-4150-97c7-0305cceddc08.png) - Create outputs.tf ``` output "codepipeline_configs" { value = { codepipeline = aws_codepipeline.codepipeline.arn } } output "deployment_role_arn" { value = aws_iam_role.lambda_codebuild_role.arn } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/codepipeline/outputs.tf ![image](https://user-images.githubusercontent.com/10358317/233832055-65f1b99b-4a16-47b7-9c01-b88d3ec256d8.png) - Create roles.tf ``` resource "aws_iam_role" "lambda_codepipeline_role" { name = "${var.env_namespace}_codepipeline_role" assume_role_policy = < - Create main.tf: ``` data "aws_caller_identity" "current" {} resource "aws_ecr_repository" "ecr_repo" { name = "${var.general_namespace}_docker_repo" #image_tag_mutability = "IMMUTABLE" #a capability that prevents image tags from being overwritten image_scanning_configuration { scan_on_push = true #https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html } tags = { env = var.env_namespace } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/ecr/main.tf ![image](https://user-images.githubusercontent.com/10358317/233832299-e05ba389-3a04-47aa-b91a-478f75b72493.png) - Create outputs.tf: ``` output "ecr_configs" { value = { ecr_repo_url = aws_ecr_repository.ecr_repo.repository_url ecr_repo_arn = aws_ecr_repository.ecr_repo.arn } } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/ecr/outputs.tf ![image](https://user-images.githubusercontent.com/10358317/233832327-78081e78-7477-4f98-909f-f2c03450a8a2.png) - Create variables.tf: ``` variable "general_namespace" { type = string } variable "env_namespace" { type = string } ``` **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/modules/ecr/variables.tf ![image](https://user-images.githubusercontent.com/10358317/233832341-f9cd9510-ce6d-40fb-8562-4b6d56a46b98.png) ### Lambda Part - Create main.tf: ``` data "aws_ecr_image" "lambda_image_latest" { repository_name = split("/", var.ecr_repo_url)[1] image_tag = "latest" } resource "aws_iam_role" "iam_for_lambda" { name = "${var.env_namespace}_lambda_role" assume_role_policy = < - Run: ``` terraform init terraform validate terraform plan terraform apply ``` ![image](https://user-images.githubusercontent.com/10358317/233833654-1382237d-7052-483c-806d-9c3ecc1f6229.png) - Create IAM CodeCommit HTTPS Key on AWS IAM. Go to IAM User > User > Security Credentials > HTTPS Git credentials for AWS CodeCommit - Generate credentials (username and password) to push the code to the CodeCommit ![image](https://user-images.githubusercontent.com/10358317/233833803-93025986-640a-43ca-afd2-4a1c8494f197.png) - Pull the repo from AWS CodeCommit: ``` git clone https://git-codecommit.eu-central-1.amazonaws.com/v1/repos/org_awsteam_lambda_code_repo ``` ![image](https://user-images.githubusercontent.com/10358317/233833969-fc1aed7f-67cc-4a4d-b932-2acca8bc6527.png) - Enter Credential on Git Bash Emulator to download empty project: ``` git config --global credential.helper '!aws codecommit credential-helper --profile CodeCommitProfile $@' git config --global credential.UseHttpPath true git clone https://git-codecommit.eu-central-1.amazonaws.com/v1/repos/org_awsteam_lambda_code_repo ``` ![image](https://user-images.githubusercontent.com/10358317/233834809-13d3cd70-ee69-46dc-907e-214a3e8d9375.png) - Copy the "lambda_bootstrap" into the downloaded project 'org_awsteam_lambda_code_repo': ``` cp lambda_bootstrap /org_awsteam_lambda_code_repo/ git add . && git commit -m "Initial Commit" && git push ``` - Push the code into the CodeCommit Repo: ![image](https://user-images.githubusercontent.com/10358317/233834664-85a51381-f938-46e8-afb0-23064c4dbd09.png) - On AWS CodeCommit, code is pushed: ![image](https://user-images.githubusercontent.com/10358317/233834726-fa1228d9-772a-4931-bf3f-4dcc6f4bb523.png) - After pushing the code, it triggers CodePipeline: ![image](https://user-images.githubusercontent.com/10358317/233835282-b53e5022-eac6-4212-9245-309db4c68678.png) - On AWS CodeBuild: ![image](https://user-images.githubusercontent.com/10358317/233835172-f6afe81a-f6d3-4065-935c-f086ae6dbd0f.png) - On AWS CodeBuild, Dev Code Build Logs: ![image](https://user-images.githubusercontent.com/10358317/233835613-f26c8658-7ea9-4deb-92f0-3b7607112e80.png) - On AWS CodeBuild, Deploy Image Build Logs on Lambda: ![image](https://user-images.githubusercontent.com/10358317/233835429-d0c6a136-9580-4ef1-b343-9bce48972822.png) - On AWS ECR, Docker image was pushed by CodeBuild: ![image](https://user-images.githubusercontent.com/10358317/233835727-821e1456-2799-4e67-9994-7f3382d327ab.png) - On AWS Lambda: ![image](https://user-images.githubusercontent.com/10358317/233835833-3ec1c5cb-0235-44f3-84a9-9491ffc996b5.png) - Testing interface with Lambda Test: ![image](https://user-images.githubusercontent.com/10358317/233835876-a6b87862-730b-4bb4-ae6a-66389b0cd744.png) - Lambda Code returns (it returns house prices and addresses): ![image](https://user-images.githubusercontent.com/10358317/233835933-129e6368-5d65-45a5-8b3e-204cd496f1d5.png) - On AWS S3, S3 bucket stores Artifacts for codepipeline stages' connection (source, build, deploy): ![image](https://user-images.githubusercontent.com/10358317/233836246-8474c407-367b-4795-9d39-ddbe2d952a6e.png) - Before destroy, delete the bucket file using GUI: ![image](https://user-images.githubusercontent.com/10358317/233836359-2bef706c-348e-4b5d-9b45-d41fd7804394.png) - Finally, destroy infrastructure: ``` terraform destroy -auto-approve ``` ![image](https://user-images.githubusercontent.com/10358317/233836460-b773ccfb-8a35-4f5d-9002-61a0ce2dbad0.png) - After destroying; control Lambda, ECR, S3, CodeCommit, CodeBuild, CodePipeline to be sure that everything is deleted. ## References - https://github.com/aws-samples/codepipeline-for-lambda-using-terraform ================================================ FILE: SAMPLE08-S3-CloudFront-Static-WebSite.md ================================================ ## SAMPLE-08: Provisioning S3 and CloudFront to serve Static Web Site This sample shows: - how to create S3 Bucket, - how to to copy the website to S3 Bucket, - how to configure S3 bucket policy, - how to create CloudFront distribution to refer S3 Static Web Site, - how to configure CloudFront (default_cache_behavior, ordered_cache_behavior, ttl, price_class, restrictions, viewer_certificate). **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/s3-cloudfront-static-website/ ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Create s3.tf: ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_s3_bucket" "mybucket" { bucket = "s3-mybucket-website2023" acl = "private" # Add specefic S3 policy in the s3-policy.json on the same directory # policy = file("s3-policy.json") versioning { enabled = false } website { index_document = "index.html" error_document = "error.html" # Add routing rules if required # routing_rules = < gitlab-rake gitlab:password:reset # run in the container > username: root > password:987aws12345 > Password successfully updated for user with username root. > exit ``` ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/0c122fd7-645a-40c3-bfb6-799d9c2fcd82) - If you run docker in the WSL, call browser in the WSL: "sensible-browser http://gitlab.example.com:150/" ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/f3e6d11a-6b6e-4ab6-a8c6-01c718090243) - This runs on 127.0.0.1:150 when you run "netstat -an" on windows, but we want to run it on the host machine IP "192.168.178.28:150" - Use PORT Proxy from 127.0.0.1:150 to 192.168.178.28:150 ``` netsh interface portproxy add v4tov4 listenport=150 connectaddress=127.0.0.1 connectport=150 listenaddress=192.168.178.28 protocol=tcp # for delete: netsh interface portproxy delete v4tov4 listenport=150 listenaddress=192.168.178.28 protocol=tcp ``` - Now, when it runs on both hostIP:150 and 127.0.0.1:150. This requires to NAT Forwarding ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/f1419f87-f3ef-4666-b4d8-9bb6fd3e0295) - On Network Config, make connection (ethernet or wireless) to static IP 192.168.178.28. ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/437d7e0b-5a76-4a94-999b-a9c6dfc07f67) - On the modem switch, enable NAT Forwarding, this enables that external traffic redirects to the host machine: 192.168.178.1 => Internet.Permit Access => PC, TCP Port 150 through 150, external 150. - Test on browser (gitlab server runs) on 192.168.178.28:150: - On 'C:\Windows\System32\drivers\etc\host' file, add '127.0.0.1 gitlab.example.com', test with 'gitlab.example.com' on browser on-premise: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/2349083d-c9b2-4ad2-980a-24839d169e76) - Close/pause the firewall to reach the service from outside. - Learn the external IP with googling 'what is my IP', and test 'externalIP:150' on browser: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/1a692453-4b6e-4bc6-9898-bfe31f28ee1d) - Create EC2 on AWS with: **Code:** https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/gitlabserver-on-premise-runner-on-EC2/main.tf ``` terraform init terraform plan terraform apply ``` - Make SSH to Ubuntu ``` ssh -i .\testkey.pem ubuntu@UbuntuPublicIP ``` - While launching the EC2, docker and gitlab-runner were installed on it. - Run 'curl http://externalIP:150', if it works, the connection was done to gitlab server on-premise - Register gitlab-runner using 'sudo gitlab-runner register', url: http://gitlab.example.com:150/, token from Gitlab Server > Admin > CI > Runners > Register new Runner (for shared-runner), add tag: ec2-shared, executable: docker, and alpine. ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/d6187256-775f-4abc-a76b-c81fa1b462fd) - Runners was added, to configure to see the local URL: - 'nano /etc/gitlab-runner/config.toml', url = "http://gitlab.example.com:150/", add extra => 'clone_url = "http://88.xx.xx.xx:150"' (externalIP:150), - Restart gitlab-runner on EC2: 'sudo gitlab-runner restart' ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/8d97fbde-6e04-4038-bcbb-d7cafc1fb7c1) - List runners on EC2 'sudo gitlab-runner list': ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/b889a30a-1f0a-4668-b1a4-44633a448e46) - Builds => gitlab runners run on EC2, responds to results to Gitlab Server on-premise: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/67e7e90f-caa1-462d-9125-02dd324519a6) - Running jobs on EC2: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/8964536b-5df5-427e-bb45-9091fc07ed29) ## References: - https://nagachiang.github.io/gitlab-ci-gitlab-runner-cant-fetch-changes-from-repository/# ================================================ FILE: SAMPLE10-MLOps-SageMaker-GitHub-Codepipeline-CodeBuild-CodeDeploy.md ================================================ ## SAMPLE-10: Implementing MLOps Pipeline using GitHub, AWS CodePipeline, AWS CodeBuild, AWS CodeDeploy, and AWS Sagemaker (Endpoint) This sample shows: - how to create MLOps Pipeline - how to use GitHub Hooks (Getting Source Code from Github to CodePipeline) - how to create Build CodePipeline (Source, Build), CodeBuild (modelbuild_buildspec.yml), Deploy CodePipeline (Source, Build, DeployStaging, DeployProd), CodeBuild (modeldeploy_buildspec.yml) - how to save the model and artifacts on S3 - how to create and test models using Notebooks **Notes:** - Original Source code was pulled, updated, and adapted: - https://github.com/aws-samples/aws-mlops-pipelines-terraform - "Modelbuild_Pipeline" and "Modeldeploy_Pipeline" are uploaded before. After applying terraform, Webhook in the CodePipeline pulls it from GitHub to inject it into AWS. - If you run it the first time, please open to request to AWS for the instance: "ml.m5.xlarge" ("ResourceLimitExceeded"). - https://repost.aws/knowledge-center/sagemaker-resource-limit-exceeded-error **Code:** https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy **Architecture:** - **Model Build Pipeline Source Code, modelbuild_pipeline:** https://github.com/omerbsezer/modelbuild_pipeline - **Model Deploy Pipeline Source Code, modeldeploy_pipeline:** https://github.com/omerbsezer/modeldeploy_pipeline - AWS Code Pipeline for **Model Build** (CI): - AWS Code Pipeline (**modelbuild_ci_pipeline.tf**) - Stage: Source (Hook from GitHub: **modelbuild_hooks.tf**) - Stage: Build (**modelbuild_codebuild.tf: artifacts, environment, source (modelbuild_buildspec.yml: run build pipeline)**) - Sagemaker Data Pipeline (**modelbuild_pipeline project: pipeline.py**): - Preprocessing Step (**modelbuild_pipeline project: pipeline.py => preprocess.py**) - Model Training Step (**modelbuild_pipeline project: pipeline.py => XGB BuiltIn Container**) - Evaluation Step (**modelbuild_pipeline project: pipeline.py => evaluate.py, ConditionStep to evaluate model quality**) - Registering Model Step (**modelbuild_pipeline project: pipeline.py => RegisterModel**) - AWS Code Pipeline for **Model Deploy** (CD): - AWS Code Pipeline (**modeldeploy_cd_pipeline.tf**) - Stage: Source (Hook from GitHub: **modeldeploy_hooks.tf**) - Stage: Build (**modeldeploy_codebuild.tf: artifacts, environment, source (modeldeploy_buildspec.yml: run deploy pipeline => modeldeploy_pipeline project: build.py, cloud formation to create endpoint)**) - Stage: DeployStaging: - Action: Deploy (**Cloudformation, DeployResourcesStaging: modeldeploy_pipeline project => endpoint-config-template.yml**) - Action: Build (**Test Staging: modeldeploy_testbuild.tf => modeldeploy_pipeline project: test/test_buildspec.yml**) - Action: Approval (**Manual Approval by User**) - Stage: DeployProd: - Action: Deploy (**Cloudformation, DeployResourcesProd: modeldeploy_pipeline project => endpoint-config-template.yml**) - Notebooks (for testing) (region: us-east-1) - End2end.ipynb - https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/SageMaker_Customer_Churn_XGB_end2end.ipynb - Pipeline.ipynb (Sagemaker Data Pipeline) - https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/SageMaker_Customer_Churn_XGB_Pipeline.ipynb ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/09c0ac8a-0fe5-4877-8440-b29a22bad5cf) ### Prerequisite - You should have a look following lab: - [LAB-00: Terraform Install, AWS Configuration with Terraform](https://github.com/omerbsezer/Fast-Terraform/blob/main/LAB00-Terraform-Install-AWS-Configuration.md) ## Steps - Before running Terraform, upload "Modelbuild_Pipeline" and "Modeldeploy_Pipeline" in your GitHub account. - Run: ``` cd terraform terraform init terraform validate terraform plan terraform apply ``` - After run: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/7e2060d0-af1c-4b5e-af43-a100f163453b) - AWS CodePipeline: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/7daef38d-2901-4087-b990-3d8b3676783e) - ModelBuild: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/1bc7cfe7-a7b5-4155-b4a1-cbb8763a036d) - ModelBuild Log: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/72ba0e56-a66f-4427-b35c-5680ae681fc6) - AWS S3: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/5b6da22e-7ec8-4a61-821b-3ebc8d272593) - ModelBuild was done successfully: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/9e4bad11-883e-463a-928e-d87834439e6f) - CloudWatch to see the training accuracy: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/fa0dbc0e-e3a0-48d2-8c75-309d6dcf9e19) - CloudWatch, Log Groups, Train Error: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/095870fb-ff0d-4ce8-bbe1-348779d9be25) - ModelDeploy: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/a77c0467-3453-41dc-8f8c-c3995973bf82) ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/6fb4beb5-3d17-459f-8b31-a487b07d39f1) - CloudFormation, stack: deploy-staging ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/205f6a3e-1e68-4fc6-895b-d30810c4e50c) - SageMaker Dashboard, staging endpoint in service: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/4121d5c4-c2db-4a2d-8a83-c5b2183e334b) - SageMaker, Model: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/ab3e3090-5f5b-42aa-84ff-53cc17a9a380) - S3, Model, possible to download: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/09bdaf28-582a-45bc-b4b1-d053da0ec206) - Try Staging Endpoint with notebook (end2end.ipynb, last cell, enter the endpointname): ``` import pandas as pd import numpy as np import sagemaker import boto3 from sagemaker import get_execution_role test_data=pd.read_csv('test.csv',header=None) testdata1=test_data.iloc[0:1,1:] runtime = boto3.client("sagemaker-runtime") Endpoint_name='aws-ml-11052023-staging-0306' # # update to your own endpoint name prediction = runtime.invoke_endpoint( EndpointName=Endpoint_name, Body=testdata1.to_csv(header=False, index=False).encode("utf-8"), ContentType="text/csv", Accept= "text/csv", ) print(prediction["Body"].read()) ``` - Endpoint returned the result: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/58d56412-b3a2-42bc-978c-4c828d3d1af8) - Approve the Product on the CodePipeline: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/f2ee09ce-3d65-4448-9732-a5cc5a9277db) - SageMaker Dashboard, 2 Endpoints are in-service: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/cfd146dd-cbe5-4622-946f-0ca457597e26) - SageMaker, Prod Endpoint: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/444de0ff-f29d-42cd-b471-e17e4b13b904) - CloudFormation: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/f803e721-2d4c-4545-a0ba-d72a260cf2e0) - Test Prod Endpoint, returns results: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/e0ea3640-5b16-47ff-9fbf-cd1cdc81b974) - Delete Endpoints manually, if the endpoints are in-service, you have to pay their cost: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/2d30f570-ad34-4972-bee7-05a5058f7c3b) - Delete stacks manually in Cloudformation. - Download artifacts on S3: ``` aws s3 sync s3://artifact-ml-11052023 C:\Users\oesezer\Desktop\aws-artifacts ``` - Downloaded to the local PC: ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/d1e30b6c-7497-4913-a25b-9e1bbd92556e) - Destroy with "terraform destroy": ![image](https://github.com/omerbsezer/Fast-Terraform/assets/10358317/cfcf5bc8-a3bc-4ef5-a661-ab6873aa5f65) - Check whether all created artifacts are deleted on CodePipeline, S3, CloudFormation, SageMaker, and CloudWatch (LogGroups) or not. If still some of the artifacts are in the AWS, please delete them all. ## References - https://github.com/aws-samples/aws-mlops-pipelines-terraform ================================================ FILE: Terraform-Cheatsheet.md ================================================ ## Terraform Cheatsheet ### Table of Contents - [Help, Version](#help) - [Formatting](#formatting) - [Initialization](#init) - [Download and Install Modules](#download) - [Validation](#validation) - [Plan Your Infrastructure](#plan) - [Deploy Your Infrastructure](#deploy) - [Destroy Your Infrastructure](#destroy) - ['Taint' or 'Untaint' Your Resources](#taint) - [Refresh the State File](#refresh) - [Show Your State File](#show) - [Manipulate Your State File](#state) - [Import Existing Infrastructure](#import) - [Get Provider Information](#provider) - [Manage Your Workspaces](#workspace) - [View Your Outputs](#output) - [Release a Lock on Your Workspace](#release) - [Log In and Out to a Remote Host (Terraform Cloud)](#login) - [Produce a Dependency Diagram](#graph) - [Test Your Expressions](#test) ### 1. Help, Version #### Get the general help for Terraform ``` terraform -help ``` #### Get the 'fmt' help for Terraform ``` terraform fmt -help ``` #### Get the Terraform version ``` terraform version ``` ### 2. Formatting #### Format your Terraform configuration files using the HCL language standard ``` terraform fmt ``` #### Format files in subdirectories ``` terraform fmt --recursive ``` #### Display differences between original configuration files and formatting changes ``` terraform fmt --diff ``` #### Format Check (If files are formatted correctly, the exit status will be zero) ``` terraform fmt --check ``` ### 3. Initialization #### Terraform Init Command (performs Backend Initialization, Child Module Installation, and Plugin Installation) ``` terraform init ``` #### Initialize the working directory, do not download plugins ``` terraform init -get-plugins=false ``` #### Initialize the working directory, don’t hold a state lock during backend migration ``` terraform init -lock=false ``` #### Initialize the working directory, and disable interactive prompts ``` terraform init -input=false ``` #### Reconfigure a backend, and attempt to migrate any existing state ``` terraform init -migrate-state ``` #### Initialize the working directory, do not verify plugins for Hashicorp signature ``` terraform init -verify-plugins=false ``` ### 4. Download and Install Modules Note this is usually not required as this is part of the terraform init command. #### Download and installs modules needed for the configuration ``` terraform get ``` #### Check the versions of the already installed modules against the available modules and installs the newer versions if available ``` terraform get -update ``` ### 5. Validation #### Validate the configuration files in your directory and does not access any remote state or services - Terraform init should be run before 'validate' command. ``` terraform validate ``` #### To see easier the number of errors and warnings that you have ``` terraform validate -json ``` ### 6. Plan Your Infrastructure #### Plan will generate an execution plan ``` terraform plan ``` #### Save the plan file to a given path - Saved file can then be passed to the terraform apply command. ``` terraform plan -out= ``` #### Create a plan to destroy all objects rather than the usual actions ``` terraform plan -destroy ``` ### 7. Deploy Your Infrastructure #### Create or update infrastructure depending on the configuration files - By default, a plan will be generated first and will need to be approved (yes/no) before it is applied. ``` terraform apply ``` #### Apply changes without having to interactively type ‘yes’ to the plan - Useful in automation CI/CD pipelines. ``` terraform apply -auto-approve ``` #### Provide the file generated using the terraform plan -out command - If provided, Terraform will take the actions in the plan without any confirmation prompts. ``` terraform apply ``` #### Do not hold a state lock during the Terraform apply operation - Use with caution if other engineers might run concurrent commands against the same workspace. ``` terraform apply -lock=false ``` #### Specify the number of operations run in parallel ``` terraform apply -parallelism= ``` #### Pass in a variable value ``` terraform apply -var="environment=dev" ``` #### Pass in variables contained in a file ``` terraform apply -var-file="varfile.tfvars" ``` #### Apply changes only to the targeted resource ``` terraform apply -target="module.appgw.0" ``` ### 8. Destroy Your Infrastructure #### Destroy the infrastructure managed by Terraform ``` terraform destroy ``` #### Destroy only the targeted resource ``` terraform destroy -target="module.appgw.0" ``` #### Destroy the infrastructure without having to interactively type ‘yes’ to the plan - Useful in automation CI/CD pipelines. ``` terraform destroy --auto-approve ``` #### Destroy an instance of a resource created with for_each ``` terraform destroy -target="module.appgw.resource[\"key\"]" ``` ### 9. 'Taint' or 'Untaint' Your Resources - Use the taint command to mark a resource as not fully functional. It will be deleted and re-created. #### Taint a specified resource instance ``` terraform taint vm1.name ``` #### Untaint the already tainted resource instance ``` terraform untaint vm1.name ``` ### 10. Refresh the State File #### Modify the state file with updated metadata containing information on the resources being managed in Terraform - Will not modify your infrastructure. ``` terraform refresh ``` ### 11. Show Your State File #### Show the state file in a human-readable format ``` terraform show ``` #### Show the specific state file - If you want to read a specific state file, you can provide the path to it. If no path is provided, the current state file is shown. ``` terraform show ``` ### 12. Manipulate Your State File #### Lists out all the resources that are tracked in the current state file ``` terraform state list ``` #### Move an item in the state - Move an item in the state, for example, this is useful when you need to tell Terraform that an item has been renamed, e.g. terraform state mv vm1.oldname vm1.newname ``` terraform state mv ``` #### Get the current state and outputs it to a local file ``` terraform state pull > state.tfstate ``` #### Update remote state from the local state file ``` terraform state push ``` #### Replace a provider, useful when switching to using a custom provider registry ``` terraform state replace-provider hashicorp/azurerm customproviderregistry/azurerm ``` #### Remove the specified instance from the state file - Useful when a resource has been manually deleted outside of Terraform ``` terraform state rm ``` #### Show the specified resource in the state file ``` terraform state show ``` ### 13. Import Existing Infrastructure #### Import Existing Infrastructure into Your Terraform State - Import a VM with id123 into the configuration defined in the configuration files under vm1.name ``` terraform import vm1.name -i id123 ``` ### 14. Get Provider Information #### Display a tree of providers used in the configuration files and their requirements ``` terraform providers ``` ### 15. Manage Your Workspaces #### Show the name of the current workspace ``` terraform workspace show ``` #### List your workspaces ``` terraform workspace list ``` #### Select a specified workspace ``` terraform workspace select ``` #### Create a new workspace with a specified name ``` terraform workspace new ``` #### Delete a specified workspace ``` terraform workspace delete ``` ### 16. View Your Outputs #### List all the outputs currently held in your state file - These are displayed by default at the end of a terraform apply, this command can be useful if you want to view them independently. ``` terraform output ``` #### List the outputs held in the specified state file - '-state' option is ignored when the remote state is used. ``` terraform output -state= ``` #### List the outputs held in your state file in JSON format to make them machine-readable ``` terraform output -json ``` #### List a specific output held in your state file ``` terraform output vm1_public_ip ``` ### 17. Release a Lock on Your Workspace #### Remove the lock with the specified lock ID from your workspace - Useful when a lock has become ‘stuck’, usually after an incomplete Terraform run. ``` terraform force-unlock ``` ### 18. Log In and Out to a Remote Host (Terraform Cloud) #### Grab an API token for Terraform cloud (app.terraform.io) using your browser ``` terraform login ``` #### Log in to a specified host ``` terraform login ``` #### Remove the credentials that are stored locally after logging in, by default for Terraform Cloud (app.terraform.io) ``` terraform logout ``` #### Remove the credentials that are stored locally after logging in for the specified hostname ``` terraform logout ``` ### 19. Produce a Dependency Diagram #### Produce a graph in DOT language showing the dependencies between objects in the state file - This can then be rendered by a program called Graphwiz (amongst others). ``` terraform graph ``` #### Produce a dependency graph using a specified plan file (generated using terraform plan -out=tfplan) ``` terraform graph -plan=tfplan ``` #### Specify the type of graph to output, either plan, plan-refresh-only, plan-destroy, or apply ``` terraform graph -type=plan ``` #### You can see if there are any dependency cycles between the resources ``` terraform graph -draw-cycles ``` ### 20. Test Your Expressions #### Allow testing and exploration of expressions on the interactive console using the command line ``` terraform console ``` ## Reference - https://spacelift.io/blog/terraform-commands-cheat-sheet ================================================ FILE: labs/backend-remote-state/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" backend "s3" { bucket = "terraform-state" key = "key/terraform.tfstate" region = "eu-central-1" } } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } ================================================ FILE: labs/basic-resource-ec2-ubuntu/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } ================================================ FILE: labs/data-sources/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_instance" "instance" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" tags = { Name = "Basic Instance" } } # with data source, new resource is not created. # data source provides to fetch (read) or retrieve the data from AWS # filter/select the existed instances # depends_on if aws_instance.instance is created data "aws_instance" "data_instance" { filter { name = "tag:Name" values = ["Basic Instance"] } depends_on = [ aws_instance.instance ] } output "instance_info" { value = data.aws_instance.data_instance } output "instance_public_ip" { value = data.aws_instance.data_instance.public_ip } ================================================ FILE: labs/dynamic-blocks/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } locals { ingress_rules = [{ port = 22 description = "Ingress rules for port SSH" }, { port = 80 description = "Ingress rules for port HTTP" }, { port = 443 description = "Ingress rules for port HTTPS" }] } resource "aws_security_group" "main" { name = "resource_with_dynamic_block" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # todo: update it with data.aws_vpc.main.id dynamic "ingress" { for_each = local.ingress_rules content { description = ingress.value.description from_port = ingress.value.port to_port = ingress.value.port protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "AWS security group dynamic block" } } resource "aws_instance" "ubuntu2204" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.main.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Ubuntu 22.04" } } output "instance_ubuntu2204_public_ip" { value = "${aws_instance.ubuntu2204.public_ip}" } ================================================ FILE: labs/iamuser-metaargs-count-for-foreach-map/count/main.tf ================================================ # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment # IAM users - roles - permissions # User -> User Group -> Policy (Permission) # AWS Services -> Roles -> Policy (Permission) terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # User - User Group Attachment (With Index Count) resource "aws_iam_user_group_membership" "user1_group_attach" { user = aws_iam_user.user_example[0].name groups = [ aws_iam_group.admin_group.name, aws_iam_group.dev_group.name, ] } resource "aws_iam_user_group_membership" "user2_group_attach" { user = aws_iam_user.user_example[1].id groups = [ aws_iam_group.admin_group.name ] } resource "aws_iam_user_group_membership" "user3_group_attach" { user = aws_iam_user.user_example[2].name groups = [ aws_iam_group.dev_group.name ] } ##################################################### # User Group Definition resource "aws_iam_group" "admin_group" { name = "admin_group" } resource "aws_iam_group" "dev_group" { name = "dev_group" } ##################################################### # Policy Definition, Policy-Group Attachment data "aws_iam_policy_document" "admin_policy" { statement { effect = "Allow" actions = ["*"] resources = ["*"] } } resource "aws_iam_policy" "admin_policy" { name = "admin-policy" description = "Admin policy" policy = data.aws_iam_policy_document.admin_policy.json } data "aws_iam_policy_document" "ec2_policy" { statement { effect = "Allow" actions = ["ec2:Describe*"] resources = ["*"] } } resource "aws_iam_policy" "ec2_policy" { name = "ec2-policy" description = "EC2 policy" policy = data.aws_iam_policy_document.ec2_policy.json } ##################################################### # Policy Attachment to the Admin, Dev Group resource "aws_iam_group_policy_attachment" "admin_group_admin_policy_attach" { group = aws_iam_group.admin_group.name policy_arn = aws_iam_policy.admin_policy.arn } resource "aws_iam_group_policy_attachment" "dev_group_ec2_policy_attach" { group = aws_iam_group.dev_group.name policy_arn = aws_iam_policy.ec2_policy.arn } ##################################################### # Username Definition # With Count resource "aws_iam_user" "user_example" { count = length(var.user_names) name = var.user_names[count.index] } # count, use list variable "user_names" { description = "IAM usernames" type = list(string) default = ["username1_admin_dev", "username2_admin", "username3_dev_ec2"] } ##################################################### # With for loop output "print_the_names" { value = [for name in var.user_names : name] } ================================================ FILE: labs/iamuser-metaargs-count-for-foreach-map/for_each/main.tf ================================================ # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment # IAM users - roles - permissions # User -> User Group -> Policy (Permission) terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # User - User Group Attachment (With Index Count) resource "aws_iam_user_group_membership" "user1_group_attach" { user = aws_iam_user.user_example["username1_admin_dev"].name groups = [ aws_iam_group.admin_group.name, aws_iam_group.dev_group.name, ] } resource "aws_iam_user_group_membership" "user2_group_attach" { user = aws_iam_user.user_example["username2_admin"].id groups = [ aws_iam_group.admin_group.name ] } resource "aws_iam_user_group_membership" "user3_group_attach" { user = aws_iam_user.user_example["username3_dev_s3"].name groups = [ aws_iam_group.dev_group.name ] } ##################################################### # User Group Definition resource "aws_iam_group" "admin_group" { name = "admin_group" } resource "aws_iam_group" "dev_group" { name = "dev_group" } ##################################################### # Policy Definition, Policy-Group Attachment data "aws_iam_policy_document" "admin_policy" { statement { effect = "Allow" actions = ["*"] resources = ["*"] } } resource "aws_iam_policy" "admin_policy" { name = "admin-policy" description = "Admin policy" policy = data.aws_iam_policy_document.admin_policy.json } data "aws_iam_policy_document" "s3_policy" { statement { effect = "Allow" actions = ["s3:*"] resources = [ "arn:aws:s3:::mybucket", "arn:aws:s3:::mybucket/*" ] } } resource "aws_iam_policy" "s3_policy" { name = "s3-policy" description = "S3 policy" policy = data.aws_iam_policy_document.s3_policy.json } ##################################################### # Policy Attachment to the Admin, Dev Group resource "aws_iam_group_policy_attachment" "admin_group_admin_policy_attach" { group = aws_iam_group.admin_group.name policy_arn = aws_iam_policy.admin_policy.arn } resource "aws_iam_group_policy_attachment" "dev_group_s3_policy_attach" { group = aws_iam_group.dev_group.name policy_arn = aws_iam_policy.s3_policy.arn } ##################################################### # With for_each resource "aws_iam_user" "user_example" { for_each = var.user_names name = each.value } # for each, use set instead of list variable "user_names" { description = "IAM usernames" type = set(string) default = ["username1_admin_dev", "username2_admin", "username3_dev_s3"] } ##################################################### # With for loop output "print_the_names" { value = [for name in var.user_names : name] } ================================================ FILE: labs/iamuser-metaargs-count-for-foreach-map/map/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } ##################################################### # With for_each resource "aws_iam_user" "example" { for_each = var.user_names name = each.value } # With Map variable "user_names" { description = "map" type = map(string) default = { user1 = "username1" user2 = "username2" user3 = "username3" } } # with for loop on map output "user_with_roles" { value = [for name, role in var.user_names : "${name} is the ${role}"] } ================================================ FILE: labs/modules/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } module "webserver-1" { source = ".//module1" instance_type = "t2.nano" tag = "Webserver1 - Module1 - 20.04" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt } module "webserver-2" { source = ".//module2" instance_type = "t2.micro" tag = "Webserver2 - Module2 - 22.04" location = "eu-central-1" availability_zone = "eu-central-1a" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt } ================================================ FILE: labs/modules/module1/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { staging_env = "module1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "${local.staging_env}-vpc-tag" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = var.availability_zone tags = { Name = "${local.staging_env}-subnet-tag" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "${local.staging_env}-Internet Gateway" } } resource "aws_route_table" "rt" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Name = "${local.staging_env}- Public Subnet Route Table" } } resource "aws_route_table_association" "rta" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.rt.id } resource "aws_security_group" "ssg" { name = "module1_security_group" # name should be different on modules description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP Apache Server ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTPS Apache Server ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ec2" { ami = var.ami instance_type = var.instance_type subnet_id = aws_subnet.public.id associate_public_ip_address = true vpc_security_group_ids = [aws_security_group.ssg.id] user_data = <<-EOF #! /bin/bash sudo apt-get update sudo apt-get install -y apache2 sudo systemctl start apache2 sudo systemctl enable apache2 echo "

!! MODULE-1 !!: Deployed via Terraform from $(hostname -f)

" | sudo tee /var/www/html/index.html EOF tags = { Name = var.tag } } # output single values output "public_ip" { value = aws_instance.ec2.public_ip } ================================================ FILE: labs/modules/module1/variables.tf ================================================ variable "instance_type" { type = string description = "EC2 Instance Type" } variable "tag" { type = string description = "The tag for the EC2 instance" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "availability_zone" { type = string description = "The project availability zone" default = "eu-central-1c" } variable "ami" { type = string description = "The project region" } ================================================ FILE: labs/modules/module2/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { staging_env = "module2" } resource "aws_vpc" "my_vpc" { cidr_block = "10.5.0.0/16" enable_dns_hostnames = true tags = { Name = "${local.staging_env}-vpc-tag" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.5.0.0/24" availability_zone = var.availability_zone tags = { Name = "${local.staging_env}-subnet-tag" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "${local.staging_env}-Internet Gateway" } } resource "aws_route_table" "rt" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Name = "${local.staging_env}- Public Subnet Route Table" } } resource "aws_route_table_association" "rta" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.rt.id } resource "aws_security_group" "ssg" { name = "module2_security_group" # name should be different on modules description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP Apache Server ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTPS Apache Server ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ec2" { ami = var.ami instance_type = var.instance_type subnet_id = aws_subnet.public.id associate_public_ip_address = true vpc_security_group_ids = [aws_security_group.ssg.id] user_data = <<-EOF #! /bin/bash sudo apt-get update sudo apt-get install -y apache2 sudo systemctl start apache2 sudo systemctl enable apache2 echo "

** MODULE-2 **: Deployed via Terraform from $(hostname -f)

" | sudo tee /var/www/html/index.html EOF tags = { Name = var.tag } } # output single values output "public_ip" { value = aws_instance.ec2.public_ip } ================================================ FILE: labs/modules/module2/variables.tf ================================================ variable "instance_type" { type = string description = "EC2 Instance Type" } variable "tag" { type = string description = "The tag for the EC2 instance" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "availability_zone" { type = string description = "The project availability zone" default = "eu-central-1c" } variable "ami" { type = string description = "The project region" } ================================================ FILE: labs/provisioners-nullresources/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_security_group" "allow_ssh" { name = "allow_ssh_sg" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ubuntu2204" { ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt instance_type = "t2.nano" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Ubuntu 22.04" } provisioner "file" { source = "test-file.txt" destination = "/home/ubuntu/test-file.txt" } provisioner "file" { content = "I want to copy this string to the destination file => server.txt (using provisioner file content)" destination = "/home/ubuntu/server.txt" } provisioner "remote-exec" { inline = [ "touch hello.txt", "echo helloworld remote-exec provisioner >> hello.txt", ] } connection { type = "ssh" host = self.public_ip user = "ubuntu" private_key = file("testkey.pem") timeout = "4m" } } resource "null_resource" "example" { provisioner "local-exec" { command = "'This is test file for null resource local-exec' >> nullresource-generated.txt" interpreter = ["PowerShell", "-Command"] } } ================================================ FILE: labs/provisioners-nullresources/test-file.txt ================================================ this is test file transferring this file to remote instance with "provisioner file" ================================================ FILE: labs/template/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_iam_user" "newuser" { name = "New-User" # must only contain alphanumeric characters, hyphens, underscores, commas, periods, @ symbols, plus and equals signs } resource "aws_iam_access_key" "access_key" { user = aws_iam_user.newuser.name } resource "aws_iam_user_policy" "instanceManageUser_assume_role" { name = "EC2-S3-Lambda-DynamoDb-Policy" user = "${aws_iam_user.newuser.name}" policy = templatefile("${path.module}/policy.tftpl", { ec2_policies = [ "ec2:RunInstances", "ec2:StopInstances", "ec2:StartInstances", "ec2:TerminateInstances", "ec2:TerminateInstances", "ec2:Describe*", "ec2:CreateTags", "ec2:RequestSpotInstances" ], s3_policies = [ "s3:Get*", "s3:List*", "s3:Describe*", "s3-object-lambda:Get*", "s3-object-lambda:List*" ], lambda_policies = [ "lambda:Create*", "lambda:List*", "lambda:Delete*", "lambda:Get*" ], dynamodb_policies = [ "dynamodb:Describe*", "dynamodb:Update*", "dynamodb:Get*", "dynamodb:List*", "dynamodb:BatchGetItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:PartiQLSelect" ], }) } output "secret_key" { value = aws_iam_access_key.access_key.secret sensitive = true } output "access_key" { value = aws_iam_access_key.access_key.id } ================================================ FILE: labs/template/policy.tftpl ================================================ { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ${jsonencode(ec2_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(s3_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(lambda_policies)}, "Resource": "*" }, { "Effect": "Allow", "Action": ${jsonencode(dynamodb_policies)}, "Resource": "*" } ] } ================================================ FILE: labs/terraform-docker-without-cloud/main.tf ================================================ # windows, prerequisite: install docker on your system # details, usage: https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container terraform { required_providers { docker = { source = "kreuzwerker/docker" version = "~> 3.0.2" } } } provider "docker" { host = "npipe:////.//pipe//docker_engine" } resource "docker_image" "windows" { name = "mcr.microsoft.com/powershell:lts-windowsservercore-1809" keep_locally = true } # docker container run -p 80:8000 --name=tutorial -it mcr.microsoft.com/powershell:lts-windowsservercore-1809 powershell resource "docker_container" "windows" { image = docker_image.windows.image_id name = "tutorial" stdin_open = true # docker run -i tty = true # docker run -t entrypoint = ["powershell"] ports { internal = 80 external = 8000 } } # docker container ls -a # docker container exec -it tutorial powershell # ls, exit # terraform destroy -auto-approve ================================================ FILE: labs/variables-locals-output/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { staging_env = "staging" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "${local.staging_env}-vpc-tag" } } resource "aws_subnet" "my_subnet" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/16" availability_zone = var.availability_zone tags = { Name = "${local.staging_env}-subnet-tag" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "${local.staging_env}-Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "${local.staging_env}- Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.my_subnet.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_instance" "ec2_example" { ami = var.ami instance_type = var.instance_type subnet_id = aws_subnet.my_subnet.id associate_public_ip_address = true tags = { Name = var.tag } } # output single values output "public_ip" { value = aws_instance.ec2_example.public_ip } # output single values output "public_dns" { value = aws_instance.ec2_example.public_dns } # output multiple values output "instance_ips" { value = { public_ip = aws_instance.ec2_example.public_ip private_ip = aws_instance.ec2_example.private_ip } } # terraform init # terraform plan --var-file="terraform-dev.tfvars" # terraform apply --var-file="terraform-dev.tfvars" # terraform destroy --var-file="terraform-dev.tfvars" # terraform plan --var-file="terraform-prod.tfvars" # terraform apply --var-file="terraform-prod.tfvars" # terraform destroy --var-file="terraform-prod.tfvars" ================================================ FILE: labs/variables-locals-output/terraform-dev.tfvars ================================================ instance_type = "t2.nano" tag = "EC2 Instance for DEV" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt ================================================ FILE: labs/variables-locals-output/terraform-prod.tfvars ================================================ instance_type = "t2.micro" tag = "EC2 Instance for PROD" location = "eu-central-1" availability_zone = "eu-central-1c" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt ================================================ FILE: labs/variables-locals-output/variables.tf ================================================ variable "instance_type" { type = string description = "EC2 Instance Type" } variable "tag" { type = string description = "The tag for the EC2 instance" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "availability_zone" { type = string description = "The project availability zone" default = "eu-central-1c" } variable "ami" { type = string description = "The project region" } ================================================ FILE: labs/workspace/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = var.location } locals { tag = "${terraform.workspace} EC2" } resource "aws_instance" "instance" { ami = var.ami instance_type = var.instance_type tags = { Name = local.tag } } ================================================ FILE: labs/workspace/terraform-dev.tfvars ================================================ instance_type = "t2.nano" location = "eu-central-1" ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt ================================================ FILE: labs/workspace/terraform-prod.tfvars ================================================ instance_type = "t2.micro" location = "eu-central-1" ami = "ami-0d1ddd83282187d18" # Ubuntu 22.04 eu-central-1 Frankfurt ================================================ FILE: labs/workspace/variables.tf ================================================ variable "instance_type" { type = string description = "EC2 Instance Type" } variable "location" { type = string description = "The project region" default = "eu-central-1" } variable "ami" { type = string description = "The project region" } ================================================ FILE: samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/lambda_bootstrap/lambda/Dockerfile ================================================ FROM public.ecr.aws/lambda/python:3.8 COPY requirements.txt ${LAMBDA_TASK_ROOT} RUN pip3 install --no-cache-dir -r requirements.txt COPY aws-lambda-url.py ${LAMBDA_TASK_ROOT} CMD ["aws-lambda-url.lambda_handler"] ================================================ FILE: samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/lambda_bootstrap/lambda/aws-lambda-url.py ================================================ from bs4 import BeautifulSoup import requests import boto3 url="https://search.longhornrealty.com/idx/results/listings?pt=4&a_propStatus%5B%5D=Active&ccz=city&idxID=c007&per=25&srt=newest&city%5B%5D=22332&city%5B%5D=45916" page = requests.get(url) def lambda_handler(event, context): try: soup = BeautifulSoup(page.content, "html.parser") results = soup.find(id="idx-results-category-active") listings = results.find_all("article") for listing in listings: prices = listing.find( 'div', {'class': 'idx-listing-card__price'}).get_text() address_city = listing.find( 'span', {'class': 'idx-listing-card__address--City'}).get_text() print(prices, address_city) except Exception as e: print(e) ================================================ FILE: samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/lambda_bootstrap/lambda/docker-test.sh ================================================ #!bin/bash #aws sts get-caller-identity docker build . -t aws-lambda-url:0.0.1 docker run \ -p 9000:8080 \ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN \ -e AWS_REGION=$AWS_REGION \ aws-lambda-url:0.0.1 ================================================ FILE: samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/lambda_bootstrap/lambda/requirements.txt ================================================ beautifulsoup4 requests ================================================ FILE: samples/codecommit-codepipeline-codebuild-codedeploy-lambda-container/lambda_bootstrap/main.tf ================================================ data "aws_ecr_image" "lambda_image_latest" { repository_name = split("/", var.ecr_repo_url)[1] image_tag = "latest" } resource "aws_iam_role" "iam_for_lambda" { name = "${var.env_namespace}_lambda_role" assume_role_policy = <Deployed via Terraform from $(hostname -f)" | sudo tee /var/www/html/index.html EOF tags = { Name = "Ubuntu 20.04" } } resource "aws_instance" "win2019" { ami = "ami-02c2da541ae36c6fc" # Windows 2019 Server eu-central-1 Frankfurt instance_type = "t2.micro" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true tags = { Name = "Win 2019 Server" } } # Output the public IP addresses of the Ubuntu and Windows EC2 instances output "instance_ubuntu2004_public_ip" { value = "${aws_instance.ubuntu2004.public_ip}" } output "instance_win2019_public_ip" { value = "${aws_instance.win2019.public_ip}" } ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/1_vpc.tf ================================================ # Internet Access -> IGW -> Route Table -> Subnets terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public_subnet_a" { availability_zone = "eu-central-1a" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" tags = { Name = "Public Subnet A" } } resource "aws_subnet" "public_subnet_b" { availability_zone = "eu-central-1b" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.1.0/24" tags = { Name = "Public Subnet B" } } resource "aws_subnet" "public_subnet_c" { availability_zone = "eu-central-1c" vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.2.0/24" tags = { Name = "Public Subnet C" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "route_table" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "route_table_association1" { subnet_id = aws_subnet.public_subnet_a.id route_table_id = aws_route_table.route_table.id } resource "aws_route_table_association" "route_table_association2" { subnet_id = aws_subnet.public_subnet_b.id route_table_id = aws_route_table.route_table.id } resource "aws_route_table_association" "route_table_association3" { subnet_id = aws_subnet.public_subnet_c.id route_table_id = aws_route_table.route_table.id } ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/2_ecs.tf ================================================ # Getting data existed ECR data "aws_ecr_repository" "flask_app" { name = "flask-app" } # Creating ECS Cluster resource "aws_ecs_cluster" "my_cluster" { name = "my-cluster" # Naming the cluster } # Creating ECS Task resource "aws_ecs_task_definition" "flask_app_task" { family = "flask-app-task" container_definitions = < IGW -> LB Security Groups -> Application Load Balancer (Listener 80) -> Target Groups -> ECS Service -> ECS SG -> Tasks on each subnets # Creating Load Balancer (LB) resource "aws_alb" "application_load_balancer" { name = "test-lb-tf" # Naming our load balancer load_balancer_type = "application" subnets = [ "${aws_subnet.public_subnet_a.id}", "${aws_subnet.public_subnet_b.id}", "${aws_subnet.public_subnet_c.id}" ] # Referencing the security group security_groups = ["${aws_security_group.load_balancer_security_group.id}"] } # Creating a security group for LB resource "aws_security_group" "load_balancer_security_group" { vpc_id = aws_vpc.my_vpc.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] # Allowing traffic in from all sources } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # Creating LB Target Group resource "aws_lb_target_group" "target_group" { name = "target-group" port = 80 protocol = "HTTP" target_type = "ip" vpc_id = "${aws_vpc.my_vpc.id}" } # Creating LB Listener resource "aws_lb_listener" "listener" { load_balancer_arn = "${aws_alb.application_load_balancer.arn}" # Referencing our load balancer port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = "${aws_lb_target_group.target_group.arn}" # Referencing our target group } } ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/4_ecs_service.tf ================================================ # Creating ECS Service resource "aws_ecs_service" "my_first_service" { name = "my-first-service" # Naming our first service cluster = "${aws_ecs_cluster.my_cluster.id}" # Referencing our created Cluster task_definition = "${aws_ecs_task_definition.flask_app_task.arn}" # Referencing the task our service will spin up launch_type = "FARGATE" desired_count = 3 # Setting the number of containers to 3 load_balancer { target_group_arn = "${aws_lb_target_group.target_group.arn}" # Referencing our target group container_name = "${aws_ecs_task_definition.flask_app_task.family}" container_port = 5000 # Specifying the container port } network_configuration { subnets = ["${aws_subnet.public_subnet_a.id}", "${aws_subnet.public_subnet_b.id}", "${aws_subnet.public_subnet_c.id}"] assign_public_ip = true # Providing our containers with public IPs security_groups = ["${aws_security_group.service_security_group.id}"] # Setting the security group } } # Creating SG for ECS Container Service, referencing the load balancer security group resource "aws_security_group" "service_security_group" { vpc_id = aws_vpc.my_vpc.id ingress { from_port = 0 to_port = 0 protocol = "-1" # Only allowing traffic in from the load balancer security group security_groups = ["${aws_security_group.load_balancer_security_group.id}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } #Log the load balancer app URL output "app_url" { value = aws_alb.application_load_balancer.dns_name } ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/ecr/0_ecr.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Creating Elastic Container Repository for application resource "aws_ecr_repository" "flask_app" { name = "flask-app" } # aws ecr get-login-password --region REGION | docker login --username AWS --password-stdin ID.dkr.ecr.REGION.amazonaws.com # docker build -t flask-app . # docker tag flask-app:latest ID.dkr.REGION.amazonaws.com/flask-app:latest # docker push ID.dkr.REGION.amazonaws.com/flask-app:latest ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/Dockerfile ================================================ FROM python:3.8-slim-buster WORKDIR /app COPY requirements.txt requirements.txt RUN pip3 install -r requirements.txt COPY app . ENV FLASK_APP=app ENV FLASK_ENV=development EXPOSE 5000 RUN python init_db.py CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0","--port","5000"] ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/README.md ================================================ ## Flask-App Docker Image - Dockerfile is created for this sample. - App code is open source and taken: - https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3 - https://github.com/do-community/flask_blog - To build Linux Container and run on local: ``` docker build -t flask-app . docker container run -p 5000:5000 -d flask-app ``` ![image](https://user-images.githubusercontent.com/10358317/232225583-253f20dc-4d95-43b3-a4a7-d156f2d0c886.png) - If you are using WSL2 on Windows, use sensible browser on WSL2 ``` sensible-browser http://localhost:5000/ ``` ![image](https://user-images.githubusercontent.com/10358317/232225726-d02927fe-9d64-4fba-b279-ff7c0ec7dbc1.png) - For app, Thank you Digital Ocean! ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/app.py ================================================ import sqlite3 from flask import Flask, render_template, request, url_for, flash, redirect from werkzeug.exceptions import abort def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn def get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() if post is None: abort(404) return post app = Flask(__name__) app.config['SECRET_KEY'] = '99' @app.route('/') def index(): conn = get_db_connection() posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts) @app.route('/') def post(post_id): post = get_post(post_id) return render_template('post.html', post=post) @app.route('/create', methods=('GET', 'POST')) def create(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] if not title: flash('Title is required!') else: conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)', (title, content)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('create.html') @app.route('//edit', methods=('GET', 'POST')) def edit(id): post = get_post(id) if request.method == 'POST': title = request.form['title'] content = request.form['content'] if not title: flash('Title is required!') else: conn = get_db_connection() conn.execute('UPDATE posts SET title = ?, content = ?' ' WHERE id = ?', (title, content, id)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('edit.html', post=post) @app.route('//delete', methods=('POST',)) def delete(id): post = get_post(id) conn = get_db_connection() conn.execute('DELETE FROM posts WHERE id = ?', (id,)) conn.commit() conn.close() flash('"{}" was successfully deleted!'.format(post['title'])) return redirect(url_for('index')) ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/hello.py ================================================ from flask import Flask, request, escape app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!' @app.route('/greet') def greet(): name = request.args['name'] return '''

Hi {}

'''.format(escape(name)) ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/init_db.py ================================================ import sqlite3 connection = sqlite3.connect('database.db') with open('schema.sql') as f: connection.executescript(f.read()) cur = connection.cursor() cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)", ('First Post', 'Content for the first post') ) cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)", ('Second Post', 'Content for the second post') ) connection.commit() connection.close() ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/schema.sql ================================================ DROP TABLE IF EXISTS posts; CREATE TABLE posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, title TEXT NOT NULL, content TEXT NOT NULL ); ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/static/css/style.css ================================================ h1 { border: 2px #eee solid; color: brown; text-align: center; padding: 10px; } ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/templates/base.html ================================================ {% block title %} {% endblock %}
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} {% block content %} {% endblock %}
================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/templates/create.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Create a New Post {% endblock %}

{% endblock %} ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/templates/edit.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Edit "{{ post['title'] }}" {% endblock %}


{% endblock %} ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/templates/index.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Welcome to FlaskBlog {% endblock %}

{% for post in posts %}

{{ post['title'] }}

{{ post['created'] }} Edit
{% endfor %} {% endblock %} ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/app/templates/post.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} {{ post['title'] }} {% endblock %}

{{ post['created'] }}

{{ post['content'] }}

{% endblock %} ================================================ FILE: samples/ecr-ecs-elb-vpc-ecsservice-container/flask-app/requirements.txt ================================================ flask db-sqlite3 Werkzeug ================================================ FILE: samples/eks-managed-node-blueprint/README.md ================================================ ## Modules - Terraform EKS Module: - https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest - https://github.com/terraform-aws-modules/terraform-aws-eks - Kubernetes Addons: - https://github.com/aws-ia/terraform-aws-eks-blueprints/modules/kubernetes-addons - Terraform VPC Module: - https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest - https://github.com/terraform-aws-modules/terraform-aws-vpc ================================================ FILE: samples/eks-managed-node-blueprint/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = ">= 4.47" } kubernetes = { source = "hashicorp/kubernetes" version = ">= 2.10" } helm = { source = "hashicorp/helm" version = ">= 2.4.1" } } required_version = ">= 1.2.0" } provider "aws" { region = local.region } provider "kubernetes" { host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) token = data.aws_eks_cluster_auth.this.token } provider "helm" { kubernetes { host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) token = data.aws_eks_cluster_auth.this.token } } data "aws_eks_cluster_auth" "this" { name = module.eks.cluster_name } data "aws_availability_zones" "available" {} locals { name = basename(path.cwd) region = "eu-central-1" cluster_version = "1.24" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) tags = { Blueprint = local.name GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" } } ################################################################################ # Cluster ################################################################################ #tfsec:ignore:aws-eks-enable-control-plane-logging module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 19.12" cluster_name = local.name cluster_version = local.cluster_version cluster_endpoint_public_access = true # EKS Addons cluster_addons = { coredns = {} kube-proxy = {} vpc-cni = {} } vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets eks_managed_node_groups = { initial = { instance_types = ["m5.large"] min_size = 1 max_size = 3 desired_size = 2 } } tags = local.tags } ################################################################################ # Kubernetes Addons ################################################################################ module "eks_blueprints_kubernetes_addons" { source = "github.com/aws-ia/terraform-aws-eks-blueprints/modules/kubernetes-addons" eks_cluster_id = module.eks.cluster_name eks_cluster_endpoint = module.eks.cluster_endpoint eks_oidc_provider = module.eks.oidc_provider eks_cluster_version = module.eks.cluster_version # Add-ons enable_metrics_server = true enable_cluster_autoscaler = true eks_worker_security_group_id = module.eks.cluster_security_group_id tags = local.tags } ################################################################################ # Supporting Resources ################################################################################ module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 4.0" name = local.name cidr = local.vpc_cidr azs = local.azs private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] enable_nat_gateway = true single_nat_gateway = true public_subnet_tags = { "kubernetes.io/role/elb" = 1 } private_subnet_tags = { "kubernetes.io/role/internal-elb" = 1 } tags = local.tags } output "configure_kubectl" { description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" value = "aws eks --region ${local.region} update-kubeconfig --name ${module.eks.cluster_name}" } ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/docker-compose.yml ================================================ # Gitlab Server on WSL2 (Linux) version: '3.6' services: web: image: 'gitlab/gitlab-ee:latest' restart: always hostname: 'gitlab.example.com' environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://gitlab.example.com' #external_url 'https://gitlab.example.com' ports: - '150:80' - '443:443' - '22:22' volumes: - '/home/omer/gitlab-tmp/config:/etc/gitlab' - '/home/omer/gitlab-tmp/logs:/var/log/gitlab' - '/home/omer/gitlab-tmp/data:/var/opt/gitlab' shm_size: '256m' ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/main.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_vpc" "my_vpc" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "My VPC" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.my_vpc.id cidr_block = "10.0.0.0/24" availability_zone = "eu-central-1c" tags = { Name = "Public Subnet" } } resource "aws_internet_gateway" "my_vpc_igw" { vpc_id = aws_vpc.my_vpc.id tags = { Name = "My VPC - Internet Gateway" } } resource "aws_route_table" "my_vpc_eu_central_1c_public" { vpc_id = aws_vpc.my_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_vpc_igw.id } tags = { Name = "Public Subnet Route Table" } } resource "aws_route_table_association" "my_vpc_eu_central_1c_public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.my_vpc_eu_central_1c_public.id } resource "aws_security_group" "allow_ssh" { name = "allow_ssh_sg" description = "Allow SSH inbound connections" vpc_id = aws_vpc.my_vpc.id # for SSH ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTP ingress { from_port = 150 to_port = 150 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for HTTPS ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for RDP ingress { from_port = 3389 to_port = 3389 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # for ping ingress { from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["10.0.0.0/16"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_ssh_sg" } } resource "aws_instance" "ubuntu2004" { ami = "ami-0e067cc8a2b58de59" # Ubuntu 20.04 eu-central-1 Frankfurt instance_type = "t2.micro" key_name = "testkey" vpc_security_group_ids = [aws_security_group.allow_ssh.id] subnet_id = aws_subnet.public.id associate_public_ip_address = true user_data = <<-EOF #! /bin/bash sudo apt-get update sudo apt-get install ca-certificates curl gnupg -y sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y sudo docker run hello-world curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash sudo apt-get install gitlab-runner EOF tags = { Name = "Ubuntu 20.04" } } output "instance_ubuntu2004_public_ip" { value = "${aws_instance.ubuntu2004.public_ip}" } ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/docker-windows/Dockerfile ================================================ # escape=` FROM mcr.microsoft.com/windows/servercore:1809 # Restore the default Windows shell for correct batch processing. SHELL ["cmd", "/S", "/C"] # install choco (win package manager like apt-get) RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" # install python3.7 RUN choco install -y python --version=3.7.2 ` && set PATH=%PATH%;C:\Python37\ RUN choco install pwsh --version=7.3.3 -y CMD ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass"] ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/gitlab-ci.yml ================================================ .image-linux: &image-linux image: 'python:3.7' .image-windows: &image-windows image: 'omerbsezer/python:3.7-windowsservercore-1809' stages: - build_debug - run_test workflow: rules: - if: $CI_COMMIT_BRANCH == "main" .build_linux: &build_linux <<: *image-linux script: - python --version - pip install -r requirements.txt - pwd - pylint --version - pylint -d C0301 src/*.py tags: - ec2-shared .build_windows: &build_windows <<: *image-windows script: - whoami - python --version - pip install -r requirements.txt - pwd - pylint --version - cd src/; pylint -d C0301 main.py tags: - ec2-shared-windows build_linux_debug: <<: *build_linux stage: build_debug build_windows_debug: extends: .build_windows stage: build_debug run_linux_test: <<: *image-linux stage: run_test needs: - build_linux_debug script: - python --version - pip install -r requirements.txt - cd test/;pytest -v coverage: '/lines: \d+\.\d+/' tags: - ec2-shared run_windows_test: <<: *image-windows stage: run_test needs: - build_windows_debug script: - python --version - pip install -r requirements.txt - cd test/;pytest -v tags: - ec2-shared-windows ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/requirements.txt ================================================ pylint==2.4.4 pytest==5.3.5 ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/src/__init__.py ================================================ ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/src/main.py ================================================ ''' This file is example ''' def func(number: int) -> int: ''' This function increases ''' return number + 1 if __name__ == '__main__': print('Running Python File...') A = 3 print(f'{A} + 1 =', func(A)) ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/test/__init__.py ================================================ ================================================ FILE: samples/gitlabserver-on-premise-runner-on-EC2/test-gitlab-runner/test/test_main.py ================================================ import pytest from src.main import func def test_answer(): assert func(3) == 4 ================================================ FILE: samples/lambda-container-apigateway-flaskapp/1_lambda.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Create IAM Role for lambda resource "aws_iam_role" "lambda_role" { name = "aws_lambda_role" assume_role_policy = <') def post(post_id): post = get_post(post_id) return render_template('post.html', post=post) @app.route('/create', methods=('GET', 'POST')) def create(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] if not title: flash('Title is required!') else: conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)', (title, content)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('create.html') @app.route('//edit', methods=('GET', 'POST')) def edit(id): post = get_post(id) if request.method == 'POST': title = request.form['title'] content = request.form['content'] if not title: flash('Title is required!') else: conn = get_db_connection() conn.execute('UPDATE posts SET title = ?, content = ?' ' WHERE id = ?', (title, content, id)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('edit.html', post=post) @app.route('//delete', methods=('POST',)) def delete(id): post = get_post(id) conn = get_db_connection() conn.execute('DELETE FROM posts WHERE id = ?', (id,)) conn.commit() conn.close() flash('"{}" was successfully deleted!'.format(post['title'])) return redirect(url_for('index')) def handler(event, context): return serverless_wsgi.handle_request(app, event, context) ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/hello.py ================================================ from flask import Flask, request, escape app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!' @app.route('/greet') def greet(): name = request.args['name'] return '''

Hi {}

'''.format(escape(name)) ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/init_db.py ================================================ import sqlite3 connection = sqlite3.connect('database.db') with open('schema.sql') as f: connection.executescript(f.read()) cur = connection.cursor() cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)", ('First Post', 'Content for the first post') ) cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)", ('Second Post', 'Content for the second post') ) connection.commit() connection.close() ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/schema.sql ================================================ DROP TABLE IF EXISTS posts; CREATE TABLE posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, title TEXT NOT NULL, content TEXT NOT NULL ); ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/static/css/style.css ================================================ h1 { border: 2px #eee solid; color: brown; text-align: center; padding: 10px; } ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/templates/base.html ================================================ {% block title %} {% endblock %}
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} {% block content %} {% endblock %}
================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/templates/create.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Create a New Post {% endblock %}

{% endblock %} ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/templates/edit.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Edit "{{ post['title'] }}" {% endblock %}


{% endblock %} ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/templates/index.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} Welcome to FlaskBlog {% endblock %}

{% for post in posts %}

{{ post['title'] }}

{{ post['created'] }} Edit
{% endfor %} {% endblock %} ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/app/templates/post.html ================================================ {% extends 'base.html' %} {% block content %}

{% block title %} {{ post['title'] }} {% endblock %}

{{ post['created'] }}

{{ post['content'] }}

{% endblock %} ================================================ FILE: samples/lambda-container-apigateway-flaskapp/flask-app-serverless/requirements.txt ================================================ flask db-sqlite3 Werkzeug serverless-wsgi>=2.0.2 ushlex ================================================ FILE: samples/lambda-role-policy-apigateway-python/api-gateway.tf ================================================ # Create API Gateway with Rest API type resource "aws_api_gateway_rest_api" "example" { name = "Serverless" description = "Serverless Application using Terraform" } # Defines a resource in the API Gateway that will capture any request path. The path_part = "{proxy+}" allows API Gateway to match all requests that have any path pattern, enabling dynamic routing. resource "aws_api_gateway_resource" "proxy" { rest_api_id = aws_api_gateway_rest_api.example.id parent_id = aws_api_gateway_rest_api.example.root_resource_id path_part = "{proxy+}" # with proxy, this resource will match any request path } # Configures to allow any HTTP method (GET, POST, DELETE, etc.) and does not require any specific authorization. It's set for the previously defined proxy resource. resource "aws_api_gateway_method" "proxy" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_resource.proxy.id http_method = "ANY" # with ANY, it allows any request method to be used, all incoming requests will match this resource authorization = "NONE" } # API Gateway - Lambda Connection # The AWS_PROXY type means API Gateway will directly pass the request details (method, headers, body, path parameters, etc.) to the Lambda function resource "aws_api_gateway_integration" "lambda" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_method.proxy.resource_id http_method = aws_api_gateway_method.proxy.http_method integration_http_method = "POST" type = "AWS_PROXY" # With AWS_PROXY, it causes API gateway to call into the API of another AWS service uri = aws_lambda_function.lambda_function.invoke_arn } # The proxy resource cannot match an empty path at the root of the API. API GW doesn't route requests to the root (/) path using the proxy, so a separate method and integration for the root resource are required. # To handle that, a similar configuration must be applied to the root resource that is built in to the REST API object. # This ensures that requests to the root (e.g., https://api.example.com/) are also forwarded to the Lambda function. resource "aws_api_gateway_method" "proxy_root" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_rest_api.example.root_resource_id http_method = "ANY" authorization = "NONE" } resource "aws_api_gateway_integration" "lambda_root" { rest_api_id = aws_api_gateway_rest_api.example.id resource_id = aws_api_gateway_method.proxy_root.resource_id http_method = aws_api_gateway_method.proxy_root.http_method integration_http_method = "POST" type = "AWS_PROXY" # With AWS_PROXY, it causes API gateway to call into the API of another AWS service uri = aws_lambda_function.lambda_function.invoke_arn } # Deploy API Gateway # Deploys the API to the specified stage (test stage). The depends_on ensures that the API is not deployed until both the Lambda integrations (for proxy and root) are complete. resource "aws_api_gateway_deployment" "example" { depends_on = [ aws_api_gateway_integration.lambda, aws_api_gateway_integration.lambda_root, ] rest_api_id = aws_api_gateway_rest_api.example.id stage_name = "test" } # Output to the URL output "base_url" { value = aws_api_gateway_deployment.example.invoke_url } ================================================ FILE: samples/lambda-role-policy-apigateway-python/code/main.py ================================================ def lambda_handler(event, context): content = """

Hello Website running on Lambda! Deployed via Terraform

""" response ={ "statusCode": 200, "body": content, "headers": {"Content-Type": "text/html",}, } return response ================================================ FILE: samples/lambda-role-policy-apigateway-python/lambda.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } # Create IAM Role for lambda # The assume_role_policy defines who or what can assume this role. In this case, it allows lambda to assume the role, which is necessary for Lambda functions to execute with this role's permissions. # The policy allows STS (Security Token Service) to manage temporary credentials for the Lambda service. resource "aws_iam_role" "lambda_role" { name = "aws_lambda_role" assume_role_policy = <\")\n", "\n", "assert(len(project_name) <= 15 ) # the project name should not have more than 15 chars" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(bucket)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Load Raw Data to S3" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Load raw data from the public S3 bucket to your own S3 bucket." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws s3 cp s3://sagemaker-sample-files/datasets/tabular/synthetic/churn.txt s3://{bucket}/sagemaker/DEMO-xgboost-churn/data/RawData.csv " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare script to be used by preprocessing job and model evaluation" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Create preprocessing script. This script will be used by SageMaker process job instance to preocess raw data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%writefile preprocess.py\n", "\n", "\"\"\"Preprocess the customer churn dataset.\"\"\"\n", "\n", "import argparse\n", "import logging\n", "import pathlib\n", "\n", "import boto3\n", "import numpy as np\n", "import pandas as pd\n", "\n", "logger = logging.getLogger()\n", "logger.setLevel(logging.INFO)\n", "logger.addHandler(logging.StreamHandler())\n", "\n", "if __name__ == \"__main__\":\n", " logger.info(\"Starting preprocessing.\")\n", " parser = argparse.ArgumentParser()\n", " parser.add_argument(\"--input-data\", type=str, required=True)\n", " args = parser.parse_args()\n", "\n", " base_dir = \"/opt/ml/processing\"\n", " pathlib.Path(f\"{base_dir}/data\").mkdir(parents=True, exist_ok=True)\n", " input_data = args.input_data\n", " print(input_data)\n", " bucket = input_data.split(\"/\")[2]\n", " key = \"/\".join(input_data.split(\"/\")[3:])\n", "\n", " logger.info(\"Downloading data from bucket: %s, key: %s\", bucket, key)\n", " fn = f\"{base_dir}/data/raw-data.csv\"\n", " s3 = boto3.resource(\"s3\")\n", " s3.Bucket(bucket).download_file(key, fn)\n", "\n", " logger.info(\"Reading downloaded data.\")\n", "\n", " # read in csv\n", " df = pd.read_csv(fn)\n", "\n", " # drop the \"Phone\" feature column\n", " df = df.drop([\"Phone\"], axis=1)\n", "\n", " # Change the data type of \"Area Code\"\n", " df[\"Area Code\"] = df[\"Area Code\"].astype(object)\n", "\n", " # Drop several other columns\n", " df = df.drop([\"Day Charge\", \"Eve Charge\", \"Night Charge\", \"Intl Charge\"], axis=1)\n", "\n", " # Convert categorical variables into dummy/indicator variables.\n", " model_data = pd.get_dummies(df)\n", "\n", " # Create one binary classification target column\n", " model_data = pd.concat(\n", " [\n", " model_data[\"Churn?_True.\"],\n", " model_data.drop([\"Churn?_False.\", \"Churn?_True.\"], axis=1),\n", " ],\n", " axis=1,\n", " )\n", "\n", " # Split the data\n", " train_data, validation_data, test_data = np.split(\n", " model_data.sample(frac=1, random_state=1729),\n", " [int(0.7 * len(model_data)), int(0.9 * len(model_data))],\n", " )\n", "\n", " pd.DataFrame(train_data).to_csv(\n", " f\"{base_dir}/train/train.csv\", header=False, index=False\n", " )\n", " pd.DataFrame(validation_data).to_csv(\n", " f\"{base_dir}/validation/validation.csv\", header=False, index=False\n", " )\n", " pd.DataFrame(test_data).to_csv(\n", " f\"{base_dir}/test/test.csv\", header=False, index=False\n", " )\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Prepare evaluation script" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%writefile evaluate.py\n", "\n", "\"\"\"Evaluation script for measuring model accuracy.\"\"\"\n", "\n", "import json\n", "import os\n", "import tarfile\n", "import logging\n", "import pickle\n", "\n", "import pandas as pd\n", "import xgboost\n", "\n", "logger = logging.getLogger()\n", "logger.setLevel(logging.INFO)\n", "logger.addHandler(logging.StreamHandler())\n", "\n", "# May need to import additional metrics depending on what you are measuring.\n", "# See https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html\n", "from sklearn.metrics import classification_report, roc_auc_score, accuracy_score\n", "\n", "\n", "if __name__ == \"__main__\":\n", " model_path = \"/opt/ml/processing/model/model.tar.gz\"\n", " with tarfile.open(model_path) as tar:\n", " tar.extractall(path=\"..\")\n", "\n", " logger.debug(\"Loading xgboost model.\")\n", " model = pickle.load(open(\"xgboost-model\", \"rb\"))\n", "\n", " print(\"Loading test input data\")\n", " test_path = \"/opt/ml/processing/test/test.csv\"\n", " df = pd.read_csv(test_path, header=None)\n", "\n", " logger.debug(\"Reading test data.\")\n", " y_test = df.iloc[:, 0].to_numpy()\n", " df.drop(df.columns[0], axis=1, inplace=True)\n", " X_test = xgboost.DMatrix(df.values)\n", "\n", " logger.info(\"Performing predictions against test data.\")\n", " predictions = model.predict(X_test)\n", "\n", " print(\"Creating classification evaluation report\")\n", " acc = accuracy_score(y_test, predictions.round())\n", " auc = roc_auc_score(y_test, predictions.round())\n", "\n", " # The metrics reported can change based on the model used, but it must be a specific name per (https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html)\n", " report_dict = {\n", " \"binary_classification_metrics\": {\n", " \"accuracy\": {\n", " \"value\": acc,\n", " \"standard_deviation\" : \"NaN\"\n", " },\n", " \"auc\" : {\n", " \"value\" : auc,\n", " \"standard_deviation\": \"NaN\"\n", " },\n", " },\n", " }\n", " evaluation_output_path = '/opt/ml/processing/evaluation/evaluation.json'\n", " with open(evaluation_output_path, 'w') as f:\n", " f.write(json.dumps(report_dict))\n", " \n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Define Model Building Pipeline" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ " Pipeline input parameters are listed below. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "processing_instance_count = ParameterInteger(name=\"ProcessingInstanceCount\", default_value=1)\n", "processing_instance_type = ParameterString(\n", " name=\"ProcessingInstanceType\", default_value=\"ml.m5.xlarge\"\n", " )\n", "\n", "training_instance_type = ParameterString(\n", " name=\"TrainingInstanceType\", default_value=\"ml.m5.xlarge\"\n", " )\n", "\n", "model_approval_status = ParameterString(\n", " name=\"ModelApprovalStatus\",\n", " default_value=\"Approved\", # ModelApprovalStatus can be set to a default of \"Approved\" if you don't want manual approval.\n", " )\n", "\n", "input_data = ParameterString(\n", " name=\"InputDataUrl\",\n", " default_value=f\"s3://{sagemaker_session.default_bucket()}/sagemaker/DEMO-xgboost-churn/data/RawData.csv\", # Change this to point to the s3 location of your raw input data.\n", " )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_package_group_name=\"CustomerChurnPackageGroup\" # Choose any name\n", "#pipeline_name=\"CustomerChurnDemoPipe2\" # \n", "base_job_prefix=\"CustomerChurn\" # Choose any name" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from time import strftime,gmtime\n", "pipeline_name = 'CustomerChurn-Pipe-' + strftime(\"%M%S\", gmtime())" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The below defines a SageMaker model buidling pipeline using workflow. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Processing step for feature engineering\n", "sklearn_processor = SKLearnProcessor(\n", " framework_version=\"0.23-1\",\n", " instance_type=processing_instance_type,\n", " instance_count=processing_instance_count,\n", " base_job_name=f\"{base_job_prefix}/sklearn-CustomerChurn-preprocess\", # choose any name\n", " sagemaker_session=sagemaker_session,\n", " role=role,\n", ")\n", "step_process = ProcessingStep(\n", " name=\"CustomerChurnProcess\", # choose any name\n", " processor=sklearn_processor,\n", " outputs=[\n", " ProcessingOutput(output_name=\"train\", source=\"/opt/ml/processing/train\"),\n", " ProcessingOutput(output_name=\"validation\", source=\"/opt/ml/processing/validation\"),\n", " ProcessingOutput(output_name=\"test\", source=\"/opt/ml/processing/test\"),\n", " ],\n", " code=(\"preprocess.py\"),\n", " job_arguments=[\"--input-data\", input_data],\n", ")\n", "# Training step for generating model artifacts\n", "model_path = f\"s3://{sagemaker_session.default_bucket()}/{base_job_prefix}/CustomerChurnTrain\"\n", "image_uri = sagemaker.image_uris.retrieve(\n", " framework=\"xgboost\", # we are using the Sagemaker built in xgboost algorithm\n", " region=region,\n", " version=\"1.0-1\",\n", " py_version=\"py3\",\n", " instance_type=training_instance_type,\n", ")\n", "xgb_train = Estimator(\n", " image_uri=image_uri,\n", " instance_type=training_instance_type,\n", " instance_count=1,\n", " output_path=model_path,\n", " base_job_name=f\"{base_job_prefix}/CustomerChurn-train\",\n", " sagemaker_session=sagemaker_session,\n", " role=role,\n", ")\n", "xgb_train.set_hyperparameters(\n", " objective=\"binary:logistic\",\n", " num_round=50,\n", " max_depth=5,\n", " eta=0.2,\n", " gamma=4,\n", " min_child_weight=6,\n", " subsample=0.7,\n", " silent=0,\n", ")\n", "step_train = TrainingStep(\n", " name=\"CustomerChurnTrain\",\n", " estimator=xgb_train,\n", " inputs={\n", " \"train\": TrainingInput(\n", " s3_data=step_process.properties.ProcessingOutputConfig.Outputs[\n", " \"train\"\n", " ].S3Output.S3Uri,\n", " content_type=\"text/csv\",\n", " ),\n", " \"validation\": TrainingInput(\n", " s3_data=step_process.properties.ProcessingOutputConfig.Outputs[\n", " \"validation\"\n", " ].S3Output.S3Uri,\n", " content_type=\"text/csv\",\n", " ),\n", " },\n", ")\n", "# Processing step for evaluation\n", "script_eval = ScriptProcessor(\n", " image_uri=image_uri,\n", " command=[\"python3\"],\n", " instance_type=processing_instance_type,\n", " instance_count=1,\n", " base_job_name=f\"{base_job_prefix}/script-CustomerChurn-eval\",\n", " sagemaker_session=sagemaker_session,\n", " role=role,\n", ")\n", "evaluation_report = PropertyFile(\n", " name=\"EvaluationReport\",\n", " output_name=\"evaluation\",\n", " path=\"evaluation.json\",\n", ")\n", "step_eval = ProcessingStep(\n", " name=\"CustomerChurnEval\",\n", " processor=script_eval,\n", " inputs=[\n", " ProcessingInput(\n", " source=step_train.properties.ModelArtifacts.S3ModelArtifacts,\n", " destination=\"/opt/ml/processing/model\",\n", " ),\n", " ProcessingInput(\n", " source=step_process.properties.ProcessingOutputConfig.Outputs[\n", " \"test\"\n", " ].S3Output.S3Uri,\n", " destination=\"/opt/ml/processing/test\",\n", " ),\n", " ],\n", " outputs=[\n", " ProcessingOutput(output_name=\"evaluation\", source=\"/opt/ml/processing/evaluation\"),\n", " ],\n", " code=(\"evaluate.py\"),\n", " property_files=[evaluation_report],\n", ")\n", "# Register model step that will be conditionally executed\n", "model_metrics = ModelMetrics(\n", " model_statistics=MetricsSource(\n", " s3_uri=\"{}/evaluation.json\".format(\n", " step_eval.arguments[\"ProcessingOutputConfig\"][\"Outputs\"][0][\"S3Output\"][\"S3Uri\"]\n", " ),\n", " content_type=\"application/json\",\n", " )\n", ")\n", "# Register model step that will be conditionally executed\n", "step_register = RegisterModel(\n", " name=\"CustomerChurnRegisterModel\",\n", " estimator=xgb_train,\n", " model_data=step_train.properties.ModelArtifacts.S3ModelArtifacts,\n", " content_types=[\"text/csv\"],\n", " response_types=[\"text/csv\"],\n", " inference_instances=[\"ml.t2.medium\", \"ml.m5.large\"],\n", " transform_instances=[\"ml.m5.large\"],\n", " model_package_group_name=model_package_group_name,\n", " approval_status=model_approval_status,\n", " model_metrics=model_metrics,\n", ")\n", "# Condition step for evaluating model quality and branching execution\n", "cond_lte = ConditionGreaterThanOrEqualTo( # You can change the condition here\n", " left=JsonGet(\n", " step_name=step_eval.name,\n", " property_file=evaluation_report,\n", " json_path=\"binary_classification_metrics.accuracy.value\", # This should follow the structure of your report_dict defined in the evaluate.py file.\n", " ),\n", " right=0.8, # You can change the threshold here\n", ")\n", "step_cond = ConditionStep(\n", " name=\"CustomerChurnAccuracyCond\",\n", " conditions=[cond_lte],\n", " if_steps=[step_register],\n", " else_steps=[],\n", ")\n", "\n", "# Pipeline instance\n", "pipeline = Pipeline(\n", " name=pipeline_name,\n", " parameters=[\n", " processing_instance_type,\n", " processing_instance_count,\n", " training_instance_type,\n", " model_approval_status,\n", " input_data,\n", " ],\n", " steps=[step_process, step_train, step_eval, step_cond],\n", " sagemaker_session=sagemaker_session,\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Create Pipeline" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from botocore.exceptions import ClientError, ValidationError\n", "\n", "try:\n", " response = pipeline.create(role_arn=role)\n", "except ClientError as e:\n", " error = e.response[\"Error\"]\n", " if error[\"Code\"] == \"ValidationError\" and \"Pipeline names must be unique within\" in error[\"Message\"]:\n", " print(error[\"Message\"])\n", " response = pipeline.describe()\n", " else:\n", " raise\n", "\n", "pipeline_arn = response[\"PipelineArn\"]\n", "sm_client.add_tags(\n", " ResourceArn=pipeline_arn,\n", " Tags=[\n", " {'Key': 'sagemaker:project-name', 'Value': project_name },\n", " {'Key': 'sagemaker:project-id', 'Value': project_id }\n", " ]\n", ")\n", "print(pipeline_arn)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Run Pipeline" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "start_response = pipeline.start()\n", "\n", "pipeline_execution_arn = start_response.arn\n", "print(pipeline_execution_arn)\n", "\n", "while True:\n", " resp = sm_client.describe_pipeline_execution(PipelineExecutionArn=pipeline_execution_arn)\n", " if resp['PipelineExecutionStatus'] == 'Executing':\n", " print('Running...')\n", " else:\n", " print(resp['PipelineExecutionStatus'], pipeline_execution_arn)\n", " break\n", " time.sleep(15)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Approve the model to kick-off the deployment process" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# list all packages and select the latest one\n", "packages = sm_client.list_model_packages(ModelPackageGroupName=model_package_group_name)['ModelPackageSummaryList']\n", "packages = sorted(packages, key=lambda x: x['CreationTime'], reverse=True)\n", "\n", "latest_model_package_arn = packages[0]['ModelPackageArn']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sm_client.list_model_packages(ModelPackageGroupName=model_package_group_name)['ModelPackageSummaryList']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(latest_model_package_arn)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Approve model\n", "Approval permission controlled by IAM role" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_package_update_response = sm_client.update_model_package(\n", " ModelPackageArn=latest_model_package_arn,\n", " ModelApprovalStatus=\"Approved\",\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Get Approved model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_details=sm_client.describe_model_package(ModelPackageName=latest_model_package_arn)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_data=model_details['InferenceSpecification']['Containers'][0]['ModelDataUrl']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "image_path=model_details['InferenceSpecification']['Containers'][0]['Image']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "image_path" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!mkdir pipeline_model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws s3 cp {model_data} ./pipeline_model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import tarfile\n", "# open file\n", "file = tarfile.open('./pipeline_model/model.tar.gz')\n", " \n", "# extracting file\n", "file.extractall('./pipeline_model')\n", " \n", "file.close()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Make prediction using Local Model" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "A model is created in the SageMaker notebook instance instead of using SageMaker endpoint instance. The local model in the notebook instance is used to check predictions within the notebook. " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Steps:\n", "1. Install XGboost library on the Notebook instance if not installed. \n", "2. Load the trained model.\n", "3. Use test data to make predictions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install xgboost" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import xgboost as xgb" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import joblib\n", "loaded_model = joblib.load(\"./pipeline_model/xgboost-model\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_data=pd.read_csv('test.csv',header=None)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X=test_data.iloc[0:1,1:]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X.shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xgtest = xgb.DMatrix(X.values)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "loaded_model.predict(xgtest)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Create Endpoint" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Create Model and deploy an endpoint" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from time import strftime,gmtime\n", "model_name = 'CustomerChurn-model-' + strftime(\"%M%S\", gmtime())\n", "model_version_arn=latest_model_package_arn\n", "\n", "print(\"Model name : {}\".format(model_name))\n", "\n", "create_model_response = sm_client.create_model(\n", " ModelName = model_name,\n", " ExecutionRoleArn = role, \n", " PrimaryContainer = {\n", " \"ModelPackageName\": model_version_arn,\n", " \n", " }\n", " \n", ") \n", "print(\"Model arn : {}\".format(create_model_response[\"ModelArn\"]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Create endpointconfig" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "endpoint_config_name = 'Test-EndpointConfig-' + strftime(\"%Y-%m-%d-%H-%M-%S\", gmtime())\n", "print(endpoint_config_name)\n", "create_endpoint_config_response = sm_client.create_endpoint_config(\n", " EndpointConfigName = endpoint_config_name,\n", " ProductionVariants=[{\n", " 'InstanceType':'ml.t2.medium',\n", " 'InitialVariantWeight':1,\n", " 'InitialInstanceCount':1,\n", " 'ModelName':model_name,\n", " 'VariantName':'AllTraffic'}])\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Deploy endpoint" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "endpoint_name = 'Test-endpoint-' + strftime(\"%Y-%m-%d-%H-%M-%S\", gmtime())\n", "print(\"EndpointName={}\".format(endpoint_name))\n", "\n", "create_endpoint_response = sm_client.create_endpoint(\n", " EndpointName=endpoint_name,\n", " EndpointConfigName=endpoint_config_name)\n", "print(create_endpoint_response['EndpointArn'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sm_client.describe_endpoint(EndpointName=endpoint_name)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def wait_for_response(client, endpoint_name, poll_interval=30):\n", " ### Wait until the job finishes\n", " status = 'Creating'\n", " while(status == 'Creating'):\n", " response = client.describe_endpoint(EndpointName=endpoint_name)\n", " status = response['EndpointStatus']\n", " print('Creating job is still in status: {} ...'.format(status))\n", " if status == 'Failed':\n", " message = response['FailureReason']\n", " logging.info('Endpoint Creation failed with the following error: {}'.format(message))\n", " print('Endpoint failed with the following error: {}'.format(message))\n", " raise Exception('Creating Endpoint failed')\n", " logging.info(\"Creating job is still in status: \" + status)\n", " time.sleep(poll_interval)\n", "\n", " if status == 'InService':\n", " logging.info(\"Creating job ended with status: \" + status)\n", " print('Creating job ended with status: {}'.format(status))\n", " else:\n", " raise Exception('Creating job stopped')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wait_for_response(sm_client, endpoint_name, poll_interval=30)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Invoke Endpoint" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Invoke the endpoint to make predictions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_data=pd.read_csv('test.csv',header=None)\n", "testdata1=test_data.iloc[0:1,1:]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "runtime = boto3.client(\"sagemaker-runtime\")\n", "Endpoint_name=endpoint_name" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%time\n", "# csv serialization\n", "\n", "prediction = runtime.invoke_endpoint(\n", " EndpointName=Endpoint_name,\n", " Body=testdata1.to_csv(header=False, index=False).encode(\"utf-8\"),\n", " ContentType=\"text/csv\",\n", " Accept= \"text/csv\",\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(prediction[\"Body\"].read())" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b'0.18987475335597992,0.9903860092163086,0.003180732252076268,0.010586234740912914,0.5162394046783447'\n" ] } ], "source": [ "import pandas as pd\n", "import numpy as np\n", "import sagemaker\n", "import boto3\n", "from sagemaker import get_execution_role\n", "\n", "test_data=pd.read_csv('test.csv',header=None)\n", "testdata1=test_data.iloc[0:5,1:]\n", "\n", "runtime = boto3.client(\"sagemaker-runtime\")\n", "Endpoint_name= '' # # update to your own endpoint name\n", "\n", "prediction = runtime.invoke_endpoint(\n", " EndpointName=Endpoint_name,\n", " Body=testdata1.to_csv(header=False, index=False).encode(\"utf-8\"),\n", " ContentType=\"text/csv\",\n", " Accept= \"text/csv\",\n", ")\n", "\n", "print(prediction[\"Body\"].read())" ] } ], "metadata": { "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "Python 3.9.13 64-bit", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.10" }, "vscode": { "interpreter": { "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" } } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/SageMaker_Customer_Churn_XGB_end2end.ipynb ================================================ { "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## SageMaker Model Building and Deployment " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "\n", "In this notebook we show how to use Amazon SageMaker to develop, train, tune and deploy a XGBoost model. Sythetic customer churn data is used. \n", "\n", "The data is in AWS public S3 bucket: s3://sagemaker-sample-files/datasets/tabular/synthetic/churn.txt\n", "\n", "Sklearn Processor is used to process the raw data.\n", "\n", "* XGBoost https://sagemaker.readthedocs.io/en/stable/frameworks/xgboost/using_xgboost.html?highlight=xgboost\n", "* Doc https://sagemaker.readthedocs.io/en/stable/using_sklearn.html\n", "* SDK https://sagemaker.readthedocs.io/en/stable/sagemaker.sklearn.html\n", "* boto3 https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#client\n", " \n", "**This sample is provided for demonstration purposes, make sure to conduct appropriate testing if derivating this code for your own use-cases!**" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import os\n", "import time\n", "import logging\n", "import pandas as pd\n", "import numpy as np\n", "import sagemaker\n", "import json\n", "import boto3\n", "from sagemaker import get_execution_role\n", "\n", "sm_client = boto3.client('sagemaker')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\n", "An error occurred (EntityAlreadyExists) when calling the CreateRole operation: Role with name Sagemaker-custom-role already exists.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"AttachedPolicies\": [\n", " {\n", " \"PolicyName\": \"AmazonSageMakerFullAccess\",\n", " \"PolicyArn\": \"arn:aws:iam::aws:policy/AmazonSageMakerFullAccess\"\n", " }\n", " ]\n", "}\n" ] } ], "source": [ "# create Sagemaker Full Access Assume Role, https://repost.aws/knowledge-center/iam-assume-role-cli\n", "!aws iam create-role --role-name Sagemaker-custom-role --assume-role-policy-document file://assume-role.json\n", "!aws iam attach-role-policy --role-name Sagemaker-custom-role --policy-arn \"arn:aws:iam::aws:policy/AmazonSageMakerFullAccess\"\n", "!aws iam list-attached-role-policies --role-name Sagemaker-custom-role" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Retrieve the bucket\n", "sagemaker_session = sagemaker.Session()\n", "bucket = sagemaker_session.default_bucket() # this could also be a hard-coded bucket name\n", "region = sagemaker_session.boto_region_name\n", "print(region)\n", "\n", "try:\n", " role = sagemaker.get_execution_role()\n", "except ValueError:\n", " iam = boto3.client('iam')\n", " role = iam.get_role(RoleName='Sagemaker-custom-role')['Role']['Arn']\n", "print(role)\n", "\n", "project_name = \"test_pro\"\n", "project_id = \"test_id\"\n", "print(f\"sagemaker role arn <{role}>\")\n", "\n", "assert(len(project_name) <= 15 ) # the project name should not have more than 15 chars" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(bucket)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Load Raw Data to S3" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Load raw data from the public S3 bucket to your own S3 bucket." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws s3 cp s3://sagemaker-sample-files/datasets/tabular/synthetic/churn.txt s3://{bucket}/sagemaker/DEMO-xgboost-churn/data/RawData.csv " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare script to process raw data" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Create preprocessing script. This script will be used by SageMaker process job instance to preocess raw data." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting preprocess.py\n" ] } ], "source": [ "%%writefile preprocess.py\n", "\n", "\"\"\"Preprocess the customer churn dataset.\"\"\"\n", "\n", "import argparse\n", "import logging\n", "import pathlib\n", "\n", "import boto3\n", "import numpy as np\n", "import pandas as pd\n", "\n", "logger = logging.getLogger()\n", "logger.setLevel(logging.INFO)\n", "logger.addHandler(logging.StreamHandler())\n", "\n", "if __name__ == \"__main__\":\n", " logger.info(\"Starting preprocessing.\")\n", " parser = argparse.ArgumentParser()\n", " parser.add_argument(\"--input-data\", type=str, required=True)\n", " args = parser.parse_args()\n", "\n", " base_dir = \"/opt/ml/processing\"\n", " pathlib.Path(f\"{base_dir}/data\").mkdir(parents=True, exist_ok=True)\n", " input_data = args.input_data\n", " print(input_data)\n", " bucket = input_data.split(\"/\")[2]\n", " key = \"/\".join(input_data.split(\"/\")[3:])\n", "\n", " logger.info(\"Downloading data from bucket: %s, key: %s\", bucket, key)\n", " fn = f\"{base_dir}/data/raw-data.csv\"\n", " s3 = boto3.resource(\"s3\")\n", " s3.Bucket(bucket).download_file(key, fn)\n", "\n", " logger.info(\"Reading downloaded data.\")\n", "\n", " # read in csv\n", " df = pd.read_csv(fn)\n", "\n", " # drop the \"Phone\" feature column\n", " df = df.drop([\"Phone\"], axis=1)\n", "\n", " # Change the data type of \"Area Code\"\n", " df[\"Area Code\"] = df[\"Area Code\"].astype(object)\n", "\n", " # Drop several other columns\n", " df = df.drop([\"Day Charge\", \"Eve Charge\", \"Night Charge\", \"Intl Charge\"], axis=1)\n", "\n", " # Convert categorical variables into dummy/indicator variables.\n", " model_data = pd.get_dummies(df)\n", "\n", " # Create one binary classification target column\n", " model_data = pd.concat(\n", " [\n", " model_data[\"Churn?_True.\"],\n", " model_data.drop([\"Churn?_False.\", \"Churn?_True.\"], axis=1),\n", " ],\n", " axis=1,\n", " )\n", "\n", " # Split the data\n", " train_data, validation_data, test_data = np.split(\n", " model_data.sample(frac=1, random_state=1729),\n", " [int(0.7 * len(model_data)), int(0.9 * len(model_data))],\n", " )\n", "\n", " pd.DataFrame(train_data).to_csv(\n", " f\"{base_dir}/train/train.csv\", header=False, index=False\n", " )\n", " pd.DataFrame(validation_data).to_csv(\n", " f\"{base_dir}/validation/validation.csv\", header=False, index=False\n", " )\n", " pd.DataFrame(test_data).to_csv(\n", " f\"{base_dir}/test/test.csv\", header=False, index=False\n", " )\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare data for model training" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from sagemaker.workflow.parameters import ParameterInteger, ParameterString" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "processing_instance_count = ParameterInteger(name=\"ProcessingInstanceCount\", default_value=1)\n", "processing_instance_type = ParameterString(\n", " name=\"ProcessingInstanceType\", default_value=\"ml.m5.xlarge\"\n", " )\n", "\n", "training_instance_type = ParameterString(\n", " name=\"TrainingInstanceType\", default_value=\"ml.m5.xlarge\"\n", " )\n", "\n", "model_approval_status = ParameterString(\n", " name=\"ModelApprovalStatus\",\n", " default_value=\"Approved\", # ModelApprovalStatus can be set to a default of \"Approved\" if you don't want manual approval.\n", " )\n", "\n", "input_data = ParameterString(\n", " name=\"InputDataUrl\",\n", " default_value=f\"s3://{sagemaker_session.default_bucket()}/sagemaker/DEMO-xgboost-churn/data/RawData.csv\", # Change this to point to the s3 location of your raw input data.\n", " )" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "SageMaker Process instance with sklearn image is used to process raw data." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from sagemaker.sklearn.processing import SKLearnProcessor\n", "\n", "sklearn_processor = SKLearnProcessor(\n", " framework_version=\"0.23-1\",\n", " role=role,\n", " instance_type= \"ml.m5.xlarge\", #\"local\", \n", " instance_count= 1, \n", " sagemaker_session=sagemaker_session\n", ")\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Processed data is saved back to S3 bucket." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sagemaker.processing import ProcessingInput, ProcessingOutput\n", "\n", "input_data=f\"s3://{sagemaker_session.default_bucket()}/sagemaker/DEMO-xgboost-churn/data/RawData.csv\"\n", "\n", "sklearn_processor.run(\n", " code=\"preprocess.py\", \n", " inputs=[\n", " ProcessingInput(source=f\"s3://{sagemaker_session.default_bucket()}/sagemaker/DEMO-xgboost-churn/data/RawData.csv\", destination=\"/opt/ml/processing/input\"),\n", " ], \n", " outputs=[\n", " ProcessingOutput(output_name=\"train\", source=\"/opt/ml/processing/train\"),\n", " ProcessingOutput(output_name=\"validation\", source=\"/opt/ml/processing/validation\"),\n", " ProcessingOutput(output_name=\"test\", source=\"/opt/ml/processing/test\"),\n", " ],\n", " arguments=[\"--input-data\", input_data],\n", ")\n", "\n", "preprocessing_job_description = sklearn_processor.jobs[-1].describe()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Model Training" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Get training and validation data paths." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "s3_input_train=preprocessing_job_description['ProcessingOutputConfig']['Outputs'][0]['S3Output']['S3Uri']" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "s3_input_validation=preprocessing_job_description['ProcessingOutputConfig']['Outputs'][1]['S3Output']['S3Uri']" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Define XGBoost model" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from sagemaker.inputs import TrainingInput\n", "\n", "content_type = \"csv\"\n", "train_input = TrainingInput(s3_input_train, content_type=content_type)\n", "validation_input = TrainingInput(s3_input_validation, content_type=content_type)\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "import sagemaker\n", "from sagemaker.serializers import CSVSerializer" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "prefix = 'sagemaker/xgboost_cutomer_churn'" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:sagemaker.image_uris:Ignoring unnecessary instance type: None.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "683313688378.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:1.2-1\n" ] } ], "source": [ "container=sagemaker.image_uris.retrieve(\"xgboost\", region, \"1.2-1\")\n", "print(container)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# initialize hyperparameters\n", "hyperparameters = {\n", " \"max_depth\":\"5\",\n", " \"eta\":\"0.2\",\n", " \"gamma\":\"4\",\n", " \"min_child_weight\":\"6\",\n", " \"subsample\":\"0.7\",\n", " \"objective\":\"binary:logistic\",\n", " \"num_round\":\"50\"}" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "xgb = sagemaker.estimator.Estimator(container,\n", " role, \n", " instance_count=1, \n", " instance_type='ml.m4.xlarge',\n", " hyperparameters=hyperparameters,\n", " output_path='s3://{}/{}/output'.format(bucket, prefix),\n", " sagemaker_session=sagemaker_session)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Train the XGboost model" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:sagemaker:Creating training-job with name: sagemaker-xgboost-2023-06-20-11-47-20-805\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "2023-06-20 11:47:21 Starting - Starting the training job...\n", "2023-06-20 11:47:46 Starting - Preparing the instances for training......\n", "2023-06-20 11:48:53 Downloading - Downloading input data...\n", "2023-06-20 11:49:23 Training - Downloading the training image......\n", "2023-06-20 11:50:13 Training - Training image download completed. Training in progress..[2023-06-20 11:50:30.215 ip-10-2-118-242.ec2.internal:7 INFO utils.py:27] RULE_JOB_STOP_SIGNAL_FILENAME: None\n", "INFO:sagemaker-containers:Imported framework sagemaker_xgboost_container.training\n", "INFO:sagemaker-containers:Failed to parse hyperparameter objective value binary:logistic to Json.\n", "Returning the value itself\n", "INFO:sagemaker-containers:No GPUs detected (normal if no gpus installed)\n", "INFO:sagemaker_xgboost_container.training:Running XGBoost Sagemaker in algorithm mode\n", "INFO:root:Determined delimiter of CSV input is ','\n", "INFO:root:Determined delimiter of CSV input is ','\n", "INFO:root:Determined delimiter of CSV input is ','\n", "INFO:root:Determined delimiter of CSV input is ','\n", "INFO:root:Single node training.\n", "[2023-06-20 11:50:30.332 ip-10-2-118-242.ec2.internal:7 INFO json_config.py:91] Creating hook from json_config at /opt/ml/input/config/debughookconfig.json.\n", "[2023-06-20 11:50:30.333 ip-10-2-118-242.ec2.internal:7 INFO hook.py:199] tensorboard_dir has not been set for the hook. SMDebug will not be exporting tensorboard summaries.\n", "[2023-06-20 11:50:30.333 ip-10-2-118-242.ec2.internal:7 INFO profiler_config_parser.py:102] User has disabled profiler.\n", "[2023-06-20 11:50:30.334 ip-10-2-118-242.ec2.internal:7 INFO hook.py:253] Saving to /opt/ml/output/tensors\n", "[2023-06-20 11:50:30.334 ip-10-2-118-242.ec2.internal:7 INFO state_store.py:77] The checkpoint config file /opt/ml/input/config/checkpointconfig.json does not exist.\n", "INFO:root:Debug hook created from config\n", "INFO:root:Train matrix has 3500 rows and 99 columns\n", "INFO:root:Validation matrix has 1000 rows\n", "[0]#011train-error:0.12486#011validation-error:0.13200\n", "[2023-06-20 11:50:30.348 ip-10-2-118-242.ec2.internal:7 INFO hook.py:413] Monitoring the collections: metrics\n", "[2023-06-20 11:50:30.351 ip-10-2-118-242.ec2.internal:7 INFO hook.py:476] Hook is writing from the hook with pid: 7\n", "[1]#011train-error:0.10200#011validation-error:0.10700\n", "[2]#011train-error:0.08971#011validation-error:0.09900\n", "[3]#011train-error:0.09257#011validation-error:0.09900\n", "[4]#011train-error:0.09257#011validation-error:0.09600\n", "[5]#011train-error:0.08971#011validation-error:0.09100\n", "[6]#011train-error:0.08514#011validation-error:0.08700\n", "[7]#011train-error:0.08171#011validation-error:0.08300\n", "[8]#011train-error:0.07686#011validation-error:0.08000\n", "[9]#011train-error:0.07486#011validation-error:0.08100\n", "[10]#011train-error:0.07257#011validation-error:0.07800\n", "[11]#011train-error:0.06943#011validation-error:0.07600\n", "[12]#011train-error:0.06914#011validation-error:0.07700\n", "[13]#011train-error:0.06971#011validation-error:0.07900\n", "[14]#011train-error:0.06629#011validation-error:0.08000\n", "[15]#011train-error:0.06514#011validation-error:0.07700\n", "[16]#011train-error:0.06571#011validation-error:0.07700\n", "[17]#011train-error:0.06429#011validation-error:0.07500\n", "[18]#011train-error:0.06371#011validation-error:0.07500\n", "[19]#011train-error:0.06429#011validation-error:0.07300\n", "[20]#011train-error:0.06400#011validation-error:0.07300\n", "[21]#011train-error:0.06400#011validation-error:0.07200\n", "[22]#011train-error:0.06400#011validation-error:0.07200\n", "[23]#011train-error:0.06371#011validation-error:0.07300\n", "[24]#011train-error:0.06257#011validation-error:0.07300\n", "[25]#011train-error:0.06086#011validation-error:0.07100\n", "[26]#011train-error:0.06086#011validation-error:0.07000\n", "[27]#011train-error:0.05914#011validation-error:0.06900\n", "[28]#011train-error:0.05771#011validation-error:0.06900\n", "[29]#011train-error:0.05771#011validation-error:0.06900\n", "[30]#011train-error:0.05743#011validation-error:0.06500\n", "[31]#011train-error:0.05543#011validation-error:0.06700\n", "[32]#011train-error:0.05600#011validation-error:0.06800\n", "[33]#011train-error:0.05400#011validation-error:0.07000\n", "[34]#011train-error:0.05343#011validation-error:0.06700\n", "[35]#011train-error:0.05314#011validation-error:0.07100\n", "[36]#011train-error:0.05286#011validation-error:0.07100\n", "[37]#011train-error:0.05286#011validation-error:0.07200\n", "[38]#011train-error:0.05257#011validation-error:0.07000\n", "[39]#011train-error:0.05343#011validation-error:0.06800\n", "[40]#011train-error:0.05257#011validation-error:0.06800\n", "[41]#011train-error:0.05286#011validation-error:0.06900\n", "[42]#011train-error:0.05143#011validation-error:0.06800\n", "[43]#011train-error:0.05029#011validation-error:0.06700\n", "[44]#011train-error:0.05029#011validation-error:0.06700\n", "[45]#011train-error:0.04914#011validation-error:0.06700\n", "[46]#011train-error:0.04914#011validation-error:0.06700\n", "[47]#011train-error:0.04943#011validation-error:0.06800\n", "[48]#011train-error:0.04971#011validation-error:0.06800\n", "[49]#011train-error:0.04886#011validation-error:0.06800\n", "\n", "2023-06-20 11:50:50 Uploading - Uploading generated training model\n", "2023-06-20 11:50:50 Completed - Training job completed\n", "Training seconds: 117\n", "Billable seconds: 117\n" ] } ], "source": [ "xgb.fit({'train': train_input, 'validation': validation_input})" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "sm_boto3 = boto3.client(\"sagemaker\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "artifact = sm_boto3.describe_training_job(\n", " TrainingJobName=xgb.latest_training_job.name\n", ")[\"ModelArtifacts\"][\"S3ModelArtifacts\"]\n", "\n", "print(\"Model artifact persisted at \" + artifact)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Create Endpoint" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Create an endpoint using SageMaker SDK" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:sagemaker:Creating model with name: sagemaker-xgboost-2023-06-20-12-03-03-421\n", "INFO:sagemaker:Creating endpoint-config with name sagemaker-xgboost-2023-06-20-12-03-03-421\n", "INFO:sagemaker:Creating endpoint with name sagemaker-xgboost-2023-06-20-12-03-03-421\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "-----------!" ] } ], "source": [ "xgb_predictor = xgb.deploy(\n", "initial_instance_count = 1,\n", "instance_type = 'ml.m4.xlarge',\n", "serializer = CSVSerializer())" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Endpoint name: sagemaker-xgboost-2023-06-20-12-03-03-421\n" ] } ], "source": [ "print(f'Endpoint name: {xgb_predictor.endpoint_name}')" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Invoke Endpoint" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 21. , 0. , 0.84385859, 3. ,\n", " 1.34772502, 0. , 4.74977594, 350. ,\n", " 4.38414605, 8. , 7. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 1. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 1. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 0. ,\n", " 0. , 0. , 0. , 1. ,\n", " 0. , 1. , 0. ])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test_data=pd.read_csv('test.csv',header=None)\n", "test_data.to_numpy()[2,1:]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def predict(data, rows=500):\n", " split_array = np.array_split(data, int(data.shape[0] / float(rows) + 1))\n", " predictions = ''\n", " for array in split_array:\n", " predictions = ','.join([predictions, xgb_predictor.predict(array).decode('utf-8')])\n", "\n", " return np.fromstring(predictions[1:], sep=',')\n", "\n", "predictions = predict(test_data.to_numpy()[:1,1:]) #test_data.to_numpy()[2,1:]\n", "predictions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Another way of invoking ML Endpoint model\n", "import pandas as pd\n", "import numpy as np\n", "import sagemaker\n", "import boto3\n", "from sagemaker import get_execution_role\n", "\n", "test_data=pd.read_csv('test.csv',header=None)\n", "testdata1=test_data.iloc[0:1,1:]\n", "\n", "runtime = boto3.client(\"sagemaker-runtime\")\n", "Endpoint_name='' # # update to your own endpoint name\n", "\n", "prediction = runtime.invoke_endpoint(\n", " EndpointName=Endpoint_name,\n", " Body=testdata1.to_csv(header=False, index=False).encode(\"utf-8\"),\n", " ContentType=\"text/csv\",\n", " Accept= \"text/csv\",\n", ")\n", "\n", "print(prediction[\"Body\"].read())" ] } ], "metadata": { "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "Python 3.9.13 64-bit", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.10" }, "vscode": { "interpreter": { "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" } } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/assume-role.json ================================================ { "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Principal": { "Service": "sagemaker.amazonaws.com" }, "Action": "sts:AssumeRole" } } ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/preprocess.py ================================================ """Preprocess the customer churn dataset.""" import argparse import logging import pathlib import boto3 import numpy as np import pandas as pd logger = logging.getLogger() logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) if __name__ == "__main__": logger.info("Starting preprocessing.") parser = argparse.ArgumentParser() parser.add_argument("--input-data", type=str, required=True) args = parser.parse_args() base_dir = "/opt/ml/processing" pathlib.Path(f"{base_dir}/data").mkdir(parents=True, exist_ok=True) input_data = args.input_data print(input_data) bucket = input_data.split("/")[2] key = "/".join(input_data.split("/")[3:]) logger.info("Downloading data from bucket: %s, key: %s", bucket, key) fn = f"{base_dir}/data/raw-data.csv" s3 = boto3.resource("s3") s3.Bucket(bucket).download_file(key, fn) logger.info("Reading downloaded data.") # read in csv df = pd.read_csv(fn) # drop the "Phone" feature column df = df.drop(["Phone"], axis=1) # Change the data type of "Area Code" df["Area Code"] = df["Area Code"].astype(object) # Drop several other columns df = df.drop(["Day Charge", "Eve Charge", "Night Charge", "Intl Charge"], axis=1) # Convert categorical variables into dummy/indicator variables. model_data = pd.get_dummies(df) # Create one binary classification target column model_data = pd.concat( [ model_data["Churn?_True."], model_data.drop(["Churn?_False.", "Churn?_True."], axis=1), ], axis=1, ) # Split the data train_data, validation_data, test_data = np.split( model_data.sample(frac=1, random_state=1729), [int(0.7 * len(model_data)), int(0.9 * len(model_data))], ) pd.DataFrame(train_data).to_csv( f"{base_dir}/train/train.csv", header=False, index=False ) pd.DataFrame(validation_data).to_csv( f"{base_dir}/validation/validation.csv", header=False, index=False ) pd.DataFrame(test_data).to_csv( f"{base_dir}/test/test.csv", header=False, index=False ) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/Notebooks/test.csv ================================================ 0,62,0,5.072152061281491,5,6.600411338234018,2,3.5335010789441563,300,4.3952998987652565,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0 1,5,0,12.524227291335833,2,5.639471129269478,5,4.937219265876419,150,5.881787271425925,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,21,0,0.8438585898741922,3,1.3477250198014177,0,4.749775942599953,350,4.384146052764612,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,140,0,1.5811272910168768,8,7.3775905213788375,7,0.9399315726440474,300,6.9385711004493835,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 1,101,400,5.234842890778278,1,2.7653548502051604,0,3.273098301114429,150,2.272057243256864,5,9,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,191,100,13.261102943769288,4,7.5674336500916075,4,4.305871058339482,150,4.768465663991106,4,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,107,300,10.797385169320467,5,6.9036193200866345,3,5.873994160867474,50,4.367043721283685,6,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,61,0,3.1719593708600997,5,7.192612132482753,1,2.1608903709588896,300,4.1725829829553325,6,10,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,5,400,7.926101580060937,4,4.19841302997281,0,4.285823889017822,0,4.125243927916747,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,165,0,13.10751352294834,3,7.128330808041529,1,3.747842976185972,150,4.3366879448093565,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,145,0,5.412983516279031,4,5.451053001131731,2,4.345574208183886,150,4.436816058258618,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,6,800,4.644654672912286,4,4.976604355618264,1,3.904821995610174,300,6.5209533901819325,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,43,1000,2.4022389683615955,2,7.167245410036478,5,3.514748674657301,400,3.8790466129275685,7,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,91,200,3.5172870115881976,3,7.716675314653963,4,4.399084344077369,300,7.069764416625022,4,9,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,58,200,13.993779163308034,1,7.4884614188499645,0,4.678051392172893,100,5.168536721709961,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,83,0,10.97330933640862,5,6.715210074686393,5,4.445928260891968,50,3.3325412623157336,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,190,0,8.724732983848599,3,5.966910030336059,0,3.3335471666270413,150,5.063433052142923,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,88,0,5.750111730098722,0,6.2327747735663666,4,4.795221920336285,350,5.572824406489756,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,156,200,5.1702616975494005,4,6.592128328345924,5,3.615887758182837,250,6.551662013538478,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,197,0,6.2308892345889415,6,4.6954222766517315,4,4.266959766420346,100,5.927521209999223,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 1,112,100,8.903061898626081,3,6.820250469491564,4,5.276664785860079,200,5.070820955481431,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,173,600,1.58906009281176,9,4.929693100462329,5,1.152582715882058,400,5.494604688571808,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,96,0,2.166957389777683,6,4.251841594388217,4,2.4090576869492044,250,5.292283549147358,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 1,107,400,4.191367223514582,5,5.778070318580312,4,4.7119789127823335,200,3.690976993873412,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,109,600,6.073256457271514,5,5.9446484262614225,1,4.4847003525447455,100,4.384511529669742,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1 0,9,0,7.1930507838181565,6,6.812657528748512,3,5.1175606692131295,250,6.612280690303956,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,176,300,7.370589433750022,3,5.4953890624108865,0,2.7693563032703787,150,5.7444432413047775,5,7,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,191,500,3.26272884502842,5,3.333865180649779,3,0.1912923125392343,450,6.175005323578705,8,8,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,191,300,9.134363142233354,3,7.236420366122483,1,2.1594117583172263,200,6.107121818617013,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,171,400,1.9576360316901276,3,13.622096683112519,11,3.4000000594176942,350,5.293506869746435,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1 0,126,600,3.1386345622459477,3,5.458623067450995,8,7.247459567295912,350,6.07511890908642,6,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,34,800,1.0707922754713532,3,2.8400122919361963,0,3.753240530235272,250,5.254966045933761,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,72,0,5.6135577711716405,3,4.737083184184555,0,1.0293862977108752,200,3.659161262883,3,11,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,56,0,0.8335649817333309,6,3.023889496147825,9,6.277701042909107,400,4.236313499700168,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0 0,80,700,2.2152994338764835,3,2.8175945607662727,2,4.3685788345780034,250,6.53312520517945,5,5,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,144,0,0.6583152207945444,3,3.4244439503870936,7,3.9502602999030914,450,4.001984939824158,7,5,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,147,0,4.510065490323991,4,7.877171596310296,4,3.3786161033673454,300,5.95205237614821,6,7,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,48,0,5.993166566752341,3,7.47694598939632,0,4.724791324836182,300,5.379847958785653,5,6,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,192,0,8.95151123321183,5,5.666876397010085,10,4.833996263971553,150,5.8213075410374575,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 1,142,0,6.8239545695522095,5,4.416190139225511,5,2.6686557540248184,150,2.2279503669012515,5,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,41,0,8.716702825690648,0,6.292468606501443,3,4.400542761094472,200,5.097634075063606,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,150,0,10.55670832656788,5,6.490773884048323,3,3.375279210541684,200,3.942929874901617,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,22,800,0.21991214610946666,6,3.5625771407323463,1,4.654536177169347,150,3.0000940404797194,6,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,8,400,6.245780645742607,2,8.082050430410893,1,1.9551033647754927,300,4.02962306200388,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 0,4,0,3.638722868927605,7,5.130774493971593,7,1.6907936291012682,400,4.134665794243514,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0 1,133,0,6.392764083227192,3,5.477191555385828,7,4.607713062633139,200,4.882654730298678,7,7,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,54,0,0.4773054336010265,4,1.3779552075288404,4,2.4122496285096293,300,6.233604144638955,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,193,0,6.245705952341785,2,3.1701268330522434,0,6.170605431010399,150,6.9692906237589805,4,6,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,56,400,6.900851194117602,2,7.206169147583297,0,3.0703283587766688,250,4.990579029864615,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1 0,136,800,0.6593742708325729,3,3.678297127366572,0,2.944623747249629,350,2.902868829574311,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 0,129,500,5.1743189948837385,4,0.24700439554894515,1,1.79685837743258,200,6.213403175893767,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,145,0,8.05388123748296,4,4.025921884452133,6,5.838149674650571,100,4.89829921947485,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,178,200,4.858360132120028,2,5.6401550102603055,4,4.0631868617759785,200,4.142877191538647,5,8,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,97,0,4.4782271246415934,5,6.182245808651588,3,2.5566850882793157,300,5.385089545881749,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0 0,68,0,10.788749464127914,2,4.2903886273526926,3,5.049848359503602,250,3.3947143976752105,9,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0 1,41,300,8.921947659672423,2,7.20746201697345,3,5.146790351163575,200,4.650730438144174,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,54,0,7.644363774543796,1,3.3168972770138048,1,5.358881263015425,150,5.661151728025391,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 0,75,700,1.2687337723540173,3,2.5287478822678833,5,3.7735855781125625,450,2.245779327608127,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1 0,52,0,3.757761834701122,3,0.272629544428173,1,5.500355043050583,200,4.306713991581266,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0 0,117,0,2.9609540314750005,3,0.7183731900535184,6,6.795121741546263,100,4.197708240560753,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,170,900,5.347709418242579,1,5.840103942698557,0,4.571734133371468,300,6.4724968607295885,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,64,600,5.164436929276945,7,4.035108533328375,3,4.502057513677036,100,6.8305447077487065,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,63,0,6.2396484176790175,3,7.536391812678818,0,1.7592503757642397,300,5.3670672853144055,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,131,600,0.9875595653989784,6,6.175227962540504,0,2.2764653663263275,250,5.636307744132725,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1 0,51,0,7.1672973733552885,4,0.6566813836612706,6,1.3964756660620283,400,5.693333714125637,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,70,400,2.92249249189642,4,5.72778609146583,0,2.6987849453774144,150,3.7216081564883448,3,10,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,171,500,10.483478079156104,5,7.3405599007239255,0,4.10504118810189,100,5.952831195166314,6,4,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,167,0,3.113960464062869,1,0.6566159175951212,6,7.394805629244548,300,5.113610474102175,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,119,400,6.553884755812585,5,5.691525622220866,3,3.2243379426274865,150,4.553336031270004,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 1,37,400,6.866377585526288,5,4.434219915065822,2,2.9929196853652438,100,5.455255153877922,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1 1,168,0,8.360324980132965,4,5.5027395973683,0,4.6727181968521565,50,3.4270889211734814,6,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,121,400,6.842377948201992,5,4.878651944404614,3,5.854214767256348,50,4.799332204133772,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1 1,92,400,8.57172165154155,4,7.853096077363831,0,5.669361832473407,100,6.057799899034017,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,142,400,1.785026473540988,4,3.2688444191341364,4,2.5327780127273742,350,5.395348700614924,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,152,0,5.521783873536278,2,3.5886131302047053,0,3.8815291954256415,200,5.434408025663743,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0 0,174,500,2.1887716216230806,3,4.215586765357034,2,2.8765230549503027,300,4.4773835948048815,6,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,133,0,0.8513656521017126,1,3.084311708501469,0,3.8595466989126974,200,3.3401629353442037,5,6,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,28,600,10.173097510204084,3,3.580092068586684,1,5.737090885738459,50,2.9007136006517387,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,42,1000,6.832749017672322,3,4.692591349315839,2,5.3600918544594816,250,4.422123764220787,5,8,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,111,400,3.9654389422694236,4,4.8137197032979735,1,1.5988074095141631,300,4.344859533223321,5,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,38,500,3.5444788416742807,0,2.4689672038192905,1,6.74498761847515,100,5.614614204717064,4,5,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,82,700,2.331272605740953,7,4.491012696475102,3,5.62348099322925,150,5.325664633630066,5,7,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,92,400,1.9358009631669688,5,7.307724939015656,10,4.661041694068195,400,5.826937879210751,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,84,400,7.524442667667318,8,5.15245183950096,0,3.19640441046818,150,4.610251612076812,4,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1 1,79,100,3.5351298895519925,1,5.094098286789706,1,1.9471320531137384,250,6.2705967932862965,1,12,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,156,300,2.5179491282559603,2,3.7030962078182608,3,5.731945457775653,100,5.834670335351329,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,190,0,4.662284546738933,4,7.16056963637891,11,2.740543663702855,550,4.406507550581475,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,19,0,10.090807264044221,4,3.477445863375781,5,6.7970681899174705,200,5.41062277737135,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,154,0,5.644725973871345,3,1.0292777268751403,4,6.837087072652331,300,5.401068663449248,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 0,108,0,2.453215979109556,3,1.6307207471629268,2,4.257160596108476,200,5.777424281338522,6,7,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,104,0,3.8285722209486313,5,5.915796553658232,5,3.702564429248035,400,5.43188549592684,7,5,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,37,0,1.0706075330280065,4,5.879947166640197,7,2.7190763262094717,300,4.805393185961638,6,4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,99,600,4.237453299042395,2,2.482097486208458,5,6.028405204069117,400,4.805237911747788,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,170,0,1.60851276557735,5,6.525856772993443,6,5.221557072061891,400,4.3649224588076265,4,4,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,166,800,6.677695497580171,3,3.551554396302285,5,4.275957714482779,250,5.316108189703966,6,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1 0,163,0,1.1515280694580097,5,3.190118784026024,3,3.4657830800960125,350,2.947144128696129,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,130,0,3.6060481784173817,4,2.366844877726582,0,5.163225478279801,100,3.0836698758534644,5,7,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,127,400,7.526318546400898,3,2.8669976051386983,0,4.714480597588897,50,6.081809102028246,3,4,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,198,600,1.8164858821899443,4,2.790770062456788,3,3.114852435486136,150,5.845825762025638,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,24,0,8.865414865831484,2,9.364471484314397,1,3.449408024649772,350,4.37773213343382,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 1,140,300,9.749438740516396,2,4.586405059700144,0,4.299905320387655,100,3.1637621363839745,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,137,0,6.825367809954172,1,2.3930011214151574,4,6.35095815887334,100,3.776455630617503,5,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,145,400,6.31502045182136,1,10.215112048769317,6,5.4416574514027225,250,5.058715311714279,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,194,400,0.4801116110106287,4,3.1083536614740437,3,2.9076099520473697,350,5.142910338581296,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1 0,35,0,3.9326962809471167,4,6.293803584051488,5,2.900464141275462,150,6.564485971249485,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0 1,138,0,4.324565707176873,1,4.770968090681833,5,4.77245703937839,250,5.762332876539076,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,52,300,6.731929327187362,1,3.5477926021894834,2,3.1240925284611243,150,7.219820358632264,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 0,65,500,2.2375758360847504,7,1.4050580786902884,2,2.826430290311802,250,4.825124929667343,5,6,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,174,500,4.5727455217268815,0,6.1171517498951244,4,2.3841932029217605,250,6.48991206420007,8,6,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,68,0,5.858054793746227,2,4.8889974646261205,0,3.9241533205961514,200,6.201631436169157,1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0 0,117,200,0.990852618833202,6,2.5336763096047967,6,2.8780306101069435,250,5.50481967431014,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,129,0,6.8204270824737545,5,3.609959504808451,3,0.1836575613400937,300,6.725165971988252,4,4,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,131,0,5.910436853619442,7,7.39790682023806,0,3.716214801959797,250,3.9198128402085914,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,64,500,7.443132643365009,4,6.869382354608447,2,2.196485637261908,150,4.082949328932225,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1 0,82,0,6.017525467259013,3,2.6391734417868578,5,5.954566404065707,100,5.4538955981834905,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,67,0,7.01206025191567,4,5.228994317146423,3,3.728660447833296,100,3.701069368937144,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0 0,64,0,6.765260230151925,3,2.597736658859452,5,6.833813092546656,100,5.3930570184141065,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 1,24,0,9.737355149245614,4,7.667280935087709,2,4.05421101847263,150,4.3489908506460155,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 0,137,300,1.7770145900107686,6,1.3603755853158268,8,3.1517214788508534,150,4.1394259772706645,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 0,5,900,0.29224870305414896,4,0.9290174294726129,1,3.3154052573547697,250,5.218604922112606,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 0,81,0,4.452010896223797,5,6.302822972924805,5,3.8728760258385013,200,4.383680980125602,3,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,168,100,0.4558437946867802,3,2.0831031647609404,5,2.814194100508538,250,4.410159277795752,5,6,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,28,400,9.697617467117297,4,8.746120758125539,0,3.1520636732076364,250,2.7512349136613814,6,4,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,98,0,3.7289758087955223,3,4.168914493360851,0,4.209233178947034,200,3.893749146268578,3,7,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,40,600,0.8268127296167798,1,0.12842917494780348,5,6.978311671116115,350,2.90833743857586,9,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,28,0,7.290016013045093,6,5.693553067060982,2,3.3328717961041567,150,6.140655719434045,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,134,0,4.60948231933355,5,4.1251018496617995,5,2.9806214646245577,300,3.81536972241166,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,32,0,3.4147156596366326,2,7.490226940432567,4,5.082978180028384,400,4.95159780877618,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,96,200,1.4039852562168258,3,4.1475599586175695,4,2.3199484226249143,150,5.9198942137188855,2,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,45,0,4.655580242568126,2,5.120947154461071,2,3.3534314890240844,250,2.36613746666891,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,180,0,0.5576690717904436,5,4.066840569534241,10,3.2476931567340337,200,5.575908117895988,6,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,64,200,9.847548068282515,2,7.689864080753567,5,3.1270694268689048,250,5.372191131660175,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,47,0,6.4398995363528355,2,7.468014492103308,8,3.8549130078993215,350,4.31392690821174,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,14,0,3.3391551988419703,3,3.546650156358998,9,5.1878197214965045,250,5.618847930919343,2,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0 0,128,0,1.8628788070966955,4,2.332914680331377,0,1.8795255868098173,250,6.446399185761446,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,198,700,1.6322376379929942,5,1.0637798397789604,2,5.334472451734622,150,3.7490254907327523,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,75,0,2.4095552873708703,5,3.99680916216097,3,2.8959651340496704,300,4.7228274072374425,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,90,0,1.6506062575654363,3,4.886340331526571,6,3.79298210081794,350,6.512670698694322,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,106,0,1.968927836395041,5,4.261306988121953,1,2.421793612789178,200,5.100687054277968,5,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,192,0,8.691290644201013,5,3.438595242753145,6,4.729950191251903,100,4.865835802567951,5,4,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,166,700,0.4942978926021322,3,1.5014994850758452,2,4.270916388668969,250,3.036846111202582,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,105,700,0.2638425596197633,6,7.908305441511286,8,2.240406027874579,500,5.187505143485703,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1 1,123,300,9.192170574286127,2,7.688682135320477,1,1.2270216665784428,250,3.9405537236251114,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,84,0,10.804369824247685,0,7.761981461963079,2,2.858448981132692,300,5.454865905316142,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,74,0,6.7377646663648845,3,4.9840948179142135,0,4.239082550296988,200,5.711875928084365,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,83,0,7.5689104571614765,1,4.1905430230894805,1,4.355836787294551,150,5.984610149660307,3,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,169,0,8.583950590073053,3,7.118482145046679,3,4.62697795453237,250,3.2415870570213814,4,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,39,700,3.558794016046836,1,2.9626493586295704,3,5.1324250562317975,250,5.021697221340407,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,52,0,0.6393232536017059,2,1.7096063547882387,6,4.151370921420087,150,5.330589060466255,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,137,900,2.2767698896689503,3,6.750955593647605,2,1.5134886580911942,400,4.661362517686082,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1 0,197,500,4.32449288802909,4,1.877205371638984,0,3.8717115081134494,200,4.739246916857281,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 1,184,0,11.404578865809786,6,4.526665332629225,4,7.576258845577537,0,4.301090112605774,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0 0,60,500,2.6611749029207004,2,7.0090058437682154,7,5.886802517075047,300,3.095087728136656,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,59,200,9.154732432967977,2,7.435691937791688,1,4.4562393899040975,200,2.726307229217338,4,6,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,12,700,5.1176881450129486,3,4.875820958217969,0,0.19968017405586205,300,4.326276245415393,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,200,0,9.120015661297057,3,3.714014877743797,0,2.896091999371422,100,5.539216255530237,4,6,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,112,100,10.578212200996724,4,7.843785695223782,2,3.57719590291427,200,6.084146631317549,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,160,500,7.017395434200541,4,0.5776925005749414,3,2.0575542689559265,300,6.143861250045692,5,6,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,93,0,2.305242536984184,5,3.79015033562527,0,4.098174040185547,300,5.39751970055067,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0 1,50,0,13.950739845915596,2,5.4795308357291095,5,3.0924033108145097,100,4.517298409652257,9,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1 1,100,0,7.760349335386204,3,4.422454730959796,5,5.001718449362356,150,4.801346676479901,2,4,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,29,0,6.500784104134398,3,3.071936440711884,0,5.002592893532568,400,6.527709903309313,11,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,104,300,9.891526467585326,2,4.739329271596307,1,3.7682487742876565,100,5.044228849210463,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,89,800,4.889667016380352,4,3.2655174441016475,0,4.8154691443122575,250,5.8783509735551736,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,164,0,13.495078620714393,1,6.980782873245256,4,4.533284576875214,150,6.8054618685184805,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,93,400,8.456998095440653,3,5.357314827344238,2,5.202608293057869,250,4.6006365059196845,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,96,300,0.6095853061654289,7,2.9758527495567697,4,1.2394433362628607,350,5.417299665132466,6,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,191,200,15.342823996492386,5,4.12844427547333,3,6.432561475298468,0,5.429031157066772,6,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,2,0,0.2231589994614982,3,1.1853359114865478,4,2.9466136717931364,100,4.343582069302854,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,85,0,11.3978644314101,0,4.9188051623428395,4,3.7780775884425006,200,3.671362847044031,2,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,12,0,7.946946415851912,3,5.296915595628083,3,7.199382083322368,300,2.960056893535181,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0 0,36,600,3.630396413978605,4,4.172281443206944,2,5.4971702305925385,300,4.569952096465747,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,65,400,4.51060048163301,1,4.180499166548862,3,2.7591202282567187,200,5.687487944010358,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,143,600,5.0207562527058585,0,1.8729025002248108,1,5.060002035016284,150,5.105513582198227,6,9,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,161,400,2.435762518609035,7,8.652909584574825,4,2.03581415189993,300,4.366317321378866,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,147,0,7.174579537589968,0,5.25247332034121,0,1.898277431624085,250,4.460356465085017,4,6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,156,0,0.6175685014999948,1,2.720824248057812,2,1.6957922803471923,300,4.309515194264952,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,144,700,0.1378976862428516,4,6.52897096761099,6,3.578811615922075,400,4.7869663027162845,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,34,0,3.79224718211522,3,1.2314203313515169,3,3.69468147081852,150,3.971925735902713,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,195,100,12.395279499846705,3,7.7749625581230415,0,3.457246390714676,150,4.058253705182331,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,121,400,7.521307946498242,5,4.6925248347784265,4,4.23085898527518,150,5.439294899850979,5,5,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,114,0,0.5066452178980967,2,3.0312259326208615,9,5.718765774708929,450,5.236299660993272,8,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,6,500,5.543081092296838,7,5.958614795297402,1,3.020830309560613,150,3.806518268696888,6,8,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,165,0,1.9282298114993344,4,0.4099907046528237,4,5.071458056400501,150,3.1711126229287494,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,56,200,10.065406044298603,4,6.5691577261818885,2,2.7454271102736696,100,4.36652958299001,5,6,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,78,400,3.4456804303422626,5,3.093716791536016,6,4.6033884479393565,200,7.651653766614838,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1 0,84,500,3.958706548155816,3,1.7866869443076523,3,5.057073608609729,250,5.426001753101845,4,7,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,121,0,14.757466890206167,4,9.858675410426358,2,4.581345529520802,200,6.202084825793787,10,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,173,600,7.321513422089577,4,4.315261856988873,3,3.3061274939104126,200,5.068036167550713,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1 0,79,0,4.081603136099142,4,3.1470377514517067,3,4.089380381568284,300,5.499476546028536,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 1,59,0,6.025338398844389,3,2.8800763586089264,2,2.4099417870445183,150,3.7650196871375634,2,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0 0,195,800,1.2191553480367503,2,2.380178348927373,2,5.017027641071305,300,5.225276911519521,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,106,0,1.2093337750010784,0,0.9172082859453408,5,4.077288981109769,200,6.2426100932975945,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,89,500,5.7716622281661625,5,6.060755910496043,4,3.3344473741839864,200,3.2080715669941,5,6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,198,0,7.031440916088082,6,8.981313911585444,1,4.736539791541329,250,4.348453413971458,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,94,0,7.473582686027497,5,4.050605578742251,5,3.696224841635321,100,4.866697468102236,7,7,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,156,400,5.570861030272894,5,6.210256626271432,2,4.672340556139983,200,6.6959760257523815,3,6,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,17,0,9.77581497587324,3,8.462488229962641,3,3.6365363633233567,250,7.995808233470321,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 0,150,0,5.46458708918195,3,6.8162820558122945,8,6.397382119161984,250,5.8379885341837365,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,72,0,0.5775488122629849,4,0.7939542357989477,5,5.1733977932080615,300,5.503087816218492,6,5,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,169,100,7.0769641075584495,4,5.225739840107572,3,2.852041414618542,150,2.994182982148692,4,8,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,175,400,1.2171262085247152,3,2.5240098113228444,6,2.5511916655681546,150,5.040414417604283,5,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,30,0,5.994060190089742,4,5.831328948592219,4,1.8110498309340657,150,6.47353826693768,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0 0,154,700,2.8471237334130928,3,5.383645347929363,2,3.1253778567249606,400,5.3848517744017075,6,6,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,10,0,15.679044256397779,6,7.607809010905927,4,5.347523169530901,50,3.275728118868676,7,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,156,0,4.169806460400138,4,7.659805937876575,4,3.672182208625871,350,4.8820286191333695,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0 1,17,0,6.621073129126527,5,7.1324519224373075,5,4.777130699406611,200,7.254987670508845,6,6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,184,200,8.056710311691809,3,4.796317591077699,7,5.744380853055467,100,5.052492803412385,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1 0,128,700,3.205405597925865,0,1.6971000651456314,1,5.635752862802787,250,6.474828175226968,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 1,56,0,3.971236468506421,0,6.532678999945098,0,3.8263723018646463,300,2.9049373178357976,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,177,0,2.09276989531031,5,8.390969555515499,2,1.9584236753966595,350,5.204256596502035,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0 0,173,700,2.7986231451067662,3,2.9871116388931633,2,2.545116494516538,300,4.760984406577483,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,99,0,1.9292831282234435,6,3.7193683693682478,3,2.62711760091222,350,5.522773782377926,5,6,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,52,0,0.0895337655500299,3,5.780109332962973,5,4.2392635796157405,400,3.613828981554537,2,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 0,52,500,5.007708132840347,4,5.0002799896539205,4,3.6935223394669614,200,3.8214290801789135,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,173,500,3.5454318266320097,1,4.016981600249371,4,0.6904126604777332,200,4.9264703009885285,3,8,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,183,0,4.208577874294827,2,3.2374608124753173,2,3.3852056170126277,300,4.4295527308256295,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,198,400,10.719792932197235,6,9.439815289308056,6,7.931765189159093,150,2.5935840105290224,9,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,108,300,4.566259212043191,3,3.0704775147551664,3,5.0204490284411225,200,4.692714829223918,2,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,80,600,8.094781454605076,4,5.268670214873822,0,2.6517094380664186,150,4.71698829706455,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,38,600,6.594239917484003,4,4.8724874472957005,2,6.218568256843129,100,4.488744471066721,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,136,700,1.3080840351670373,3,3.5316437075482323,2,3.799771831628848,200,6.146108401779194,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,11,0,8.652643839367661,4,5.912721361331087,5,5.030123730323028,200,4.692480121209352,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,168,100,10.986138880049081,3,6.033296085784093,3,3.019161843660074,200,6.47762455062349,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,168,500,2.6662011343711414,1,3.108439565905265,3,5.539324647785474,250,7.088358851890714,5,7,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,13,0,9.425316562973313,3,5.5686984717073225,1,4.024949634896204,100,3.994961940679565,4,7,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,106,600,0.6187907608657621,3,4.158614710692878,3,4.1675483238322535,350,5.701499727267264,6,5,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,82,700,3.4216461190398864,5,6.763163144801933,1,4.225188185087438,250,4.1477222636371645,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1 1,91,500,11.370720828387675,1,7.801939526258888,0,4.4560985227408505,200,7.297788687858166,7,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,150,200,11.576985201625805,5,10.590615023286915,5,3.5559700613467538,250,3.6641024572992777,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,100,400,4.796070546251724,1,5.161618922597127,4,6.872085438900911,150,5.429283824079398,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,191,0,5.805228233234426,1,5.374096909817811,4,5.729189465248842,400,6.569277138741213,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,125,300,6.900981584788616,2,5.281042176214202,2,4.279352416698846,200,5.391115428710593,3,5,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,126,0,3.816301464693282,2,5.420409753405697,9,8.989510760940297,250,4.142588527377276,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,152,0,1.5276827129234345,2,2.87411613449966,7,3.983030867706958,150,5.178725587041378,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,27,0,0.5052799815618458,6,5.806945600467063,7,1.7340047335410644,300,4.545258994583228,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 0,95,0,0.050595916890978636,2,0.3236705601211236,2,4.090669737254672,200,4.563195862043757,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,92,0,10.656271235224748,4,4.943316463450077,2,4.375848030615428,100,4.158447080217171,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 0,150,0,1.3306615427520478,2,1.2940422443074393,2,3.9849343129580963,200,5.0665584877135865,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0 1,194,0,10.010985700812848,4,6.773600195935902,4,5.201800739870512,150,3.6921046460245206,6,4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,58,0,8.601301061957955,3,4.564508614483281,4,3.2356693481191123,100,4.063134130270493,6,8,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,129,0,2.3350424200703577,5,6.7734429122488375,6,2.8472268904640328,350,4.711906455621764,3,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,134,0,0.1531755682416227,4,1.9628036136122382,2,5.363325717274781,300,3.881205427388447,6,6,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,116,0,2.3316206951780867,2,5.6192910193017624,2,3.333289979525901,250,5.405833498412256,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0 1,179,400,10.292922650358888,4,5.463122885055424,1,3.723713369700555,100,4.507743799724453,6,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,2,0,2.2710665175537925,2,2.204608968788204,0,5.32198763471242,200,4.42577645906518,5,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0 1,63,300,8.957848422820248,2,6.580991176278281,3,5.90354159267677,200,5.489467809024824,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 0,172,700,1.4768953832200546,5,0.1948725443651318,0,3.463206368665504,200,5.318383212248226,5,7,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,76,0,2.068338269363032,4,2.2794157562070545,4,3.771888033391649,300,5.458535321353168,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,117,0,10.63137547498156,4,10.279355595751394,2,3.292709028449973,250,4.855138314015852,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 1,10,0,6.478759285987907,3,6.702655031583746,1,4.0111180583575985,250,5.3667149824191815,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,55,500,3.8509123658239317,5,3.458048749493947,3,4.218917055928625,150,5.510206090311774,4,8,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,46,400,9.333840042316881,4,6.341943316477273,0,3.3593410210175914,150,4.613052488623438,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,128,300,9.226466415155492,6,7.153196814714036,1,4.50796922205483,150,6.048436315729601,6,5,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,13,0,9.661583775060206,2,5.095779420859566,1,3.66136768306703,100,5.298557090944331,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,145,0,4.433946224805883,4,6.164412200315546,1,3.919256002229007,300,3.150420718033125,6,7,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,170,0,1.21939981008828,6,7.5510861412222114,9,2.70165375133939,200,5.433183548684144,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,93,0,5.130962541565151,3,5.007669918951094,1,3.4213819459985917,200,5.936048861174805,4,6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,114,0,12.277307273251955,2,6.756966284802611,2,3.1626863387760427,150,4.671007752954163,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 1,119,0,4.785056250631675,1,4.43423861820334,5,3.872155050466376,250,6.3859569126957485,5,6,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,27,500,2.3363305324699173,3,0.3988852516709596,5,4.707320801619401,300,5.65068211911736,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,177,200,3.747253126842136,2,6.406400234941579,2,2.258410451019728,300,6.754390336911137,1,8,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,193,0,7.101619607481427,3,1.8895438041589452,5,5.6788635781022565,100,4.908463327400812,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,22,700,1.6686011763535165,3,3.725937901010942,1,1.9239475387185068,300,5.527073904605674,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1 0,68,600,3.721949606798596,6,2.38556517848904,1,2.845924122076388,250,5.895878729348175,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1 0,182,500,4.666779517171845,0,6.204288877570367,5,5.740598587631299,250,5.172386108218452,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1 0,160,800,0.8043837750233012,2,2.7249139125898245,3,3.9365134019499934,300,5.1431985411336125,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,122,0,6.466980435676723,3,6.583793038235246,0,3.927845599539042,250,5.184683860486057,2,8,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,126,0,1.1129122965677514,4,2.7716229115183832,1,2.7244902156686712,300,7.028834399724233,7,8,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,113,100,6.530987045104951,4,1.1065566702528409,4,3.0518807764322133,100,5.7387165183921285,2,4,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,192,500,2.7324481087447583,6,7.438365887428817,7,0.6850870501098258,350,6.3933379371746035,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,87,0,5.472753147448547,5,2.164659314505137,6,1.1399867202285412,350,4.498819165205923,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,23,600,8.216964433622277,4,7.55023013715765,0,4.4771038703749815,200,4.6440974605822625,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,6,400,7.272365123917245,3,6.248245303274068,0,2.177913632991244,150,5.852783849693241,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,146,0,3.7556719208528815,5,5.29428849058569,3,5.956389800481657,150,4.361227295637823,6,7,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,152,200,7.195300497099818,2,5.2646809623987085,5,4.113217903013926,200,4.761339856491924,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1 1,98,0,6.666329530302683,4,3.169978608306933,4,3.462688168355574,150,5.053545612903013,3,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,183,400,4.5251696631984,5,2.73017087328121,6,4.4852885928624975,100,5.893220896590557,5,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1 0,56,700,0.3846698205961738,10,7.087236547984822,1,0.6049363361738109,550,5.6582591502438,6,9,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,150,0,8.735239799298267,3,6.959893299031696,1,2.839336261311169,150,5.342917777959636,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,41,0,12.752871343995546,4,5.583948721274501,2,4.536555855654519,50,3.5532356277831267,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,61,0,0.6278529903229888,3,0.4709838442759153,1,4.1288417857687145,250,3.2960538931759884,7,7,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,58,500,6.22661300717709,3,8.999674185525322,0,4.29909802600909,300,3.7397488710794256,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,35,500,5.140424247384729,5,6.574796848952554,2,5.161299543737007,200,6.738178477096996,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 0,35,0,2.05796533167263,2,9.928294967184547,4,3.0493925828612465,300,5.12281547162478,6,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,15,0,0.9086520827083122,6,2.219255452043128,0,1.3791466082173809,350,4.552201940018407,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,152,100,1.6716897418637098,5,1.59322795325726,3,4.4809262996512285,200,6.628742198960067,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,62,200,9.340109935820298,3,7.837501790351053,6,3.7945424617242063,250,4.874846180334741,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,137,100,10.340911993857887,5,7.716276628920164,3,2.632981897016559,200,5.790073565287898,7,7,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,148,500,0.8761671247690526,0,3.801816868486324,1,4.172187834374139,300,6.254749149630939,10,8,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,29,600,7.5101376193830705,5,6.891976612403107,2,2.02878389459824,200,5.392913241515123,5,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,161,0,5.7357982471065725,4,4.240546510048774,2,5.685126940039082,100,4.134161546476268,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,77,500,6.723846319350875,3,4.832989596439403,3,5.407792771746653,150,6.786841059104193,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1 1,72,100,7.634205397880316,5,5.550220406711137,3,3.942355053486194,200,5.2405552322613005,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,109,400,6.232516073006338,2,7.401403139218218,0,3.5591974949311465,250,7.149768436141103,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,176,400,4.090563157700574,2,4.582795753572059,7,3.3300000070016047,350,3.074718426409195,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,185,200,2.3644146952258693,3,5.781596229875518,5,2.700227741096503,200,6.241097539823759,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,129,0,8.490898274004287,5,5.912629024096301,1,3.711982906839941,100,5.219172472179271,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,159,200,7.021202523757397,5,6.983686646080811,5,4.561158932412691,250,4.446251786505126,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,20,900,1.7339492478034209,0,3.274847417815822,2,3.556915899728798,350,6.501046979240647,7,6,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,190,500,7.282764199678618,5,5.618324974574307,0,3.745206767481886,100,3.8875959173413412,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,153,100,9.547135664104557,3,4.281478659043356,7,2.977235746983001,150,4.7409415756739035,7,6,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,57,200,8.580651658920651,5,5.713050083390041,1,3.612266680447029,100,6.490181632486293,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1 1,125,0,5.451430606217252,2,4.614823365324272,0,2.2004494287388274,100,6.369748199949233,6,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0 1,72,0,3.828206205754449,4,3.7662003626903866,4,2.4735600310317403,200,5.234642992004925,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0 1,163,100,16.897529354153928,5,8.177616093065312,3,5.7270460819944775,100,4.931918414332554,5,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,24,0,10.311418270901056,7,5.228084240924392,6,5.464869257732481,50,5.790633871409267,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,113,600,0.5186814408540803,1,1.4516128869210243,0,0.9956224575657622,250,7.039914108260103,8,9,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,90,500,3.7682365685272767,2,6.908166494225906,2,4.505805883240245,300,4.8578798362019375,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1 0,33,700,0.8544833773904967,2,3.800313664929375,7,4.44052833698736,450,4.349945251610467,6,4,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,43,500,7.668287998761293,6,6.8302115266298715,1,3.4398075559661963,150,5.1568502201813695,8,5,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,182,0,13.019325152874934,3,6.214163620473848,4,2.175615067529848,150,4.688646475028292,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,190,300,5.752132281896131,2,7.493617251397722,2,1.7518190257351909,250,7.8080045290770705,7,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,25,400,0.9558255694353691,5,1.3848051990052537,2,2.5697669263895153,250,4.1021834006294435,7,6,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,144,0,4.8712520888869975,2,4.830823570743477,4,4.263651955367925,250,4.953383701341995,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,4,1000,4.237402103904462,3,6.587320803489084,1,3.1399947379685944,300,4.25807780497461,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,128,0,6.605379278405966,5,8.118672001160762,0,2.17251981145697,250,4.643112529507304,9,8,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,162,0,16.164527149647874,4,11.038385332281747,1,3.622965532567009,200,4.338718644399108,8,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,193,0,5.190992621312807,1,4.122644719803498,1,2.335126098738893,150,4.2574797062545855,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,193,0,4.680872026439213,5,4.556796507038864,2,2.8566575550986806,200,6.771131799495,6,8,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,168,0,5.099966034644754,2,3.602989072149713,1,5.713789224244003,200,7.1880168617433045,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,123,0,8.072828724655729,4,9.370346753784382,2,5.746874475237781,250,4.294831741192406,5,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,23,1100,2.331034311710047,1,2.154107222792945,2,6.260367008917291,350,5.351510438526537,10,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1 1,140,0,8.783615008474,4,7.144719980420513,2,3.2547780571539637,150,4.231996657132538,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,101,0,7.807451515655797,3,5.272054466193652,5,3.856629430464515,200,3.8325087957395385,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,36,0,1.7234893433443703,6,3.387582385380199,5,2.2114860044952955,300,3.3107317920325183,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0 0,2,0,3.579489242194448,3,3.273732226380017,5,2.731500951740915,300,5.5847860849845326,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,147,0,0.981805621946272,4,5.051746759115275,0,2.690484464812217,250,6.066130284273648,5,9,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,31,800,8.820347301086146,0,2.309639015735519,0,9.496316501399988,200,5.399324167410707,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,67,300,13.358312108660927,4,7.81216008586679,1,4.102268489162111,0,6.0132593365527285,8,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,86,0,8.832601771879133,2,6.514991099485358,4,5.061867993015911,250,3.554604964933752,5,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,153,500,2.7458023383080286,3,2.5606692829316566,3,3.2732403516690365,250,4.516001863409675,6,4,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,186,0,1.2524603263484972,3,4.95197077171325,5,3.1009001444540125,300,5.613394193256223,4,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 0,174,600,1.3429924825515118,6,2.4823337218504102,3,3.438656788681122,350,3.7883893109589266,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,140,0,5.705663260653293,4,5.077386308881577,5,4.1801058803722,200,6.0750852054331705,2,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,178,0,4.153946594042634,4,5.3212298226625165,1,3.900966759290864,200,5.351170822945559,2,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,125,700,3.2288115508536808,1,4.764362334699068,2,3.138309418694469,250,6.138844195567017,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,65,800,7.202082036419872,7,6.201128833715554,3,5.47790964889235,50,4.871287017813428,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,4,600,2.771617512997256,4,4.364061919991247,0,2.3705283354610067,250,4.932623700590908,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1 1,86,700,6.471225256958407,6,7.370264814487764,0,4.6862705491274665,200,4.270550262871728,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1 1,172,300,8.737569511328386,1,7.823843875906676,2,2.5462625094193667,250,4.5472128158660094,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1 1,80,0,7.464251608190335,3,5.43704142952778,2,3.8127799417698167,150,5.074269366789778,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,121,0,7.23203135765498,2,8.599432986024576,3,2.0453764218671626,250,5.723951494567604,6,7,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,124,0,2.322310246221602,3,6.171197586561068,4,2.0297561905706933,350,5.443081813258486,4,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,40,0,5.026895179371855,5,5.867449560163732,2,5.3941199076670046,250,5.194531323235776,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 0,133,400,0.32636834718955393,6,5.287543123865229,5,3.8789503560135934,300,4.836874764175335,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,12,600,5.73085019472309,4,4.9487281195225545,0,5.179324498229724,150,4.271697950161746,3,6,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,125,0,6.295474694786253,5,4.636438334472579,6,5.976295431918776,100,4.0629564061378325,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,151,600,0.8295735708334302,1,2.7659429955671935,3,3.1694063443313216,250,6.845664385036043,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1 1,150,0,12.381786830606137,3,7.696715338014494,6,5.051486884610852,150,6.680727239429363,6,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,142,300,10.081762931774175,2,8.902149704135983,0,4.026830955858474,250,6.2943068415867,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,16,0,7.748160199003287,1,4.912464750148366,1,0.8936878770680616,150,3.3030485475696403,4,8,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,30,600,3.1841099559925587,3,4.693183123685866,1,5.154936416185087,150,5.453368453090713,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,124,0,6.350164019046546,6,6.174900893663557,4,5.097930527525358,150,4.88224683079949,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,72,0,1.711691181009578,0,4.239773023490659,0,3.4329543465751264,150,5.2848899372421565,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,45,700,0.08608664149002633,3,4.375054536230333,2,5.816351600093507,300,4.852053899466614,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1 1,5,200,8.70828391579197,1,8.627271703675758,5,5.636551523948169,300,4.67349254438222,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,69,600,3.9218629530764098,3,0.6606131042461412,1,7.736648678199284,200,3.596103745060788,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,35,300,3.7176052771152217,2,4.409190837789851,7,7.13117324753869,250,4.554213583888281,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,45,600,4.5608518930732265,2,4.944372153388863,2,2.561613046988089,250,4.545869571815807,5,6,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,162,500,3.4643117855529733,2,6.068858381223543,0,3.4540353293752437,300,4.302254354388332,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 0,2,0,1.7695607334034582,5,1.6089231888263678,9,4.989532702088363,400,3.4222179563881507,4,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,32,400,7.902175173918209,4,2.8111907798829137,5,4.996498095766689,100,6.210908069199926,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 1,80,0,1.085652294078468,5,5.405487403007681,2,3.044118363987888,150,5.134650126014571,5,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,147,0,10.410876190439387,5,6.121556471532603,8,5.716964359653233,150,6.3937412385141394,7,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,11,400,0.939121952033796,4,3.1721278212207813,4,4.005080598445405,100,4.408897375640574,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1 1,190,500,9.117224646292255,5,8.075639431989769,2,5.479068220121814,150,4.056899770885953,7,2,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,19,0,3.4150939695694245,5,2.277511240124201,9,3.859251694820836,400,4.809225331217407,7,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,105,600,0.7906868387085622,6,3.1167133010364565,2,1.6423256127936259,300,4.805656284207709,7,9,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,132,700,3.0453496549352645,4,3.2039850589683416,1,2.583618810916924,250,4.947075014860911,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,81,0,6.636607431046803,3,5.243057753931001,5,4.890138215680539,250,4.929006120121176,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,111,700,11.61561855906364,2,9.791103721307325,0,5.144235388828281,250,4.413320414031617,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,63,0,4.5282735632075095,3,6.3245079156658335,4,3.306024788314458,250,4.499637351924905,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,138,800,7.741945996788541,4,6.376694040524103,5,6.395538148973062,450,5.372021717900632,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1 1,39,400,13.177610718689596,4,4.409394182273072,4,5.387005444342339,50,5.0384372725533915,9,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,175,0,3.579378082507744,5,8.260249381460827,4,4.011491811726569,300,3.720274864405501,7,8,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,61,500,4.7398025962454025,4,6.85463330321082,5,3.9282621548592696,250,3.9290953874809067,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 0,77,800,9.616257414206443,3,6.358484995084198,2,6.5415345339342545,400,5.863795728835018,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,172,0,3.278958928189128,5,8.39292667973588,7,2.492052966458528,350,5.74160731150651,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,117,400,4.598860801783222,2,7.808073313775782,1,3.2365918773049,300,4.166398742388497,6,7,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,45,400,7.731493775183103,2,7.360375812486478,2,4.007538287225746,150,5.173537335489105,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1 0,135,0,4.251071234568392,0,6.265121835794682,3,4.084317363702906,400,3.9737478971542255,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,75,800,4.096505082265394,4,6.526019593432657,6,5.877901357478603,450,3.897035526916493,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,184,0,4.441940301145067,0,4.9670194511800405,5,6.455427143717861,150,4.324242410898774,3,5,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,114,400,10.903889422654872,4,5.94560153315781,0,3.4625965890525747,50,5.770841302549707,5,4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,121,0,0.8680204353948895,4,8.143065809441643,5,2.9617998436900534,300,5.808890829049492,5,3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,33,0,7.582888237894733,0,6.48423168801367,5,2.2408520315334437,300,5.28203929660044,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,103,500,0.5245685665295303,1,0.28610424963229075,1,3.7735330721084748,200,3.9466501233458313,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,196,0,3.6311733430357545,4,4.791796820126766,1,3.5142891976834534,300,5.022606908941649,7,7,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,96,0,7.825095257219669,3,3.9108393882877706,5,5.973246286486098,100,4.670561350861977,9,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,119,300,11.222733173038659,4,8.790412518408457,2,5.640198021607943,200,2.838309022078189,7,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,104,0,3.3222737479269933,0,0.4211701898739193,8,5.6260808270645635,250,5.4426854122342405,6,3,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,144,0,2.03714227959798,4,5.492746928986063,1,2.2377564829224594,350,5.839744470213581,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,154,0,8.300781924816599,4,8.452148804697984,2,4.128373678913023,300,4.876769153332254,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0 0,87,900,6.009231992802834,2,5.6210552664653015,4,5.680514544078957,300,4.792492303383079,7,4,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,146,400,9.199506645307947,4,6.43408233408802,4,4.269726606055725,150,5.929763083245461,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1 0,68,0,3.011957528550816,2,3.8970066289113774,3,6.559714446915987,200,4.484529962650568,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,161,0,0.8750888338032947,4,4.910024025155372,6,6.199675838597956,250,4.818097217071443,2,4,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,39,200,11.728271755532527,5,4.09663766730641,6,6.051995673543075,50,7.398623651663463,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,48,500,2.5212854833114857,5,3.6501197971396833,2,2.2913860860392594,350,4.823343163989403,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,107,100,2.8878546565428995,3,4.5562627663108515,7,5.884113629653702,150,4.950048668440645,4,3,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,102,0,9.192087167342276,6,4.840815521361356,4,4.31075807989866,50,6.061406069462437,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,158,0,7.213736897257496,2,6.442340844993111,0,2.741375998273816,300,4.71182575175518,8,9,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,29,0,8.964728798907764,4,5.8091222623564365,4,4.894734571177769,150,5.774637224313871,6,4,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,173,0,3.1558803149309025,5,4.784470359791851,5,0.9940596994459144,350,4.6640475689465966,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0 1,29,0,10.998575000374574,4,7.857770654985572,0,4.801264920143264,100,5.465171315528816,9,5,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,23,300,1.6889451601238368,7,5.361661512632631,3,0.9040703590319136,250,5.428092425840154,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,147,400,5.197832608292192,5,6.321280543933318,7,2.6726332679307347,350,3.221425079130088,3,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,149,0,4.736840876560535,4,6.05628037462207,2,4.109512323666607,250,5.760405980115267,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,62,700,5.772571396509483,5,6.160102572316878,2,5.112252502577195,250,5.35204894077084,3,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,7,400,9.599980957164156,4,7.453305968540617,1,3.365096599098407,200,6.310066719552332,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,187,0,7.977263663298243,4,4.686295984424088,0,5.896823775297217,150,6.5383057565317975,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,13,700,5.074851995952561,0,7.490495794207447,4,6.769123675093255,300,5.154998420799467,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,195,300,7.526700560937034,4,4.286264573987019,0,3.1120772271192445,100,5.53922497251585,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,8,0,3.554673386488379,5,6.708317539515628,8,5.183700165173073,350,1.965165510812758,5,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0 1,79,0,8.121589035436397,2,5.35648780269926,6,5.143066213388522,250,4.0606659658451845,6,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,54,0,3.1099119303952296,5,3.5561353154444664,1,4.6293822634704735,100,4.443912261332633,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,99,0,2.453977438012467,4,6.4158779597528435,0,2.6755034924742223,200,4.231484152117522,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,135,0,7.352076648066445,1,3.858050880901196,3,5.467269065664805,150,5.9513230437775615,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,139,0,13.101611271839127,6,8.309973084283289,0,4.753709911900351,100,5.198858922258305,8,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,58,500,3.0465926420297116,3,3.796879374780633,1,1.667252875753241,200,5.139821894166252,2,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,107,0,6.121453891068121,3,2.6943221052721564,3,0.2401502044049666,350,5.249664676732693,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 0,49,0,2.2614336709512246,5,5.948743524178817,4,2.8304592212591704,450,5.476154007823283,8,6,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,138,0,0.8681725666970506,5,2.460251019509019,0,1.498162569573926,250,4.863824825582304,5,8,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,85,0,7.046521773893682,4,7.075284362293796,0,3.482121463267041,250,6.5263627283512475,2,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0 0,180,0,5.3046485073221845,3,5.855006551300042,1,6.588823283246683,150,4.9579221771628275,2,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 1,199,400,7.551988433384826,6,6.0777041722714324,7,5.844004216161753,200,6.349252380780863,7,5,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,165,500,0.7588488417679349,4,1.1435516944270998,3,3.4296486763119063,250,3.024971708248711,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,66,600,1.693085502727956,4,3.4958882145390286,1,4.258217005408347,400,4.705002241392402,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,153,300,7.091816251238053,5,10.272569384051419,10,5.125592606018309,350,4.760267796842014,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,168,0,7.828809358390637,3,7.335539331202911,3,3.378262833703332,250,6.914875549226965,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,180,500,0.2735726458662073,5,1.7713788667451782,6,5.896292233254432,200,4.8929083340376565,3,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1 1,81,100,10.201260697281253,4,4.744525017977616,2,1.4522387013895797,100,5.177574873310874,6,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 0,3,700,3.2189918811087352,4,0.730951233962081,5,4.441998339831212,400,4.406390086333511,6,4,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,64,0,3.457787572720441,2,0.6577357687696619,6,5.876279055950304,100,5.943248636260141,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0 1,162,0,4.034886371101552,6,3.350960773127548,0,5.954435000701926,50,4.884715138003465,3,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0 1,30,0,9.189129286472216,3,5.6759964866394474,2,4.260741937685662,150,3.8909391433171803,3,6,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,182,500,1.3975597623478864,3,3.462628622735194,5,4.048250819388513,350,4.262375681171017,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,174,0,6.014333998298228,2,5.653403064832974,0,4.0308247896479035,250,5.149838433132491,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,197,500,1.1974629498044478,4,6.849415949562934,4,2.3574203137108642,350,5.459583665975996,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 1,48,600,5.324478774508263,4,6.320753609718956,1,3.8939130008194014,200,4.6278463708329385,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,68,800,5.228037169441588,6,8.325166621591336,4,3.965539732670925,350,4.211120651050317,6,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,141,500,1.8039073833729429,0,8.357507945265535,0,2.6242985145215223,400,5.798783370256835,6,7,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,84,0,7.91953953530074,3,5.673053285002254,4,3.4065472142911637,250,5.5892441817070315,2,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0 1,140,0,4.854800843294177,3,4.121944911126091,3,4.0302372555053125,200,5.614852705873023,4,8,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,177,400,11.031323017243338,2,8.713140561060808,3,6.9411527116276135,250,5.154735283886264,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,47,0,3.3110295203308144,3,0.6114452674813124,11,4.5433442663604495,350,4.804588147201908,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,35,300,4.287592127036311,2,3.8567619123740715,1,3.657358784324745,150,4.831109128613154,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,88,0,2.1216931869051177,1,4.071063694971662,0,2.551061617387336,150,6.392443482788724,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0 1,105,0,0.9720518089265928,3,7.7380740771292835,2,1.6666196940705484,350,5.375677954083702,5,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,61,0,4.7281781577773625,5,2.075976649883163,3,5.973814386663673,200,3.7458796980160667,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,99,0,2.755629186355804,1,1.3826795511924286,4,5.918787798349575,300,4.764011458076945,6,6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,43,500,8.441610635984148,3,5.431942446194513,0,4.2701023815033725,150,6.2464862550499785,2,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,49,0,0.6756861822433802,3,1.909871615538096,3,3.1087267110895183,200,4.604699021930412,7,4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,97,600,7.019000917847654,7,4.649786858223724,3,3.5711388523405336,0,6.290167096881478,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1 1,106,600,6.65581051421857,4,6.512468781939198,0,3.4025918274461966,150,6.59949784455581,8,5,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,50,200,2.0551487783682107,2,3.5186872160368887,6,6.5932653643248225,200,4.987097609316327,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,163,600,2.5279154246948763,6,3.5549129759379587,3,3.376108848867156,300,3.9585252202335024,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,86,700,3.932101848443183,1,5.4172755255614575,1,3.77464329837501,250,6.754927138555567,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,178,200,8.899662261422524,1,7.183830331536481,1,1.2110154576768664,200,4.8366964533057315,5,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,18,0,2.8378412966328157,5,2.970262205973612,1,3.158801580245888,250,4.8010370780400144,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0 0,167,1200,5.983074325020446,3,6.663035438066317,0,4.567146211102722,350,5.2115743973707245,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1 1,134,400,7.7133454089639,7,5.3823394528330315,0,3.2067969254021094,100,5.274015128833618,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,67,0,9.160468443350677,3,5.668370353138284,6,5.156926282115425,150,2.9638260573389066,7,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,69,300,2.0125197792772305,4,2.208641617967265,6,2.4963623412809635,250,5.7719430226324375,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,158,500,0.4791905890859241,3,6.868683938333696,8,6.8440623663542866,250,4.286309986106692,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1 0,24,0,2.874327517790653,0,5.010041576744663,2,3.3411385997766936,350,4.267412129131272,7,4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,6,0,5.846556947089861,3,3.1164943096670026,1,3.941683800753169,50,6.1110318754534525,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,140,800,1.5155339343463368,4,1.0406001508290963,2,5.206303643485744,350,4.583501368923301,8,6,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,196,0,10.116189344367724,2,7.101833874085107,1,2.086303297179572,250,2.1823671374236304,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0 0,181,300,0.25417886660500244,3,3.5101268031812514,6,3.767753515586662,250,6.8779118950984275,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,167,0,4.705224275179245,3,7.063070227553313,3,6.162911756666057,250,3.6216936057568265,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,102,0,0.3568397129744918,2,2.9080249852472417,7,3.4662705397581983,250,3.8435315688293312,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,143,400,0.8482268106534514,3,1.3404611494777097,5,4.039720989162665,200,4.747484680224677,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,86,0,1.3735305541210083,3,2.579185798514777,2,6.275145323152556,250,5.051759848827503,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,22,0,5.513984837965664,1,4.2525118787025695,2,5.011393160597116,300,3.3317293097887237,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0 0,30,0,1.4105653003660423,4,6.478880981111883,0,2.4728259929914014,400,3.8879456326629214,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,53,0,6.96810884065032,0,2.3491662007511778,2,5.258726192531723,150,3.999345649876908,3,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0 0,28,0,0.8355731162595097,5,3.9022632558048183,0,1.0255538301442255,300,6.844759435258736,6,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,167,300,5.032755926574869,3,5.350524323930786,2,3.5139265988711124,200,5.414258827287863,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 0,12,800,5.336233283057791,1,4.5440722620802765,0,5.5884306657675165,350,6.173600501724334,8,9,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,150,0,0.6880559322601371,3,0.736563548562347,1,2.704656281514044,250,3.748287545808711,9,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0 0,141,0,4.47439526991695,4,0.22469970151381613,3,0.4131798029624054,350,4.853366777174074,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,130,300,4.7967352368415614,5,7.090931103100372,1,3.5152165016782813,200,4.159869046656222,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,194,0,7.630530643122491,2,6.734497114494169,0,4.591040383175488,200,4.723798724879954,7,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,185,0,10.144767993064047,3,6.239292593362298,3,3.8334447031054473,150,3.4579756179773025,4,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,178,0,4.113598389963926,5,6.166345934560348,5,5.724720712620798,200,3.867966086575637,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0 1,95,0,5.968038137119581,6,6.8570738402713225,5,7.125352624315068,100,5.556147221870629,6,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 1,88,0,3.784946701372887,6,2.8975262387986254,3,5.7384209267982405,50,7.8472695186646195,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 0,99,0,6.970549983004397,0,7.403363137690379,3,5.562602831060912,350,5.190369028589666,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 1,71,0,7.390948297118487,5,5.208541669797309,1,5.613518503524021,100,4.644341065658332,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,73,0,1.614325648430193,5,5.736865858000349,3,0.7341793775927323,300,3.9909208896564317,8,9,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 0,190,0,2.528129598219966,3,1.6356761209747828,0,3.5478870483538034,150,4.177822962858156,5,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0 1,5,200,7.774450344703282,3,5.635926349730963,4,4.135892027495111,150,3.894515545906768,7,6,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 0,46,0,0.030402536452707633,7,5.465951147469727,0,2.7045092828731465,450,6.122231846402133,7,8,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0 1,88,500,3.011483361619748,1,5.216294437717832,2,1.8155991865064265,200,6.235281038355072,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 1,98,300,11.300033638432755,2,9.533078320116383,1,3.644060633271567,250,6.240388247916959,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1 1,42,0,6.724411692000152,5,6.117244663258974,3,4.623102029382443,150,4.1194165953621225,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0 1,139,0,8.484852958099115,0,6.625929738768878,0,0.9369274471547816,300,5.136757011455005,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1 1,122,100,11.045656402288408,7,9.70031751822358,6,5.3149351855549805,200,4.8724892374814335,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/README.md ================================================ # modelbuild_pipeline ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/__init__.py ================================================ ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/__version__.py ================================================ """Metadata for the pipelines package.""" __title__ = "pipelines" __description__ = "pipelines - template package" __version__ = "0.0.1" __author__ = "" __author_email__ = "" __license__ = "Apache 2.0" __url__ = "" ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/_utils.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Provides utilities for SageMaker Pipeline CLI.""" from __future__ import absolute_import import ast def get_pipeline_driver(module_name, passed_args=None): """Gets the driver for generating your pipeline definition. Pipeline modules must define a get_pipeline() module-level method. Args: module_name: The module name of your pipeline. passed_args: Optional passed arguments that your pipeline may be templated by. Returns: The SageMaker Workflow pipeline. """ _imports = __import__(module_name, fromlist=["get_pipeline"]) kwargs = convert_struct(passed_args) return _imports.get_pipeline(**kwargs) def convert_struct(str_struct=None): return ast.literal_eval(str_struct) if str_struct else {} ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/customer_churn/__init__.py ================================================ ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/customer_churn/evaluate.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Evaluation script for measuring model accuracy.""" import json import os import tarfile import logging import pickle import pandas as pd import xgboost logger = logging.getLogger() logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) # May need to import additional metrics depending on what you are measuring. # See https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html from sklearn.metrics import classification_report, roc_auc_score, accuracy_score if __name__ == "__main__": model_path = "/opt/ml/processing/model/model.tar.gz" with tarfile.open(model_path) as tar: tar.extractall(path="..") logger.debug("Loading xgboost model.") model = pickle.load(open("xgboost-model", "rb")) print("Loading test input data") test_path = "/opt/ml/processing/test/test.csv" df = pd.read_csv(test_path, header=None) logger.debug("Reading test data.") y_test = df.iloc[:, 0].to_numpy() df.drop(df.columns[0], axis=1, inplace=True) X_test = xgboost.DMatrix(df.values) logger.info("Performing predictions against test data.") predictions = model.predict(X_test) print("Creating classification evaluation report") acc = accuracy_score(y_test, predictions.round()) auc = roc_auc_score(y_test, predictions.round()) # The metrics reported can change based on the model used, but it must be a specific name per (https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html) report_dict = { "binary_classification_metrics": { "accuracy": { "value": acc, "standard_deviation" : "NaN" }, "auc" : { "value" : auc, "standard_deviation": "NaN" }, }, } evaluation_output_path = '/opt/ml/processing/evaluation/evaluation.json' with open(evaluation_output_path, 'w') as f: f.write(json.dumps(report_dict)) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/customer_churn/pipeline.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Example workflow pipeline script for CustomerChurn pipeline. . -RegisterModel . Process-> Train -> Evaluate -> Condition . . . -(stop) Implements a get_pipeline(**kwargs) method. """ import os import boto3 import sagemaker import sagemaker.session from sagemaker.estimator import Estimator from sagemaker.inputs import TrainingInput from sagemaker.model_metrics import MetricsSource, ModelMetrics from sagemaker.processing import ProcessingInput, ProcessingOutput, ScriptProcessor from sagemaker.sklearn.processing import SKLearnProcessor from sagemaker.workflow.condition_step import ConditionStep from sagemaker.workflow.conditions import ConditionGreaterThanOrEqualTo from sagemaker.workflow.functions import JsonGet from sagemaker.workflow.parameters import ParameterInteger, ParameterString from sagemaker.workflow.pipeline import Pipeline from sagemaker.workflow.properties import PropertyFile from sagemaker.workflow.step_collections import RegisterModel from sagemaker.workflow.steps import ProcessingStep, TrainingStep BASE_DIR = os.path.dirname(os.path.realpath(__file__)) def get_session(region, default_bucket): """Gets the sagemaker session based on the region. Args: region: the aws region to start the session default_bucket: the bucket to use for storing the artifacts Returns: `sagemaker.session.Session instance """ boto_session = boto3.Session(region_name=region) sagemaker_client = boto_session.client("sagemaker") runtime_client = boto_session.client("sagemaker-runtime") return sagemaker.session.Session( boto_session=boto_session, sagemaker_client=sagemaker_client, sagemaker_runtime_client=runtime_client, default_bucket=default_bucket, ) def get_pipeline( region, role=None, default_bucket=None, model_package_group_name="CustomerChurnPackageGroup", # Choose any name pipeline_name="CustomerChurnDemo-p-ewf8t7lvhivm", # You can find your pipeline name in the Studio UI (project -> Pipelines -> name) base_job_prefix="CustomerChurn", # Choose any name ): """Gets a SageMaker ML Pipeline instance working with on CustomerChurn data. Args: region: AWS region to create and run the pipeline. role: IAM role to create and run steps and pipeline. default_bucket: the bucket to use for storing the artifacts Returns: an instance of a pipeline """ sagemaker_session = get_session(region, default_bucket) if role is None: role = sagemaker.session.get_execution_role(sagemaker_session) # Parameters for pipeline execution processing_instance_count = ParameterInteger(name="ProcessingInstanceCount", default_value=1) processing_instance_type = ParameterString( name="ProcessingInstanceType", default_value="ml.m5.xlarge" ) training_instance_type = ParameterString( name="TrainingInstanceType", default_value="ml.m5.xlarge" ) model_approval_status = ParameterString( name="ModelApprovalStatus", default_value="Approved", # ModelApprovalStatus can be set to a default of "Approved" if you don't want manual approval. ) input_data = ParameterString( name="InputDataUrl", default_value=f"s3://{sagemaker_session.default_bucket()}/sagemaker/DEMO-xgboost-churn/data/RawData.csv", # Change this to point to the s3 location of your raw input data. ) # Processing step for feature engineering sklearn_processor = SKLearnProcessor( framework_version="0.23-1", instance_type=processing_instance_type, instance_count=processing_instance_count, base_job_name=f"{base_job_prefix}/sklearn-CustomerChurn-preprocess", # choose any name sagemaker_session=sagemaker_session, role=role, ) step_process = ProcessingStep( name="CustomerChurnProcess", # choose any name processor=sklearn_processor, outputs=[ ProcessingOutput(output_name="train", source="/opt/ml/processing/train"), ProcessingOutput(output_name="validation", source="/opt/ml/processing/validation"), ProcessingOutput(output_name="test", source="/opt/ml/processing/test"), ], code=os.path.join(BASE_DIR, "preprocess.py"), job_arguments=["--input-data", input_data], ) # Training step for generating model artifacts model_path = f"s3://{sagemaker_session.default_bucket()}/{base_job_prefix}/CustomerChurnTrain" image_uri = sagemaker.image_uris.retrieve( framework="xgboost", # we are using the Sagemaker built in xgboost algorithm region=region, version="1.0-1", py_version="py3", instance_type=training_instance_type, ) xgb_train = Estimator( image_uri=image_uri, instance_type=training_instance_type, instance_count=1, output_path=model_path, base_job_name=f"{base_job_prefix}/CustomerChurn-train", sagemaker_session=sagemaker_session, role=role, ) xgb_train.set_hyperparameters( objective="binary:logistic", num_round=50, max_depth=5, eta=0.2, gamma=4, min_child_weight=6, subsample=0.7, silent=0, ) step_train = TrainingStep( name="CustomerChurnTrain", estimator=xgb_train, inputs={ "train": TrainingInput( s3_data=step_process.properties.ProcessingOutputConfig.Outputs[ "train" ].S3Output.S3Uri, content_type="text/csv", ), "validation": TrainingInput( s3_data=step_process.properties.ProcessingOutputConfig.Outputs[ "validation" ].S3Output.S3Uri, content_type="text/csv", ), }, ) # Processing step for evaluation script_eval = ScriptProcessor( image_uri=image_uri, command=["python3"], instance_type=processing_instance_type, instance_count=1, base_job_name=f"{base_job_prefix}/script-CustomerChurn-eval", sagemaker_session=sagemaker_session, role=role, ) evaluation_report = PropertyFile( name="EvaluationReport", output_name="evaluation", path="evaluation.json", ) step_eval = ProcessingStep( name="CustomerChurnEval", processor=script_eval, inputs=[ ProcessingInput( source=step_train.properties.ModelArtifacts.S3ModelArtifacts, destination="/opt/ml/processing/model", ), ProcessingInput( source=step_process.properties.ProcessingOutputConfig.Outputs[ "test" ].S3Output.S3Uri, destination="/opt/ml/processing/test", ), ], outputs=[ ProcessingOutput(output_name="evaluation", source="/opt/ml/processing/evaluation"), ], code=os.path.join(BASE_DIR, "evaluate.py"), property_files=[evaluation_report], ) # Register model step that will be conditionally executed model_metrics = ModelMetrics( model_statistics=MetricsSource( s3_uri="{}/evaluation.json".format( step_eval.arguments["ProcessingOutputConfig"]["Outputs"][0]["S3Output"]["S3Uri"] ), content_type="application/json", ) ) # Register model step that will be conditionally executed step_register = RegisterModel( name="CustomerChurnRegisterModel", estimator=xgb_train, model_data=step_train.properties.ModelArtifacts.S3ModelArtifacts, content_types=["text/csv"], response_types=["text/csv"], inference_instances=["ml.t2.medium", "ml.m5.large"], transform_instances=["ml.m5.large"], model_package_group_name=model_package_group_name, approval_status=model_approval_status, model_metrics=model_metrics, ) # Condition step for evaluating model quality and branching execution cond_lte = ConditionGreaterThanOrEqualTo( # You can change the condition here left=JsonGet( step_name=step_eval.name, property_file=evaluation_report, json_path="binary_classification_metrics.accuracy.value", # This should follow the structure of your report_dict defined in the evaluate.py file. ), right=0.8, # You can change the threshold here ) step_cond = ConditionStep( name="CustomerChurnAccuracyCond", conditions=[cond_lte], if_steps=[step_register], else_steps=[], ) # Pipeline instance pipeline = Pipeline( name=pipeline_name, parameters=[ processing_instance_type, processing_instance_count, training_instance_type, model_approval_status, input_data, ], steps=[step_process, step_train, step_eval, step_cond], sagemaker_session=sagemaker_session, ) return pipeline ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/customer_churn/preprocess.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Feature engineers the customer churn dataset.""" import argparse import logging import pathlib import boto3 import numpy as np import pandas as pd logger = logging.getLogger() logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) if __name__ == "__main__": logger.info("Starting preprocessing.") parser = argparse.ArgumentParser() parser.add_argument("--input-data", type=str, required=True) args = parser.parse_args() base_dir = "/opt/ml/processing" pathlib.Path(f"{base_dir}/data").mkdir(parents=True, exist_ok=True) input_data = args.input_data print(input_data) bucket = input_data.split("/")[2] key = "/".join(input_data.split("/")[3:]) logger.info("Downloading data from bucket: %s, key: %s", bucket, key) fn = f"{base_dir}/data/raw-data.csv" s3 = boto3.resource("s3") s3.Bucket(bucket).download_file(key, fn) logger.info("Reading downloaded data.") # read in csv df = pd.read_csv(fn) # drop the "Phone" feature column df = df.drop(["Phone"], axis=1) # Change the data type of "Area Code" df["Area Code"] = df["Area Code"].astype(object) # Drop several other columns df = df.drop(["Day Charge", "Eve Charge", "Night Charge", "Intl Charge"], axis=1) # Convert categorical variables into dummy/indicator variables. model_data = pd.get_dummies(df) # Create one binary classification target column model_data = pd.concat( [ model_data["Churn?_True."], model_data.drop(["Churn?_False.", "Churn?_True."], axis=1), ], axis=1, ) # Split the data train_data, validation_data, test_data = np.split( model_data.sample(frac=1, random_state=1729), [int(0.7 * len(model_data)), int(0.9 * len(model_data))], ) pd.DataFrame(train_data).to_csv( f"{base_dir}/train/train.csv", header=False, index=False ) pd.DataFrame(validation_data).to_csv( f"{base_dir}/validation/validation.csv", header=False, index=False ) pd.DataFrame(test_data).to_csv( f"{base_dir}/test/test.csv", header=False, index=False ) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/get_pipeline_definition.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """A CLI to get pipeline definitions from pipeline modules.""" from __future__ import absolute_import import argparse import sys from pipelines._utils import get_pipeline_driver def main(): # pragma: no cover """The main harness that gets the pipeline definition JSON. Prints the json to stdout or saves to file. """ parser = argparse.ArgumentParser("Gets the pipeline definition for the pipeline script.") parser.add_argument( "-n", "--module-name", dest="module_name", type=str, help="The module name of the pipeline to import.", ) parser.add_argument( "-f", "--file-name", dest="file_name", type=str, default=None, help="The file to output the pipeline definition json to.", ) parser.add_argument( "-kwargs", "--kwargs", dest="kwargs", default=None, help="Dict string of keyword arguments for the pipeline generation (if supported)", ) args = parser.parse_args() if args.module_name is None: parser.print_help() sys.exit(2) try: pipeline = get_pipeline_driver(args.module_name, args.kwargs) content = pipeline.definition() if args.file_name: with open(args.file_name, "w") as f: f.write(content) else: print(content) except Exception as e: # pylint: disable=W0703 print(f"Exception: {e}") sys.exit(1) if __name__ == "__main__": main() ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/pipelines/run_pipeline.py ================================================ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """A CLI to create or update and run pipelines.""" from __future__ import absolute_import import argparse import json import sys from pipelines._utils import get_pipeline_driver, convert_struct def main(): # pragma: no cover """The main harness that creates or updates and runs the pipeline. Creates or updates the pipeline and runs it. """ parser = argparse.ArgumentParser( "Creates or updates and runs the pipeline for the pipeline script." ) parser.add_argument( "-n", "--module-name", dest="module_name", type=str, help="The module name of the pipeline to import.", ) parser.add_argument( "-kwargs", "--kwargs", dest="kwargs", default=None, help="Dict string of keyword arguments for the pipeline generation (if supported)", ) parser.add_argument( "-role-arn", "--role-arn", dest="role_arn", type=str, help="The role arn for the pipeline service execution role.", ) parser.add_argument( "-description", "--description", dest="description", type=str, default=None, help="The description of the pipeline.", ) parser.add_argument( "-tags", "--tags", dest="tags", default=None, help="""List of dict strings of '[{"Key": "string", "Value": "string"}, ..]'""", ) args = parser.parse_args() if args.module_name is None or args.role_arn is None: parser.print_help() sys.exit(2) tags = convert_struct(args.tags) try: pipeline = get_pipeline_driver(args.module_name, args.kwargs) print("###### Creating/updating a SageMaker Pipeline with the following definition:") parsed = json.loads(pipeline.definition()) print(json.dumps(parsed, indent=2, sort_keys=True)) upsert_response = pipeline.upsert( role_arn=args.role_arn, description=args.description, tags=tags ) print("\n###### Created/Updated SageMaker Pipeline: Response received:") print(upsert_response) execution = pipeline.start() print(f"\n###### Execution started with PipelineExecutionArn: {execution.arn}") print("Waiting for the execution to finish...") execution.wait() print("\n#####Execution completed. Execution step details:") print(execution.list_steps()) # Todo print the status? except Exception as e: # pylint: disable=W0703 print(f"Exception: {e}") sys.exit(1) if __name__ == "__main__": main() ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/setup.cfg ================================================ [tool:pytest] addopts = -vv testpaths = tests [aliases] test=pytest [metadata] description-file = README.md license_file = LICENSE [wheel] universal = 1 ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/setup.py ================================================ import os import setuptools about = {} here = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(here, "pipelines", "__version__.py")) as f: exec(f.read(), about) with open("README.md", "r") as f: readme = f.read() required_packages = ["sagemaker"] extras = { "test": [ "black", "coverage", "flake8", "mock", "pydocstyle", "pytest", "pytest-cov", "sagemaker", "tox", ] } setuptools.setup( name=about["__title__"], description=about["__description__"], version=about["__version__"], author=about["__author__"], author_email=["__author_email__"], long_description=readme, long_description_content_type="text/markdown", url=about["__url__"], license=about["__license__"], packages=setuptools.find_packages(), include_package_data=True, python_requires=">=3.6", install_requires=required_packages, extras_require=extras, entry_points={ "console_scripts": [ "get-pipeline-definition=pipelines.get_pipeline_definition:main", "run-pipeline=pipelines.run_pipeline:main", ] }, classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", ], ) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/tests/test_pipelines.py ================================================ import pytest @pytest.mark.xfail def test_that_you_wrote_tests(): assert False, "No tests written" def test_pipelines_importable(): import pipelines # noqa: F401 ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modelbuild_pipeline/tox.ini ================================================ # Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = black-format,flake8,pydocstyle,py{36,37,38} [flake8] max-line-length = 120 exclude = build/ .git __pycache__ .tox venv/ max-complexity = 10 ignore = C901, E203, # whitespace before ':': Black disagrees with and explicitly violates this. FI10, FI12, FI13, FI14, FI15, FI16, FI17, FI18, # __future__ import "annotations" missing -> check only Python 3.7 compatible FI50, FI51, FI52, FI53, FI54, FI55, FI56, FI57, W503 require-code = True [testenv] commands = pytest --cov=pipelines --cov-append {posargs} coverage report --fail-under=0 deps = .[test] depends = {py36,py37,py38}: clean [testenv:flake8] skipdist = true skip_install = true deps = flake8 commands = flake8 [testenv:black-format] deps = black commands = black -l 100 ./ [testenv:black-check] deps = black commands = black -l 100 --check ./ [testenv:clean] skip_install = true deps = coverage commands = coverage erase [testenv:pydocstyle] deps = pydocstyle commands = pydocstyle pipelines ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/README.md ================================================ # modeldeploy_pipeline ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/build.py ================================================ import argparse import json import logging import os import boto3 from botocore.exceptions import ClientError logger = logging.getLogger(__name__) sm_client = boto3.client("sagemaker") def get_approved_package(model_package_group_name): """Gets the latest approved model package for a model package group. Args: model_package_group_name: The model package group name. Returns: The SageMaker Model Package ARN. """ try: # Get the latest approved model package response = sm_client.list_model_packages( ModelPackageGroupName=model_package_group_name, ModelApprovalStatus="Approved", SortBy="CreationTime", MaxResults=100, ) approved_packages = response["ModelPackageSummaryList"] # Fetch more packages if none returned with continuation token while len(approved_packages) == 0 and "NextToken" in response: logger.debug("Getting more packages for token: {}".format(response["NextToken"])) response = sm_client.list_model_packages( ModelPackageGroupName=model_package_group_name, ModelApprovalStatus="Approved", SortBy="CreationTime", MaxResults=100, NextToken=response["NextToken"], ) approved_packages.extend(response["ModelPackageSummaryList"]) # Return error if no packages found if len(approved_packages) == 0: error_message = ( f"No approved ModelPackage found for ModelPackageGroup: {model_package_group_name}" ) logger.error(error_message) raise Exception(error_message) # Return the pmodel package arn model_package_arn = approved_packages[0]["ModelPackageArn"] logger.info(f"Identified the latest approved model package: {model_package_arn}") return model_package_arn except ClientError as e: error_message = e.response["Error"]["Message"] logger.error(error_message) raise Exception(error_message) def extend_config(args, model_package_arn, stage_config): """ Extend the stage configuration with additional parameters and tags based. """ # Verify that config has parameters and tags sections if not "Parameters" in stage_config or not "StageName" in stage_config["Parameters"]: raise Exception("Configuration file must include SageName parameter") if not "Tags" in stage_config: stage_config["Tags"] = {} # Create new params and tags new_params = { "SageMakerProjectName": args.sagemaker_project_name, "ModelPackageName": model_package_arn, "ModelExecutionRoleArn": args.model_execution_role, } new_tags = { "sagemaker:deployment-stage": stage_config["Parameters"]["StageName"], "sagemaker:project-id": args.sagemaker_project_id, "sagemaker:project-name": args.sagemaker_project_name, } return { "Parameters": {**stage_config["Parameters"], **new_params}, "Tags": {**stage_config.get("Tags", {}), **new_tags}, } if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--log-level", type=str, default=os.environ.get("LOGLEVEL", "INFO").upper()) parser.add_argument("--model-execution-role", type=str, required=True) parser.add_argument("--model-package-group-name", type=str, required=True) parser.add_argument("--sagemaker-project-id", type=str, required=True) parser.add_argument("--sagemaker-project-name", type=str, required=True) parser.add_argument("--import-staging-config", type=str, default="staging-config.json") parser.add_argument("--import-prod-config", type=str, default="prod-config.json") parser.add_argument("--export-staging-config", type=str, default="staging-config-export.json") parser.add_argument("--export-prod-config", type=str, default="prod-config-export.json") args, _ = parser.parse_known_args() # Configure logging to output the line number and message log_format = "%(levelname)s: [%(filename)s:%(lineno)s] %(message)s" logging.basicConfig(format=log_format, level=args.log_level) # Get the latest approved package model_package_arn = get_approved_package(args.model_package_group_name) # Write the staging config with open(args.import_staging_config, "r") as f: staging_config = extend_config(args, model_package_arn, json.load(f)) logger.debug("Staging config: {}".format(json.dumps(staging_config, indent=4))) with open(args.export_staging_config, "w") as f: json.dump(staging_config, f, indent=4) # Write the prod config with open(args.import_prod_config, "r") as f: prod_config = extend_config(args, model_package_arn, json.load(f)) logger.debug("Prod config: {}".format(json.dumps(prod_config, indent=4))) with open(args.export_prod_config, "w") as f: json.dump(prod_config, f, indent=4) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/endpoint-config-template.yml ================================================ Description: This template is built and deployed by the infrastructure pipeline in various stages (staging/production) as required. It specifies the resources that need to be created, like the SageMaker Endpoint. It can be extended to include resources like AutoScalingPolicy, API Gateway, etc,. as required. Parameters: SageMakerProjectName: Type: String Description: Name of the project MinLength: 1 MaxLength: 32 AllowedPattern: ^[a-zA-Z](-*[a-zA-Z0-9])* ModelExecutionRoleArn: Type: String Description: Execution role used for deploying the model. ModelPackageName: Type: String Description: The trained Model Package Name StageName: Type: String Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed. EndpointInstanceCount: Type: Number Description: Number of instances to launch for the endpoint. MinValue: 1 EndpointInstanceType: Type: String Description: The ML compute instance type for the endpoint. Resources: Model: Type: AWS::SageMaker::Model Properties: PrimaryContainer: ModelPackageName: !Ref ModelPackageName ExecutionRoleArn: !Ref ModelExecutionRoleArn EndpointConfig: Type: AWS::SageMaker::EndpointConfig Properties: ProductionVariants: - InitialInstanceCount: !Ref EndpointInstanceCount InitialVariantWeight: 1.0 InstanceType: !Ref EndpointInstanceType ModelName: !GetAtt Model.ModelName VariantName: AllTraffic Endpoint: Type: AWS::SageMaker::Endpoint Properties: EndpointName: !Sub ${SageMakerProjectName}-${StageName} EndpointConfigName: !GetAtt EndpointConfig.EndpointConfigName ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/fix_model_permission.py ================================================ import argparse import boto3 import json import os import logging from botocore.exceptions import ClientError # this script is a workaround to fix some permission issues with the file # created for the model and stored in an S3 bucket s3_client = boto3.client('s3') sm_client = boto3.client('sagemaker') if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--log-level", type=str, default=os.environ.get("LOGLEVEL", "INFO").upper()) parser.add_argument("--prod-config-file", type=str, default="prod-config-export.json") args, _ = parser.parse_known_args() # Configure logging to output the line number and message log_format = "%(levelname)s: [%(filename)s:%(lineno)s] %(message)s" logging.basicConfig(format=log_format, level=args.log_level) # first retrieve the name of the package that will be deployed model_package_name = None with open(args.prod_config_file, 'r') as f: for param in json.loads(f.read()): if param.get('ParameterKey') == 'ModelPackageName': model_package_name = param.get('ParameterValue') if model_package_name is None: raise Exception("Configuration file must include ModelPackageName parameter") # then, describe it to get the S3 URL of the model resp = sm_client.describe_model_package(ModelPackageName=model_package_name) model_data_url = resp['InferenceSpecification']['Containers'][0]['ModelDataUrl'] _,_,bucket_name,key = model_data_url.split('/', 3) # finally, copy the file to override the permissions with open('/tmp/model.tar.gz', 'wb') as data: s3_client.download_fileobj(bucket_name, key, data) with open('/tmp/model.tar.gz', 'rb') as data: s3_client.upload_fileobj(data, bucket_name, key) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/prod-config.json ================================================ { "Parameters": { "StageName": "prod-0306", "EndpointInstanceCount": "1", "EndpointInstanceType": "ml.m5.large" } } ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/setup.py ================================================ import argparse import json import logging import os import argparse import boto3 from botocore.exceptions import ClientError logger = logging.getLogger(__name__) sm_client = boto3.client("sagemaker") org_client = boto3.client("organizations") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--log-level", type=str, default=os.environ.get("LOGLEVEL", "INFO").upper()) parser.add_argument("--sagemaker-project-id", type=str, required=True) parser.add_argument("--sagemaker-project-name", type=str, required=True) parser.add_argument("--model-package-group-name", type=str, required=True) parser.add_argument("--organizational-unit-staging-id", type=str, required=True) parser.add_argument("--organizational-unit-prod-id", type=str, required=True) args, _ = parser.parse_known_args() # Configure logging to output the line number and message log_format = "%(levelname)s: [%(filename)s:%(lineno)s] %(message)s" logging.basicConfig(format=log_format, level=args.log_level) model_package_group_arn = None # Create model package group if necessary try: # check if the model package group exists resp = sm_client.describe_model_package_group( ModelPackageGroupName=args.model_package_group_name) model_package_group_arn = resp['ModelPackageGroupArn'] except ClientError as e: if e.response['Error']['Code'] == 'ValidationException': # it doesn't exist, lets create a new one resp = sm_client.create_model_package_group( ModelPackageGroupName=args.model_package_group_name, ModelPackageGroupDescription="Multi account model group", Tags=[ {'Key': 'sagemaker:project-name', 'Value': args.sagemaker_project_name}, {'Key': 'sagemaker:project-id', 'Value': args.sagemaker_project_id}, ] ) model_package_group_arn = resp['ModelPackageGroupArn'] else: raise e staging_ou_id = args.organizational_unit_staging_id prod_ou_id = args.organizational_unit_prod_id # finally, we need to update the model package group policy # Get the account principals based on staging and prod ids staging_accounts = [i['Id'] for i in org_client.list_accounts_for_parent(ParentId=staging_ou_id)['Accounts']] prod_accounts = [i['Id'] for i in org_client.list_accounts_for_parent(ParentId=prod_ou_id)['Accounts']] # update the policy sm_client.put_model_package_group_policy( ModelPackageGroupName=args.model_package_group_name, ResourcePolicy=json.dumps({ 'Version': '2012-10-17', 'Statement': [{ 'Sid': 'Stmt1527884065456', 'Effect': 'Allow', 'Principal': {'AWS': ['arn:aws:iam::%s:root' % i for i in staging_accounts + prod_accounts] }, 'Action': 'sagemaker:CreateModel', 'Resource': '%s/*' % model_package_group_arn.replace('model-package-group', 'model-package') }] }) ) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/staging-config.json ================================================ { "Parameters": { "StageName": "staging-0306", "EndpointInstanceCount": "1", "EndpointInstanceType": "ml.m5.large" } } ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/test/test.py ================================================ import argparse import json import logging import os import boto3 from botocore.exceptions import ClientError logger = logging.getLogger(__name__) sm_client = boto3.client("sagemaker") def invoke_endpoint(endpoint_name): """ Add custom logic here to invoke the endpoint and validate reponse """ return {"endpoint_name": endpoint_name, "success": True} def test_endpoint(endpoint_name): """ Describe the endpoint and ensure InSerivce, then invoke endpoint. Raises exception on error. """ error_message = None try: # Ensure endpoint is in service response = sm_client.describe_endpoint(EndpointName=endpoint_name) status = response["EndpointStatus"] if status != "InService": error_message = f"SageMaker endpoint: {endpoint_name} status: {status} not InService" logger.error(error_message) raise Exception(error_message) # Output if endpoint has data capture enbaled endpoint_config_name = response["EndpointConfigName"] response = sm_client.describe_endpoint_config(EndpointConfigName=endpoint_config_name) if "DataCaptureConfig" in response and response["DataCaptureConfig"]["EnableCapture"]: logger.info(f"data capture enabled for endpoint config {endpoint_config_name}") # Call endpoint to handle return invoke_endpoint(endpoint_name) except ClientError as e: error_message = e.response["Error"]["Message"] logger.error(error_message) raise Exception(error_message) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--log-level", type=str, default=os.environ.get("LOGLEVEL", "INFO").upper()) parser.add_argument("--import-build-config", type=str, required=True) parser.add_argument("--export-test-results", type=str, required=True) args, _ = parser.parse_known_args() # Configure logging to output the line number and message log_format = "%(levelname)s: [%(filename)s:%(lineno)s] %(message)s" logging.basicConfig(format=log_format, level=args.log_level) # Load the build config with open(args.import_build_config, "r") as f: config = json.load(f) # Get the endpoint name from sagemaker project name endpoint_name = "{}-{}".format( config["Parameters"]["SageMakerProjectName"], config["Parameters"]["StageName"] ) results = test_endpoint(endpoint_name) # Print results and write to file logger.debug(json.dumps(results, indent=4)) with open(args.export_test_results, "w") as f: json.dump(results, f, indent=4) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/test/test_buildspec.yml ================================================ version: 0.2 phases: install: runtime-versions: python: 3.8 build: commands: # Call the test python code - python test/test.py --import-build-config $CODEBUILD_SRC_DIR_BuildArtifact/staging-config-export.json --export-test-results ${EXPORT_TEST_RESULTS} # Show the test results file - cat ${EXPORT_TEST_RESULTS} artifacts: files: - ${EXPORT_TEST_RESULTS} ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/test/test_buildspec_singleaccount.yml ================================================ version: 0.2 phases: install: runtime-versions: python: 3.8 build: commands: # Call the test python code - python test/test.py --import-build-config $CODEBUILD_SRC_DIR_BuildArtifact/staging-config-export.json --export-test-results ${EXPORT_TEST_RESULTS} # Show the test results file - cat ${EXPORT_TEST_RESULTS} artifacts: files: - ${EXPORT_TEST_RESULTS} ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/modeldeploy_pipeline/test/test_singleaccount.py ================================================ import argparse import json import logging import os import boto3 from botocore.exceptions import ClientError logger = logging.getLogger(__name__) sm_client = boto3.client("sagemaker") def invoke_endpoint(endpoint_name): """ Add custom logic here to invoke the endpoint and validate reponse """ return {"endpoint_name": endpoint_name, "success": True} def test_endpoint(endpoint_name): """ Describe the endpoint and ensure InSerivce, then invoke endpoint. Raises exception on error. """ error_message = None try: # Ensure endpoint is in service response = sm_client.describe_endpoint(EndpointName=endpoint_name) status = response["EndpointStatus"] if status != "InService": error_message = f"SageMaker endpoint: {endpoint_name} status: {status} not InService" logger.error(error_message) raise Exception(error_message) # Output if endpoint has data capture enbaled endpoint_config_name = response["EndpointConfigName"] response = sm_client.describe_endpoint_config(EndpointConfigName=endpoint_config_name) if "DataCaptureConfig" in response and response["DataCaptureConfig"]["EnableCapture"]: logger.info(f"data capture enabled for endpoint config {endpoint_config_name}") # Call endpoint to handle return invoke_endpoint(endpoint_name) except ClientError as e: error_message = e.response["Error"]["Message"] logger.error(error_message) raise Exception(error_message) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--log-level", type=str, default=os.environ.get("LOGLEVEL", "INFO").upper()) parser.add_argument("--import-build-config", type=str, required=True) parser.add_argument("--export-test-results", type=str, required=True) args, _ = parser.parse_known_args() # Configure logging to output the line number and message log_format = "%(levelname)s: [%(filename)s:%(lineno)s] %(message)s" logging.basicConfig(format=log_format, level=args.log_level) # Load the build config with open(args.import_build_config, "r") as f: config = json.load(f) # Get the endpoint name from sagemaker project name endpoint_name = "{}-{}".format( config["Parameters"]["SageMakerProjectName"], config["Parameters"]["StageName"] ) results = test_endpoint(endpoint_name) # Print results and write to file logger.debug(json.dumps(results, indent=4)) with open(args.export_test_results, "w") as f: json.dump(results, f, indent=4) ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/terraform/events.tf ================================================ resource "aws_cloudwatch_event_rule" "sm_model_registry_rule" { name = "sm-model-registry-event-rule" description = "Capture new model registry" event_pattern = <" env = "dev" project_name = "" #"aws-ml-11052023" project_id = "" #"04052023" region = "us-east-1" repository_owner = "" build_repository_name = "modelbuild_pipeline" deploy_repository_name = "modeldeploy_pipeline" artifacts_bucket_name = "" # "artifact-ml-11052023" #join("-", [var.project_name, var.project_id, var.env]) github_token = "" # to pull modelbuild and modeldeploy ================================================ FILE: samples/mlops-sagemaker-github-codepipeline-codebuild-codedeploy/terraform/variables.tf ================================================ variable "repository_branch" { description = "Repository branch to connect to" default = "" } variable "env" { description = "Deployment environment" default = "dev" } variable "project_name" { description = "Project name" default = "" } variable "project_id" { description = "Project ID" default = "" } variable "region" { description = "AWS region" default = "us-east-1" } variable "repository_owner" { description = "GitHub repository owner" default = "" } variable "build_repository_name" { description = "GitHub repository name" default = "modelbuild_pipeline" } variable "deploy_repository_name" { description = "GitHub repository name" default = "modeldeploy_pipeline" } variable "artifacts_bucket_name" { description = "S3 Bucket for storing artifacts" default = "" } variable "github_token" { description = "GitHub token" default = "" } ================================================ FILE: samples/s3-cloudfront-static-website/cloudfront.tf ================================================ locals { s3_origin_id = "s3-my-website2023" } resource "aws_cloudfront_origin_access_identity" "origin_access_identity" { comment = "s3-my-website2023" } resource "aws_cloudfront_distribution" "s3_distribution" { origin { domain_name = aws_s3_bucket.mybucket.bucket_regional_domain_name origin_id = local.s3_origin_id s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path } } enabled = true is_ipv6_enabled = true comment = "my-cloudfront" default_root_object = "index.html" # Configure logging here if required #logging_config { # include_cookies = false # bucket = "mylogs.s3.amazonaws.com" # prefix = "myprefix" #} # If you have domain configured use it here #aliases = ["mywebsite.example.com", "s3-static-web-dev.example.com"] default_cache_behavior { allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] cached_methods = ["GET", "HEAD"] target_origin_id = local.s3_origin_id forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "allow-all" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 } # Cache behavior with precedence 0 ordered_cache_behavior { path_pattern = "/content/immutable/*" allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD", "OPTIONS"] target_origin_id = local.s3_origin_id forwarded_values { query_string = false headers = ["Origin"] cookies { forward = "none" } } min_ttl = 0 default_ttl = 86400 max_ttl = 31536000 compress = true viewer_protocol_policy = "redirect-to-https" } # Cache behavior with precedence 1 ordered_cache_behavior { path_pattern = "/content/*" allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = local.s3_origin_id forwarded_values { query_string = false cookies { forward = "none" } } min_ttl = 0 default_ttl = 3600 max_ttl = 86400 compress = true viewer_protocol_policy = "redirect-to-https" } price_class = "PriceClass_200" restrictions { geo_restriction { restriction_type = "whitelist" locations = ["US", "CA", "GB", "DE", "IN", "IR"] } } tags = { Environment = "development" Name = "my-tag" } viewer_certificate { cloudfront_default_certificate = true } } # to get the Cloud front URL if doamin/alias is not configured output "cloudfront_domain_name" { value = aws_cloudfront_distribution.s3_distribution.domain_name } ================================================ FILE: samples/s3-cloudfront-static-website/s3.tf ================================================ terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16" } } required_version = ">= 1.2.0" } provider "aws" { region = "eu-central-1" } resource "aws_s3_bucket" "mybucket" { bucket = "s3-mybucket-website2023" acl = "private" # Add specefic S3 policy in the s3-policy.json on the same directory # policy = file("s3-policy.json") versioning { enabled = false } website { index_document = "index.html" error_document = "error.html" # Add routing rules if required # routing_rules = < code { color: inherit; } kbd { padding: 0.1875rem 0.375rem; font-size: 0.875em; color: var(--bs-body-bg); background-color: var(--bs-body-color); border-radius: 0.25rem; } kbd kbd { padding: 0; font-size: 1em; } figure { margin: 0 0 1rem; } img, svg { vertical-align: middle; } table { caption-side: bottom; border-collapse: collapse; } caption { padding-top: 0.5rem; padding-bottom: 0.5rem; color: #6c757d; text-align: left; } th { text-align: inherit; text-align: -webkit-match-parent; } thead, tbody, tfoot, tr, td, th { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, select { text-transform: none; } [role=button] { cursor: pointer; } select { word-wrap: normal; } select:disabled { opacity: 1; } [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { display: none !important; } button, [type=button], [type=reset], [type=submit] { -webkit-appearance: button; } button:not(:disabled), [type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled) { cursor: pointer; } ::-moz-focus-inner { padding: 0; border-style: none; } textarea { resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { float: left; width: 100%; padding: 0; margin-bottom: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: left; } ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type=search] { outline-offset: -2px; -webkit-appearance: textfield; } /* rtl:raw: [type="tel"], [type="url"], [type="email"], [type="number"] { direction: ltr; } */ ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::file-selector-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } iframe { border: 0; } summary { display: list-item; cursor: pointer; } progress { vertical-align: baseline; } [hidden] { display: none !important; } .lead { font-size: 1.25rem; font-weight: 300; } .display-1 { font-size: calc(1.625rem + 4.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-1 { font-size: 5rem; } } .display-2 { font-size: calc(1.575rem + 3.9vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-2 { font-size: 4.5rem; } } .display-3 { font-size: calc(1.525rem + 3.3vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-3 { font-size: 4rem; } } .display-4 { font-size: calc(1.475rem + 2.7vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-4 { font-size: 3.5rem; } } .display-5 { font-size: calc(1.425rem + 2.1vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-5 { font-size: 3rem; } } .display-6 { font-size: calc(1.375rem + 1.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-6 { font-size: 2.5rem; } } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline-item { display: inline-block; } .list-inline-item:not(:last-child) { margin-right: 0.5rem; } .initialism { font-size: 0.875em; text-transform: uppercase; } .blockquote { margin-bottom: 1rem; font-size: 1.25rem; } .blockquote > :last-child { margin-bottom: 0; } .blockquote-footer { margin-top: -1rem; margin-bottom: 1rem; font-size: 0.875em; color: #6c757d; } .blockquote-footer::before { content: "— "; } .img-fluid { max-width: 100%; height: auto; } .img-thumbnail { padding: 0.25rem; background-color: #fff; border: 0.125rem solid var(--bs-border-color); border-radius: 0.5rem; max-width: 100%; height: auto; } .figure { display: inline-block; } .figure-img { margin-bottom: 0.5rem; line-height: 1; } .figure-caption { font-size: 0.875em; color: #6c757d; } .container, .container-fluid, .container-xxl, .container-xl, .container-lg, .container-md, .container-sm { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-right: auto; margin-left: auto; } @media (min-width: 576px) { .container-sm, .container { max-width: 540px; } } @media (min-width: 768px) { .container-md, .container-sm, .container { max-width: 720px; } } @media (min-width: 992px) { .container-lg, .container-md, .container-sm, .container { max-width: 960px; } } @media (min-width: 1200px) { .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1140px; } } @media (min-width: 1400px) { .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { max-width: 1320px; } } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); } .row > * { flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .col { flex: 1 0 0%; } .row-cols-auto > * { flex: 0 0 auto; width: auto; } .row-cols-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-auto { flex: 0 0 auto; width: auto; } .col-1 { flex: 0 0 auto; width: 8.33333333%; } .col-2 { flex: 0 0 auto; width: 16.66666667%; } .col-3 { flex: 0 0 auto; width: 25%; } .col-4 { flex: 0 0 auto; width: 33.33333333%; } .col-5 { flex: 0 0 auto; width: 41.66666667%; } .col-6 { flex: 0 0 auto; width: 50%; } .col-7 { flex: 0 0 auto; width: 58.33333333%; } .col-8 { flex: 0 0 auto; width: 66.66666667%; } .col-9 { flex: 0 0 auto; width: 75%; } .col-10 { flex: 0 0 auto; width: 83.33333333%; } .col-11 { flex: 0 0 auto; width: 91.66666667%; } .col-12 { flex: 0 0 auto; width: 100%; } .offset-1 { margin-left: 8.33333333%; } .offset-2 { margin-left: 16.66666667%; } .offset-3 { margin-left: 25%; } .offset-4 { margin-left: 33.33333333%; } .offset-5 { margin-left: 41.66666667%; } .offset-6 { margin-left: 50%; } .offset-7 { margin-left: 58.33333333%; } .offset-8 { margin-left: 66.66666667%; } .offset-9 { margin-left: 75%; } .offset-10 { margin-left: 83.33333333%; } .offset-11 { margin-left: 91.66666667%; } .g-0, .gx-0 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @media (min-width: 576px) { .col-sm { flex: 1 0 0%; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } .row-cols-sm-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-sm-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-sm-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-sm-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-sm-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-sm-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-sm-auto { flex: 0 0 auto; width: auto; } .col-sm-1 { flex: 0 0 auto; width: 8.33333333%; } .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; } .col-sm-3 { flex: 0 0 auto; width: 25%; } .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; } .col-sm-5 { flex: 0 0 auto; width: 41.66666667%; } .col-sm-6 { flex: 0 0 auto; width: 50%; } .col-sm-7 { flex: 0 0 auto; width: 58.33333333%; } .col-sm-8 { flex: 0 0 auto; width: 66.66666667%; } .col-sm-9 { flex: 0 0 auto; width: 75%; } .col-sm-10 { flex: 0 0 auto; width: 83.33333333%; } .col-sm-11 { flex: 0 0 auto; width: 91.66666667%; } .col-sm-12 { flex: 0 0 auto; width: 100%; } .offset-sm-0 { margin-left: 0; } .offset-sm-1 { margin-left: 8.33333333%; } .offset-sm-2 { margin-left: 16.66666667%; } .offset-sm-3 { margin-left: 25%; } .offset-sm-4 { margin-left: 33.33333333%; } .offset-sm-5 { margin-left: 41.66666667%; } .offset-sm-6 { margin-left: 50%; } .offset-sm-7 { margin-left: 58.33333333%; } .offset-sm-8 { margin-left: 66.66666667%; } .offset-sm-9 { margin-left: 75%; } .offset-sm-10 { margin-left: 83.33333333%; } .offset-sm-11 { margin-left: 91.66666667%; } .g-sm-0, .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @media (min-width: 768px) { .col-md { flex: 1 0 0%; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } .row-cols-md-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-md-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-md-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-md-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-md-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-md-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-md-auto { flex: 0 0 auto; width: auto; } .col-md-1 { flex: 0 0 auto; width: 8.33333333%; } .col-md-2 { flex: 0 0 auto; width: 16.66666667%; } .col-md-3 { flex: 0 0 auto; width: 25%; } .col-md-4 { flex: 0 0 auto; width: 33.33333333%; } .col-md-5 { flex: 0 0 auto; width: 41.66666667%; } .col-md-6 { flex: 0 0 auto; width: 50%; } .col-md-7 { flex: 0 0 auto; width: 58.33333333%; } .col-md-8 { flex: 0 0 auto; width: 66.66666667%; } .col-md-9 { flex: 0 0 auto; width: 75%; } .col-md-10 { flex: 0 0 auto; width: 83.33333333%; } .col-md-11 { flex: 0 0 auto; width: 91.66666667%; } .col-md-12 { flex: 0 0 auto; width: 100%; } .offset-md-0 { margin-left: 0; } .offset-md-1 { margin-left: 8.33333333%; } .offset-md-2 { margin-left: 16.66666667%; } .offset-md-3 { margin-left: 25%; } .offset-md-4 { margin-left: 33.33333333%; } .offset-md-5 { margin-left: 41.66666667%; } .offset-md-6 { margin-left: 50%; } .offset-md-7 { margin-left: 58.33333333%; } .offset-md-8 { margin-left: 66.66666667%; } .offset-md-9 { margin-left: 75%; } .offset-md-10 { margin-left: 83.33333333%; } .offset-md-11 { margin-left: 91.66666667%; } .g-md-0, .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @media (min-width: 992px) { .col-lg { flex: 1 0 0%; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } .row-cols-lg-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-lg-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-lg-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-lg-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-lg-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-lg-auto { flex: 0 0 auto; width: auto; } .col-lg-1 { flex: 0 0 auto; width: 8.33333333%; } .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; } .col-lg-3 { flex: 0 0 auto; width: 25%; } .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; } .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; } .col-lg-6 { flex: 0 0 auto; width: 50%; } .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; } .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; } .col-lg-9 { flex: 0 0 auto; width: 75%; } .col-lg-10 { flex: 0 0 auto; width: 83.33333333%; } .col-lg-11 { flex: 0 0 auto; width: 91.66666667%; } .col-lg-12 { flex: 0 0 auto; width: 100%; } .offset-lg-0 { margin-left: 0; } .offset-lg-1 { margin-left: 8.33333333%; } .offset-lg-2 { margin-left: 16.66666667%; } .offset-lg-3 { margin-left: 25%; } .offset-lg-4 { margin-left: 33.33333333%; } .offset-lg-5 { margin-left: 41.66666667%; } .offset-lg-6 { margin-left: 50%; } .offset-lg-7 { margin-left: 58.33333333%; } .offset-lg-8 { margin-left: 66.66666667%; } .offset-lg-9 { margin-left: 75%; } .offset-lg-10 { margin-left: 83.33333333%; } .offset-lg-11 { margin-left: 91.66666667%; } .g-lg-0, .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1200px) { .col-xl { flex: 1 0 0%; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xl-auto { flex: 0 0 auto; width: auto; } .col-xl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xl-3 { flex: 0 0 auto; width: 25%; } .col-xl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xl-6 { flex: 0 0 auto; width: 50%; } .col-xl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xl-9 { flex: 0 0 auto; width: 75%; } .col-xl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xl-12 { flex: 0 0 auto; width: 100%; } .offset-xl-0 { margin-left: 0; } .offset-xl-1 { margin-left: 8.33333333%; } .offset-xl-2 { margin-left: 16.66666667%; } .offset-xl-3 { margin-left: 25%; } .offset-xl-4 { margin-left: 33.33333333%; } .offset-xl-5 { margin-left: 41.66666667%; } .offset-xl-6 { margin-left: 50%; } .offset-xl-7 { margin-left: 58.33333333%; } .offset-xl-8 { margin-left: 66.66666667%; } .offset-xl-9 { margin-left: 75%; } .offset-xl-10 { margin-left: 83.33333333%; } .offset-xl-11 { margin-left: 91.66666667%; } .g-xl-0, .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @media (min-width: 1400px) { .col-xxl { flex: 1 0 0%; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } .row-cols-xxl-1 > * { flex: 0 0 auto; width: 100%; } .row-cols-xxl-2 > * { flex: 0 0 auto; width: 50%; } .row-cols-xxl-3 > * { flex: 0 0 auto; width: 33.3333333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; width: 25%; } .row-cols-xxl-5 > * { flex: 0 0 auto; width: 20%; } .row-cols-xxl-6 > * { flex: 0 0 auto; width: 16.6666666667%; } .col-xxl-auto { flex: 0 0 auto; width: auto; } .col-xxl-1 { flex: 0 0 auto; width: 8.33333333%; } .col-xxl-2 { flex: 0 0 auto; width: 16.66666667%; } .col-xxl-3 { flex: 0 0 auto; width: 25%; } .col-xxl-4 { flex: 0 0 auto; width: 33.33333333%; } .col-xxl-5 { flex: 0 0 auto; width: 41.66666667%; } .col-xxl-6 { flex: 0 0 auto; width: 50%; } .col-xxl-7 { flex: 0 0 auto; width: 58.33333333%; } .col-xxl-8 { flex: 0 0 auto; width: 66.66666667%; } .col-xxl-9 { flex: 0 0 auto; width: 75%; } .col-xxl-10 { flex: 0 0 auto; width: 83.33333333%; } .col-xxl-11 { flex: 0 0 auto; width: 91.66666667%; } .col-xxl-12 { flex: 0 0 auto; width: 100%; } .offset-xxl-0 { margin-left: 0; } .offset-xxl-1 { margin-left: 8.33333333%; } .offset-xxl-2 { margin-left: 16.66666667%; } .offset-xxl-3 { margin-left: 25%; } .offset-xxl-4 { margin-left: 33.33333333%; } .offset-xxl-5 { margin-left: 41.66666667%; } .offset-xxl-6 { margin-left: 50%; } .offset-xxl-7 { margin-left: 58.33333333%; } .offset-xxl-8 { margin-left: 66.66666667%; } .offset-xxl-9 { margin-left: 75%; } .offset-xxl-10 { margin-left: 83.33333333%; } .offset-xxl-11 { margin-left: 91.66666667%; } .g-xxl-0, .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .table { --bs-table-color: var(--bs-body-color); --bs-table-bg: transparent; --bs-table-border-color: var(--bs-border-color); --bs-table-accent-bg: transparent; --bs-table-striped-color: var(--bs-body-color); --bs-table-striped-bg: rgba(0, 0, 0, 0.05); --bs-table-active-color: var(--bs-body-color); --bs-table-active-bg: rgba(0, 0, 0, 0.1); --bs-table-hover-color: var(--bs-body-color); --bs-table-hover-bg: rgba(0, 0, 0, 0.075); width: 100%; margin-bottom: 1rem; color: var(--bs-table-color); vertical-align: top; border-color: var(--bs-table-border-color); } .table > :not(caption) > * > * { padding: 0.5rem 0.5rem; background-color: var(--bs-table-bg); border-bottom-width: 0.125rem; box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); } .table > tbody { vertical-align: inherit; } .table > thead { vertical-align: bottom; } .table-group-divider { border-top: 0.25rem solid currentcolor; } .caption-top { caption-side: top; } .table-sm > :not(caption) > * > * { padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { border-width: 0.125rem 0; } .table-bordered > :not(caption) > * > * { border-width: 0 0.125rem; } .table-borderless > :not(caption) > * > * { border-bottom-width: 0; } .table-borderless > :not(:first-child) { border-top-width: 0; } .table-striped > tbody > tr:nth-of-type(odd) > * { --bs-table-accent-bg: var(--bs-table-striped-bg); color: var(--bs-table-striped-color); } .table-striped-columns > :not(caption) > tr > :nth-child(even) { --bs-table-accent-bg: var(--bs-table-striped-bg); color: var(--bs-table-striped-color); } .table-active { --bs-table-accent-bg: var(--bs-table-active-bg); color: var(--bs-table-active-color); } .table-hover > tbody > tr:hover > * { --bs-table-accent-bg: var(--bs-table-hover-bg); color: var(--bs-table-hover-color); } .table-primary { --bs-table-color: #000; --bs-table-bg: #d1f2eb; --bs-table-border-color: #bcdad4; --bs-table-striped-bg: #c7e6df; --bs-table-striped-color: #000; --bs-table-active-bg: #bcdad4; --bs-table-active-color: #000; --bs-table-hover-bg: #c1e0d9; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-secondary { --bs-table-color: #000; --bs-table-bg: #d5d8dc; --bs-table-border-color: #c0c2c6; --bs-table-striped-bg: #cacdd1; --bs-table-striped-color: #000; --bs-table-active-bg: #c0c2c6; --bs-table-active-color: #000; --bs-table-hover-bg: #c5c8cc; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-success { --bs-table-color: #000; --bs-table-bg: #d1e7dd; --bs-table-border-color: #bcd0c7; --bs-table-striped-bg: #c7dbd2; --bs-table-striped-color: #000; --bs-table-active-bg: #bcd0c7; --bs-table-active-color: #000; --bs-table-hover-bg: #c1d6cc; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-info { --bs-table-color: #000; --bs-table-bg: #cff4fc; --bs-table-border-color: #badce3; --bs-table-striped-bg: #c5e8ef; --bs-table-striped-color: #000; --bs-table-active-bg: #badce3; --bs-table-active-color: #000; --bs-table-hover-bg: #bfe2e9; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-warning { --bs-table-color: #000; --bs-table-bg: #fff3cd; --bs-table-border-color: #e6dbb9; --bs-table-striped-bg: #f2e7c3; --bs-table-striped-color: #000; --bs-table-active-bg: #e6dbb9; --bs-table-active-color: #000; --bs-table-hover-bg: #ece1be; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-danger { --bs-table-color: #000; --bs-table-bg: #f8d7da; --bs-table-border-color: #dfc2c4; --bs-table-striped-bg: #eccccf; --bs-table-striped-color: #000; --bs-table-active-bg: #dfc2c4; --bs-table-active-color: #000; --bs-table-hover-bg: #e5c7ca; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-light { --bs-table-color: #000; --bs-table-bg: #f8f9fa; --bs-table-border-color: #dfe0e1; --bs-table-striped-bg: #ecedee; --bs-table-striped-color: #000; --bs-table-active-bg: #dfe0e1; --bs-table-active-color: #000; --bs-table-hover-bg: #e5e6e7; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-dark { --bs-table-color: #fff; --bs-table-bg: #212529; --bs-table-border-color: #373b3e; --bs-table-striped-bg: #2c3034; --bs-table-striped-color: #fff; --bs-table-active-bg: #373b3e; --bs-table-active-color: #fff; --bs-table-hover-bg: #323539; --bs-table-hover-color: #fff; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; } @media (max-width: 575.98px) { .table-responsive-sm { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 767.98px) { .table-responsive-md { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 991.98px) { .table-responsive-lg { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1199.98px) { .table-responsive-xl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } @media (max-width: 1399.98px) { .table-responsive-xxl { overflow-x: auto; -webkit-overflow-scrolling: touch; } } .form-label { margin-bottom: 0.5rem; } .col-form-label { padding-top: 0.5rem; padding-bottom: 0.5rem; margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { padding-top: 0.625rem; padding-bottom: 0.625rem; font-size: 1.25rem; } .col-form-label-sm { padding-top: 0.375rem; padding-bottom: 0.375rem; font-size: 0.875rem; } .form-text { margin-top: 0.25rem; font-size: 0.875em; color: #6c757d; } .form-control { display: block; width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-clip: padding-box; border: 0.125rem solid #ced4da; -webkit-appearance: none; -moz-appearance: none; appearance: none; border-radius: 0.5rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control { transition: none; } } .form-control[type=file] { overflow: hidden; } .form-control[type=file]:not(:disabled):not([readonly]) { cursor: pointer; } .form-control:focus { color: #212529; background-color: #fff; border-color: #8ddece; outline: 0; box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); } .form-control::-webkit-date-and-time-value { height: 1.5em; } .form-control::-moz-placeholder { color: #6c757d; opacity: 1; } .form-control::placeholder { color: #6c757d; opacity: 1; } .form-control:disabled { background-color: #e9ecef; opacity: 1; } .form-control::file-selector-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: #212529; background-color: #e9ecef; pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: 0.125rem; border-radius: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::file-selector-button { transition: none; } } .form-control:hover:not(:disabled):not([readonly])::file-selector-button { background-color: #dde0e3; } .form-control-plaintext { display: block; width: 100%; padding: 0.375rem 0; margin-bottom: 0; line-height: 1.5; color: #212529; background-color: transparent; border: solid transparent; border-width: 0.125rem 0; } .form-control-plaintext:focus { outline: 0; } .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { padding-right: 0; padding-left: 0; } .form-control-sm { min-height: calc(1.5em + 0.75rem); padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.25rem; } .form-control-sm::file-selector-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-lg { min-height: calc(1.5em + 1.25rem); padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.75rem; } .form-control-lg::file-selector-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } textarea.form-control { min-height: calc(1.5em + 1rem); } textarea.form-control-sm { min-height: calc(1.5em + 0.75rem); } textarea.form-control-lg { min-height: calc(1.5em + 1.25rem); } .form-control-color { width: 3rem; height: calc(1.5em + 1rem); padding: 0.375rem; } .form-control-color:not(:disabled):not([readonly]) { cursor: pointer; } .form-control-color::-moz-color-swatch { border: 0 !important; border-radius: 0.5rem; } .form-control-color::-webkit-color-swatch { border-radius: 0.5rem; } .form-control-color.form-control-sm { height: calc(1.5em + 0.75rem); } .form-control-color.form-control-lg { height: calc(1.5em + 1.25rem); } .form-select { display: block; width: 100%; padding: 0.375rem 2.25rem 0.375rem 0.75rem; -moz-padding-start: calc(0.75rem - 3px); font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; background-color: #fff; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; border: 0.125rem solid #ced4da; border-radius: 0.5rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-select { transition: none; } } .form-select:focus { border-color: #8ddece; outline: 0; box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); } .form-select[multiple], .form-select[size]:not([size="1"]) { padding-right: 0.75rem; background-image: none; } .form-select:disabled { background-color: #e9ecef; } .form-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 #212529; } .form-select-sm { padding-top: 0.25rem; padding-bottom: 0.25rem; padding-left: 0.5rem; font-size: 0.875rem; border-radius: 0.25rem; } .form-select-lg { padding-top: 0.5rem; padding-bottom: 0.5rem; padding-left: 1rem; font-size: 1.25rem; border-radius: 0.75rem; } .form-check { display: block; min-height: 1.5rem; padding-left: 1.5em; margin-bottom: 0.125rem; } .form-check .form-check-input { float: left; margin-left: -1.5em; } .form-check-reverse { padding-right: 1.5em; padding-left: 0; text-align: right; } .form-check-reverse .form-check-input { float: right; margin-right: -1.5em; margin-left: 0; } .form-check-input { width: 1em; height: 1em; margin-top: 0.25em; vertical-align: top; background-color: #fff; background-repeat: no-repeat; background-position: center; background-size: contain; border: 1px solid rgba(0, 0, 0, 0.25); -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-print-color-adjust: exact; print-color-adjust: exact; } .form-check-input[type=checkbox] { border-radius: 0.25em; } .form-check-input[type=radio] { border-radius: 50%; } .form-check-input:active { filter: brightness(90%); } .form-check-input:focus { border-color: #8ddece; outline: 0; box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); } .form-check-input:checked { background-color: #1abc9c; border-color: #1abc9c; } .form-check-input:checked[type=checkbox] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } .form-check-input:checked[type=radio] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type=checkbox]:indeterminate { background-color: #1abc9c; border-color: #1abc9c; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } .form-check-input:disabled { pointer-events: none; filter: none; opacity: 0.5; } .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { cursor: default; opacity: 0.5; } .form-switch { padding-left: 2.5em; } .form-switch .form-check-input { width: 2em; margin-left: -2.5em; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); background-position: left center; border-radius: 2em; transition: background-position 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-switch .form-check-input { transition: none; } } .form-switch .form-check-input:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%238ddece'/%3e%3c/svg%3e"); } .form-switch .form-check-input:checked { background-position: right center; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .form-switch.form-check-reverse { padding-right: 2.5em; padding-left: 0; } .form-switch.form-check-reverse .form-check-input { margin-right: -2.5em; margin-left: 0; } .form-check-inline { display: inline-block; margin-right: 1rem; } .btn-check { position: absolute; clip: rect(0, 0, 0, 0); pointer-events: none; } .btn-check[disabled] + .btn, .btn-check:disabled + .btn { pointer-events: none; filter: none; opacity: 0.65; } .form-range { width: 100%; height: 1.5rem; padding: 0; background-color: transparent; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .form-range:focus { outline: 0; } .form-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(26, 188, 156, 0.25); } .form-range:focus::-moz-range-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(26, 188, 156, 0.25); } .form-range::-moz-focus-outer { border: 0; } .form-range::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -0.25rem; background-color: #1abc9c; border: 0; border-radius: 1rem; -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-webkit-slider-thumb { -webkit-transition: none; transition: none; } } .form-range::-webkit-slider-thumb:active { background-color: #baebe1; } .form-range::-webkit-slider-runnable-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range::-moz-range-thumb { width: 1rem; height: 1rem; background-color: #1abc9c; border: 0; border-radius: 1rem; -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-moz-range-thumb { -moz-transition: none; transition: none; } } .form-range::-moz-range-thumb:active { background-color: #baebe1; } .form-range::-moz-range-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: #dee2e6; border-color: transparent; border-radius: 1rem; } .form-range:disabled { pointer-events: none; } .form-range:disabled::-webkit-slider-thumb { background-color: #adb5bd; } .form-range:disabled::-moz-range-thumb { background-color: #adb5bd; } .form-floating { position: relative; } .form-floating > .form-control, .form-floating > .form-control-plaintext, .form-floating > .form-select { height: 5.5rem; line-height: 1.25; } .form-floating > label { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 1.5rem 0; overflow: hidden; text-align: start; text-overflow: ellipsis; white-space: nowrap; pointer-events: none; border: 0.125rem solid transparent; transform-origin: 0 0; transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-floating > label { transition: none; } } .form-floating > .form-control, .form-floating > .form-control-plaintext { padding: 1.5rem 0; } .form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder { color: transparent; } .form-floating > .form-control::placeholder, .form-floating > .form-control-plaintext::placeholder { color: transparent; } .form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) { padding-top: 2.5rem; padding-bottom: 1.5rem; } .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown), .form-floating > .form-control-plaintext:focus, .form-floating > .form-control-plaintext:not(:placeholder-shown) { padding-top: 2.5rem; padding-bottom: 1.5rem; } .form-floating > .form-control:-webkit-autofill, .form-floating > .form-control-plaintext:-webkit-autofill { padding-top: 2.5rem; padding-bottom: 1.5rem; } .form-floating > .form-select { padding-top: 2.5rem; padding-bottom: 1.5rem; } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { opacity: 0.65; transform: scale(0.65) translateY(-0.5rem) translateX(0rem); } .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-control-plaintext ~ label, .form-floating > .form-select ~ label { opacity: 0.65; transform: scale(0.65) translateY(-0.5rem) translateX(0rem); } .form-floating > .form-control:-webkit-autofill ~ label { opacity: 0.65; transform: scale(0.65) translateY(-0.5rem) translateX(0rem); } .form-floating > .form-control-plaintext ~ label { border-width: 0.125rem 0; } .input-group { position: relative; display: flex; flex-wrap: wrap; align-items: stretch; width: 100%; } .input-group > .form-control, .input-group > .form-select, .input-group > .form-floating { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; } .input-group > .form-control:focus, .input-group > .form-select:focus, .input-group > .form-floating:focus-within { z-index: 5; } .input-group .btn { position: relative; z-index: 2; } .input-group .btn:focus { z-index: 5; } .input-group-text { display: flex; align-items: center; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; text-align: center; white-space: nowrap; background-color: #e9ecef; border: 0.125rem solid #ced4da; border-radius: 0.5rem; } .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text, .input-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: 0.75rem; } .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text, .input-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.25rem; } .input-group-lg > .form-select, .input-group-sm > .form-select { padding-right: 3rem; } .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3), .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control, .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group.has-validation > .dropdown-toggle:nth-last-child(n+4), .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control, .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { margin-left: -0.125rem; border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group > .form-floating:not(:first-child) > .form-control, .input-group > .form-floating:not(:first-child) > .form-select { border-top-left-radius: 0; border-bottom-left-radius: 0; } .valid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #198754; } .valid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(25, 135, 84, 0.9); border-radius: 0.5rem; } .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip, .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip { display: block; } .was-validated .form-control:valid, .form-control.is-valid { border-color: #198754; padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:valid, .form-select.is-valid { border-color: #198754; } .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { padding-right: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:valid:focus, .form-select.is-valid:focus { border-color: #198754; box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-control-color:valid, .form-control-color.is-valid { width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:valid, .form-check-input.is-valid { border-color: #198754; } .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { background-color: #198754; } .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #198754; } .form-check-inline .form-check-input ~ .valid-feedback { margin-left: 0.5em; } .was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid, .was-validated .input-group > .form-select:not(:focus):valid, .input-group > .form-select:not(:focus).is-valid, .was-validated .input-group > .form-floating:not(:focus-within):valid, .input-group > .form-floating:not(:focus-within).is-valid { z-index: 3; } .invalid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: #dc3545; } .invalid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: rgba(220, 53, 69, 0.9); border-radius: 0.5rem; } .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip, .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip { display: block; } .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:invalid, .form-select.is-invalid { border-color: #dc3545; } .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { padding-right: 4.125rem; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-control-color:invalid, .form-control-color.is-invalid { width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { border-color: #dc3545; } .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { background-color: #dc3545; } .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } .form-check-inline .form-check-input ~ .invalid-feedback { margin-left: 0.5em; } .was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid, .was-validated .input-group > .form-select:not(:focus):invalid, .input-group > .form-select:not(:focus).is-invalid, .was-validated .input-group > .form-floating:not(:focus-within):invalid, .input-group > .form-floating:not(:focus-within).is-invalid { z-index: 4; } .btn { --bs-btn-padding-x: 0.75rem; --bs-btn-padding-y: 0.375rem; --bs-btn-font-family: ; --bs-btn-font-size: 1rem; --bs-btn-font-weight: 400; --bs-btn-line-height: 1.5; --bs-btn-color: #212529; --bs-btn-bg: transparent; --bs-btn-border-width: 0.125rem; --bs-btn-border-color: transparent; --bs-btn-border-radius: 0.5rem; --bs-btn-hover-border-color: transparent; --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); --bs-btn-disabled-opacity: 0.65; --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); display: inline-block; padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); font-family: var(--bs-btn-font-family); font-size: var(--bs-btn-font-size); font-weight: var(--bs-btn-font-weight); line-height: var(--bs-btn-line-height); color: var(--bs-btn-color); text-align: center; text-decoration: none; vertical-align: middle; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); border-radius: var(--bs-btn-border-radius); background-color: var(--bs-btn-bg); transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .btn { transition: none; } } .btn:hover { color: var(--bs-btn-hover-color); background-color: var(--bs-btn-hover-bg); border-color: var(--bs-btn-hover-border-color); } .btn-check + .btn:hover { color: var(--bs-btn-color); background-color: var(--bs-btn-bg); border-color: var(--bs-btn-border-color); } .btn:focus-visible { color: var(--bs-btn-hover-color); background-color: var(--bs-btn-hover-bg); border-color: var(--bs-btn-hover-border-color); outline: 0; box-shadow: var(--bs-btn-focus-box-shadow); } .btn-check:focus-visible + .btn { border-color: var(--bs-btn-hover-border-color); outline: 0; box-shadow: var(--bs-btn-focus-box-shadow); } .btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show { color: var(--bs-btn-active-color); background-color: var(--bs-btn-active-bg); border-color: var(--bs-btn-active-border-color); } .btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible { box-shadow: var(--bs-btn-focus-box-shadow); } .btn:disabled, .btn.disabled, fieldset:disabled .btn { color: var(--bs-btn-disabled-color); pointer-events: none; background-color: var(--bs-btn-disabled-bg); border-color: var(--bs-btn-disabled-border-color); opacity: var(--bs-btn-disabled-opacity); } .btn-primary { --bs-btn-color: #fff; --bs-btn-bg: #1abc9c; --bs-btn-border-color: #1abc9c; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #16a085; --bs-btn-hover-border-color: #15967d; --bs-btn-focus-shadow-rgb: 60, 198, 171; --bs-btn-active-color: #fff; --bs-btn-active-bg: #15967d; --bs-btn-active-border-color: #148d75; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #1abc9c; --bs-btn-disabled-border-color: #1abc9c; } .btn-secondary { --bs-btn-color: #fff; --bs-btn-bg: #2c3e50; --bs-btn-border-color: #2c3e50; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #253544; --bs-btn-hover-border-color: #233240; --bs-btn-focus-shadow-rgb: 76, 91, 106; --bs-btn-active-color: #fff; --bs-btn-active-bg: #233240; --bs-btn-active-border-color: #212f3c; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #2c3e50; --bs-btn-disabled-border-color: #2c3e50; } .btn-success { --bs-btn-color: #fff; --bs-btn-bg: #198754; --bs-btn-border-color: #198754; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #157347; --bs-btn-hover-border-color: #146c43; --bs-btn-focus-shadow-rgb: 60, 153, 110; --bs-btn-active-color: #fff; --bs-btn-active-bg: #146c43; --bs-btn-active-border-color: #13653f; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #198754; --bs-btn-disabled-border-color: #198754; } .btn-info { --bs-btn-color: #000; --bs-btn-bg: #0dcaf0; --bs-btn-border-color: #0dcaf0; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #31d2f2; --bs-btn-hover-border-color: #25cff2; --bs-btn-focus-shadow-rgb: 11, 172, 204; --bs-btn-active-color: #000; --bs-btn-active-bg: #3dd5f3; --bs-btn-active-border-color: #25cff2; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #0dcaf0; --bs-btn-disabled-border-color: #0dcaf0; } .btn-warning { --bs-btn-color: #000; --bs-btn-bg: #ffc107; --bs-btn-border-color: #ffc107; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #ffca2c; --bs-btn-hover-border-color: #ffc720; --bs-btn-focus-shadow-rgb: 217, 164, 6; --bs-btn-active-color: #000; --bs-btn-active-bg: #ffcd39; --bs-btn-active-border-color: #ffc720; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #ffc107; --bs-btn-disabled-border-color: #ffc107; } .btn-danger { --bs-btn-color: #fff; --bs-btn-bg: #dc3545; --bs-btn-border-color: #dc3545; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #bb2d3b; --bs-btn-hover-border-color: #b02a37; --bs-btn-focus-shadow-rgb: 225, 83, 97; --bs-btn-active-color: #fff; --bs-btn-active-bg: #b02a37; --bs-btn-active-border-color: #a52834; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #dc3545; --bs-btn-disabled-border-color: #dc3545; } .btn-light { --bs-btn-color: #000; --bs-btn-bg: #f8f9fa; --bs-btn-border-color: #f8f9fa; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #d3d4d5; --bs-btn-hover-border-color: #c6c7c8; --bs-btn-focus-shadow-rgb: 211, 212, 213; --bs-btn-active-color: #000; --bs-btn-active-bg: #c6c7c8; --bs-btn-active-border-color: #babbbc; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #f8f9fa; --bs-btn-disabled-border-color: #f8f9fa; } .btn-dark { --bs-btn-color: #fff; --bs-btn-bg: #212529; --bs-btn-border-color: #212529; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #424649; --bs-btn-hover-border-color: #373b3e; --bs-btn-focus-shadow-rgb: 66, 70, 73; --bs-btn-active-color: #fff; --bs-btn-active-bg: #4d5154; --bs-btn-active-border-color: #373b3e; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #212529; --bs-btn-disabled-border-color: #212529; } .btn-outline-primary { --bs-btn-color: #1abc9c; --bs-btn-border-color: #1abc9c; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #1abc9c; --bs-btn-hover-border-color: #1abc9c; --bs-btn-focus-shadow-rgb: 26, 188, 156; --bs-btn-active-color: #fff; --bs-btn-active-bg: #1abc9c; --bs-btn-active-border-color: #1abc9c; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #1abc9c; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #1abc9c; --bs-gradient: none; } .btn-outline-secondary { --bs-btn-color: #2c3e50; --bs-btn-border-color: #2c3e50; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #2c3e50; --bs-btn-hover-border-color: #2c3e50; --bs-btn-focus-shadow-rgb: 44, 62, 80; --bs-btn-active-color: #fff; --bs-btn-active-bg: #2c3e50; --bs-btn-active-border-color: #2c3e50; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #2c3e50; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #2c3e50; --bs-gradient: none; } .btn-outline-success { --bs-btn-color: #198754; --bs-btn-border-color: #198754; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #198754; --bs-btn-hover-border-color: #198754; --bs-btn-focus-shadow-rgb: 25, 135, 84; --bs-btn-active-color: #fff; --bs-btn-active-bg: #198754; --bs-btn-active-border-color: #198754; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #198754; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #198754; --bs-gradient: none; } .btn-outline-info { --bs-btn-color: #0dcaf0; --bs-btn-border-color: #0dcaf0; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #0dcaf0; --bs-btn-hover-border-color: #0dcaf0; --bs-btn-focus-shadow-rgb: 13, 202, 240; --bs-btn-active-color: #000; --bs-btn-active-bg: #0dcaf0; --bs-btn-active-border-color: #0dcaf0; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #0dcaf0; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #0dcaf0; --bs-gradient: none; } .btn-outline-warning { --bs-btn-color: #ffc107; --bs-btn-border-color: #ffc107; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #ffc107; --bs-btn-hover-border-color: #ffc107; --bs-btn-focus-shadow-rgb: 255, 193, 7; --bs-btn-active-color: #000; --bs-btn-active-bg: #ffc107; --bs-btn-active-border-color: #ffc107; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #ffc107; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #ffc107; --bs-gradient: none; } .btn-outline-danger { --bs-btn-color: #dc3545; --bs-btn-border-color: #dc3545; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #dc3545; --bs-btn-hover-border-color: #dc3545; --bs-btn-focus-shadow-rgb: 220, 53, 69; --bs-btn-active-color: #fff; --bs-btn-active-bg: #dc3545; --bs-btn-active-border-color: #dc3545; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #dc3545; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #dc3545; --bs-gradient: none; } .btn-outline-light { --bs-btn-color: #f8f9fa; --bs-btn-border-color: #f8f9fa; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #f8f9fa; --bs-btn-hover-border-color: #f8f9fa; --bs-btn-focus-shadow-rgb: 248, 249, 250; --bs-btn-active-color: #000; --bs-btn-active-bg: #f8f9fa; --bs-btn-active-border-color: #f8f9fa; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #f8f9fa; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #f8f9fa; --bs-gradient: none; } .btn-outline-dark { --bs-btn-color: #212529; --bs-btn-border-color: #212529; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #212529; --bs-btn-hover-border-color: #212529; --bs-btn-focus-shadow-rgb: 33, 37, 41; --bs-btn-active-color: #fff; --bs-btn-active-bg: #212529; --bs-btn-active-border-color: #212529; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #212529; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #212529; --bs-gradient: none; } .btn-link { --bs-btn-font-weight: 400; --bs-btn-color: var(--bs-link-color); --bs-btn-bg: transparent; --bs-btn-border-color: transparent; --bs-btn-hover-color: var(--bs-link-hover-color); --bs-btn-hover-border-color: transparent; --bs-btn-active-color: var(--bs-link-hover-color); --bs-btn-active-border-color: transparent; --bs-btn-disabled-color: #6c757d; --bs-btn-disabled-border-color: transparent; --bs-btn-box-shadow: none; --bs-btn-focus-shadow-rgb: 60, 198, 171; text-decoration: underline; } .btn-link:focus-visible { color: var(--bs-btn-color); } .btn-link:hover { color: var(--bs-btn-hover-color); } .btn-lg, .btn-group-lg > .btn { --bs-btn-padding-y: 0.5rem; --bs-btn-padding-x: 1rem; --bs-btn-font-size: 1.25rem; --bs-btn-border-radius: 0.75rem; } .btn-sm, .btn-group-sm > .btn { --bs-btn-padding-y: 0.25rem; --bs-btn-padding-x: 0.5rem; --bs-btn-font-size: 0.875rem; --bs-btn-border-radius: 0.25rem; } .fade { transition: opacity 0.15s linear; } @media (prefers-reduced-motion: reduce) { .fade { transition: none; } } .fade:not(.show) { opacity: 0; } .collapse:not(.show) { display: none; } .collapsing { height: 0; overflow: hidden; transition: height 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing { transition: none; } } .collapsing.collapse-horizontal { width: 0; height: auto; transition: width 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing.collapse-horizontal { transition: none; } } .dropup, .dropend, .dropdown, .dropstart, .dropup-center, .dropdown-center { position: relative; } .dropdown-toggle { white-space: nowrap; } .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid; border-right: 0.3em solid transparent; border-bottom: 0; border-left: 0.3em solid transparent; } .dropdown-toggle:empty::after { margin-left: 0; } .dropdown-menu { --bs-dropdown-zindex: 1000; --bs-dropdown-min-width: 10rem; --bs-dropdown-padding-x: 0; --bs-dropdown-padding-y: 0.5rem; --bs-dropdown-spacer: 0.125rem; --bs-dropdown-font-size: 1rem; --bs-dropdown-color: #212529; --bs-dropdown-bg: #fff; --bs-dropdown-border-color: var(--bs-border-color-translucent); --bs-dropdown-border-radius: 0.5rem; --bs-dropdown-border-width: 0.125rem; --bs-dropdown-inner-border-radius: 0.375rem; --bs-dropdown-divider-bg: var(--bs-border-color-translucent); --bs-dropdown-divider-margin-y: 0.5rem; --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-dropdown-link-color: #212529; --bs-dropdown-link-hover-color: #1e2125; --bs-dropdown-link-hover-bg: #e9ecef; --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #1abc9c; --bs-dropdown-link-disabled-color: #adb5bd; --bs-dropdown-item-padding-x: 1rem; --bs-dropdown-item-padding-y: 0.25rem; --bs-dropdown-header-color: #6c757d; --bs-dropdown-header-padding-x: 1rem; --bs-dropdown-header-padding-y: 0.5rem; position: absolute; z-index: var(--bs-dropdown-zindex); display: none; min-width: var(--bs-dropdown-min-width); padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); margin: 0; font-size: var(--bs-dropdown-font-size); color: var(--bs-dropdown-color); text-align: left; list-style: none; background-color: var(--bs-dropdown-bg); background-clip: padding-box; border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); border-radius: var(--bs-dropdown-border-radius); } .dropdown-menu[data-bs-popper] { top: 100%; left: 0; margin-top: var(--bs-dropdown-spacer); } .dropdown-menu-start { --bs-position: start; } .dropdown-menu-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-end { --bs-position: end; } .dropdown-menu-end[data-bs-popper] { right: 0; left: auto; } @media (min-width: 576px) { .dropdown-menu-sm-start { --bs-position: start; } .dropdown-menu-sm-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-sm-end { --bs-position: end; } .dropdown-menu-sm-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 768px) { .dropdown-menu-md-start { --bs-position: start; } .dropdown-menu-md-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-md-end { --bs-position: end; } .dropdown-menu-md-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 992px) { .dropdown-menu-lg-start { --bs-position: start; } .dropdown-menu-lg-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-lg-end { --bs-position: end; } .dropdown-menu-lg-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 1200px) { .dropdown-menu-xl-start { --bs-position: start; } .dropdown-menu-xl-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-xl-end { --bs-position: end; } .dropdown-menu-xl-end[data-bs-popper] { right: 0; left: auto; } } @media (min-width: 1400px) { .dropdown-menu-xxl-start { --bs-position: start; } .dropdown-menu-xxl-start[data-bs-popper] { right: auto; left: 0; } .dropdown-menu-xxl-end { --bs-position: end; } .dropdown-menu-xxl-end[data-bs-popper] { right: 0; left: auto; } } .dropup .dropdown-menu[data-bs-popper] { top: auto; bottom: 100%; margin-top: 0; margin-bottom: var(--bs-dropdown-spacer); } .dropup .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0; border-right: 0.3em solid transparent; border-bottom: 0.3em solid; border-left: 0.3em solid transparent; } .dropup .dropdown-toggle:empty::after { margin-left: 0; } .dropend .dropdown-menu[data-bs-popper] { top: 0; right: auto; left: 100%; margin-top: 0; margin-left: var(--bs-dropdown-spacer); } .dropend .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0; border-bottom: 0.3em solid transparent; border-left: 0.3em solid; } .dropend .dropdown-toggle:empty::after { margin-left: 0; } .dropend .dropdown-toggle::after { vertical-align: 0; } .dropstart .dropdown-menu[data-bs-popper] { top: 0; right: 100%; left: auto; margin-top: 0; margin-right: var(--bs-dropdown-spacer); } .dropstart .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; } .dropstart .dropdown-toggle::after { display: none; } .dropstart .dropdown-toggle::before { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0.3em solid; border-bottom: 0.3em solid transparent; } .dropstart .dropdown-toggle:empty::after { margin-left: 0; } .dropstart .dropdown-toggle::before { vertical-align: 0; } .dropdown-divider { height: 0; margin: var(--bs-dropdown-divider-margin-y) 0; overflow: hidden; border-top: 1px solid var(--bs-dropdown-divider-bg); opacity: 1; } .dropdown-item { display: block; width: 100%; padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); clear: both; font-weight: 400; color: var(--bs-dropdown-link-color); text-align: inherit; text-decoration: none; white-space: nowrap; background-color: transparent; border: 0; } .dropdown-item:hover, .dropdown-item:focus { color: var(--bs-dropdown-link-hover-color); background-color: var(--bs-dropdown-link-hover-bg); } .dropdown-item.active, .dropdown-item:active { color: var(--bs-dropdown-link-active-color); text-decoration: none; background-color: var(--bs-dropdown-link-active-bg); } .dropdown-item.disabled, .dropdown-item:disabled { color: var(--bs-dropdown-link-disabled-color); pointer-events: none; background-color: transparent; } .dropdown-menu.show { display: block; } .dropdown-header { display: block; padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); margin-bottom: 0; font-size: 0.875rem; color: var(--bs-dropdown-header-color); white-space: nowrap; } .dropdown-item-text { display: block; padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); color: var(--bs-dropdown-link-color); } .dropdown-menu-dark { --bs-dropdown-color: #dee2e6; --bs-dropdown-bg: #343a40; --bs-dropdown-border-color: var(--bs-border-color-translucent); --bs-dropdown-box-shadow: ; --bs-dropdown-link-color: #dee2e6; --bs-dropdown-link-hover-color: #fff; --bs-dropdown-divider-bg: var(--bs-border-color-translucent); --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #1abc9c; --bs-dropdown-link-disabled-color: #adb5bd; --bs-dropdown-header-color: #adb5bd; } .btn-group, .btn-group-vertical { position: relative; display: inline-flex; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; flex: 1 1 auto; } .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn:hover, .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn-check:checked + .btn, .btn-group-vertical > .btn-check:focus + .btn, .btn-group-vertical > .btn:hover, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { z-index: 1; } .btn-toolbar { display: flex; flex-wrap: wrap; justify-content: flex-start; } .btn-toolbar .input-group { width: auto; } .btn-group { border-radius: 0.5rem; } .btn-group > :not(.btn-check:first-child) + .btn, .btn-group > .btn-group:not(:first-child) { margin-left: -0.125rem; } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn.dropdown-toggle-split:first-child, .btn-group > .btn-group:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:nth-child(n+3), .btn-group > :not(.btn-check) + .btn, .btn-group > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .dropdown-toggle-split { padding-right: 0.5625rem; padding-left: 0.5625rem; } .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { margin-left: 0; } .dropstart .dropdown-toggle-split::before { margin-right: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { padding-right: 0.375rem; padding-left: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { padding-right: 0.75rem; padding-left: 0.75rem; } .btn-group-vertical { flex-direction: column; align-items: flex-start; justify-content: center; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { width: 100%; } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { margin-top: -0.125rem; } .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn ~ .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-top-right-radius: 0; } .nav { --bs-nav-link-padding-x: 1rem; --bs-nav-link-padding-y: 0.5rem; --bs-nav-link-font-weight: ; --bs-nav-link-color: var(--bs-link-color); --bs-nav-link-hover-color: var(--bs-link-hover-color); --bs-nav-link-disabled-color: #6c757d; display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; } .nav-link { display: block; padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); font-size: var(--bs-nav-link-font-size); font-weight: var(--bs-nav-link-font-weight); color: var(--bs-nav-link-color); text-decoration: none; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .nav-link { transition: none; } } .nav-link:hover, .nav-link:focus { color: var(--bs-nav-link-hover-color); } .nav-link.disabled { color: var(--bs-nav-link-disabled-color); pointer-events: none; cursor: default; } .nav-tabs { --bs-nav-tabs-border-width: 0.125rem; --bs-nav-tabs-border-color: #dee2e6; --bs-nav-tabs-border-radius: 0.5rem; --bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6; --bs-nav-tabs-link-active-color: #495057; --bs-nav-tabs-link-active-bg: #fff; --bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff; border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } .nav-tabs .nav-link { margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); background: none; border: var(--bs-nav-tabs-border-width) solid transparent; border-top-left-radius: var(--bs-nav-tabs-border-radius); border-top-right-radius: var(--bs-nav-tabs-border-radius); } .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { isolation: isolate; border-color: var(--bs-nav-tabs-link-hover-border-color); } .nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled { color: var(--bs-nav-link-disabled-color); background-color: transparent; border-color: transparent; } .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { color: var(--bs-nav-tabs-link-active-color); background-color: var(--bs-nav-tabs-link-active-bg); border-color: var(--bs-nav-tabs-link-active-border-color); } .nav-tabs .dropdown-menu { margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); border-top-left-radius: 0; border-top-right-radius: 0; } .nav-pills { --bs-nav-pills-border-radius: 0.5rem; --bs-nav-pills-link-active-color: #fff; --bs-nav-pills-link-active-bg: #1abc9c; } .nav-pills .nav-link { background: none; border: 0; border-radius: var(--bs-nav-pills-border-radius); } .nav-pills .nav-link:disabled { color: var(--bs-nav-link-disabled-color); background-color: transparent; border-color: transparent; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: var(--bs-nav-pills-link-active-color); background-color: var(--bs-nav-pills-link-active-bg); } .nav-fill > .nav-link, .nav-fill .nav-item { flex: 1 1 auto; text-align: center; } .nav-justified > .nav-link, .nav-justified .nav-item { flex-basis: 0; flex-grow: 1; text-align: center; } .nav-fill .nav-item .nav-link, .nav-justified .nav-item .nav-link { width: 100%; } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .navbar { --bs-navbar-padding-x: 0; --bs-navbar-padding-y: 0.5rem; --bs-navbar-color: rgba(0, 0, 0, 0.55); --bs-navbar-hover-color: rgba(0, 0, 0, 0.7); --bs-navbar-disabled-color: rgba(0, 0, 0, 0.3); --bs-navbar-active-color: rgba(0, 0, 0, 0.9); --bs-navbar-brand-padding-y: 0.3125rem; --bs-navbar-brand-margin-end: 1rem; --bs-navbar-brand-font-size: 1.25rem; --bs-navbar-brand-color: rgba(0, 0, 0, 0.9); --bs-navbar-brand-hover-color: rgba(0, 0, 0, 0.9); --bs-navbar-nav-link-padding-x: 0.5rem; --bs-navbar-toggler-padding-y: 0.25rem; --bs-navbar-toggler-padding-x: 0.75rem; --bs-navbar-toggler-font-size: 1.25rem; --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); --bs-navbar-toggler-border-color: rgba(0, 0, 0, 0.1); --bs-navbar-toggler-border-radius: 0.5rem; --bs-navbar-toggler-focus-width: 0.25rem; --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); } .navbar > .container, .navbar > .container-fluid, .navbar > .container-sm, .navbar > .container-md, .navbar > .container-lg, .navbar > .container-xl, .navbar > .container-xxl { display: flex; flex-wrap: inherit; align-items: center; justify-content: space-between; } .navbar-brand { padding-top: var(--bs-navbar-brand-padding-y); padding-bottom: var(--bs-navbar-brand-padding-y); margin-right: var(--bs-navbar-brand-margin-end); font-size: var(--bs-navbar-brand-font-size); color: var(--bs-navbar-brand-color); text-decoration: none; white-space: nowrap; } .navbar-brand:hover, .navbar-brand:focus { color: var(--bs-navbar-brand-hover-color); } .navbar-nav { --bs-nav-link-padding-x: 0; --bs-nav-link-padding-y: 0.5rem; --bs-nav-link-font-weight: ; --bs-nav-link-color: var(--bs-navbar-color); --bs-nav-link-hover-color: var(--bs-navbar-hover-color); --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; list-style: none; } .navbar-nav .show > .nav-link, .navbar-nav .nav-link.active { color: var(--bs-navbar-active-color); } .navbar-nav .dropdown-menu { position: static; } .navbar-text { padding-top: 0.5rem; padding-bottom: 0.5rem; color: var(--bs-navbar-color); } .navbar-text a, .navbar-text a:hover, .navbar-text a:focus { color: var(--bs-navbar-active-color); } .navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; } .navbar-toggler { padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); font-size: var(--bs-navbar-toggler-font-size); line-height: 1; color: var(--bs-navbar-color); background-color: transparent; border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); border-radius: var(--bs-navbar-toggler-border-radius); transition: var(--bs-navbar-toggler-transition); } @media (prefers-reduced-motion: reduce) { .navbar-toggler { transition: none; } } .navbar-toggler:hover { text-decoration: none; } .navbar-toggler:focus { text-decoration: none; outline: 0; box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); } .navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; background-image: var(--bs-navbar-toggler-icon-bg); background-repeat: no-repeat; background-position: center; background-size: 100%; } .navbar-nav-scroll { max-height: var(--bs-scroll-height, 75vh); overflow-y: auto; } @media (min-width: 576px) { .navbar-expand-sm { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-sm .navbar-nav { flex-direction: row; } .navbar-expand-sm .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-sm .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand-sm .navbar-nav-scroll { overflow: visible; } .navbar-expand-sm .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-sm .navbar-toggler { display: none; } .navbar-expand-sm .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand-sm .offcanvas .offcanvas-header { display: none; } .navbar-expand-sm .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 768px) { .navbar-expand-md { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-md .navbar-nav { flex-direction: row; } .navbar-expand-md .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-md .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand-md .navbar-nav-scroll { overflow: visible; } .navbar-expand-md .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-md .navbar-toggler { display: none; } .navbar-expand-md .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand-md .offcanvas .offcanvas-header { display: none; } .navbar-expand-md .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 992px) { .navbar-expand-lg { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-lg .navbar-nav { flex-direction: row; } .navbar-expand-lg .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-lg .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand-lg .navbar-nav-scroll { overflow: visible; } .navbar-expand-lg .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-lg .navbar-toggler { display: none; } .navbar-expand-lg .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand-lg .offcanvas .offcanvas-header { display: none; } .navbar-expand-lg .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1200px) { .navbar-expand-xl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xl .navbar-nav { flex-direction: row; } .navbar-expand-xl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xl .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand-xl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xl .navbar-toggler { display: none; } .navbar-expand-xl .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand-xl .offcanvas .offcanvas-header { display: none; } .navbar-expand-xl .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } @media (min-width: 1400px) { .navbar-expand-xxl { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand-xxl .navbar-nav { flex-direction: row; } .navbar-expand-xxl .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand-xxl .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand-xxl .navbar-nav-scroll { overflow: visible; } .navbar-expand-xxl .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand-xxl .navbar-toggler { display: none; } .navbar-expand-xxl .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand-xxl .offcanvas .offcanvas-header { display: none; } .navbar-expand-xxl .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } } .navbar-expand { flex-wrap: nowrap; justify-content: flex-start; } .navbar-expand .navbar-nav { flex-direction: row; } .navbar-expand .navbar-nav .dropdown-menu { position: absolute; } .navbar-expand .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); } .navbar-expand .navbar-nav-scroll { overflow: visible; } .navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; } .navbar-expand .navbar-toggler { display: none; } .navbar-expand .offcanvas { position: static; z-index: auto; flex-grow: 1; width: auto !important; height: auto !important; visibility: visible !important; background-color: transparent !important; border: 0 !important; transform: none !important; transition: none; } .navbar-expand .offcanvas .offcanvas-header { display: none; } .navbar-expand .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; } .navbar-dark { --bs-navbar-color: rgba(255, 255, 255, 0.55); --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); --bs-navbar-active-color: #fff; --bs-navbar-brand-color: #fff; --bs-navbar-brand-hover-color: #fff; --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .card { --bs-card-spacer-y: 1rem; --bs-card-spacer-x: 1rem; --bs-card-title-spacer-y: 0.5rem; --bs-card-border-width: 0.125rem; --bs-card-border-color: var(--bs-border-color-translucent); --bs-card-border-radius: 0.5rem; --bs-card-box-shadow: ; --bs-card-inner-border-radius: 0.375rem; --bs-card-cap-padding-y: 0.5rem; --bs-card-cap-padding-x: 1rem; --bs-card-cap-bg: rgba(0, 0, 0, 0.03); --bs-card-cap-color: ; --bs-card-height: ; --bs-card-color: ; --bs-card-bg: #fff; --bs-card-img-overlay-padding: 1rem; --bs-card-group-margin: 0.75rem; position: relative; display: flex; flex-direction: column; min-width: 0; height: var(--bs-card-height); word-wrap: break-word; background-color: var(--bs-card-bg); background-clip: border-box; border: var(--bs-card-border-width) solid var(--bs-card-border-color); border-radius: var(--bs-card-border-radius); } .card > hr { margin-right: 0; margin-left: 0; } .card > .list-group { border-top: inherit; border-bottom: inherit; } .card > .list-group:first-child { border-top-width: 0; border-top-left-radius: var(--bs-card-inner-border-radius); border-top-right-radius: var(--bs-card-inner-border-radius); } .card > .list-group:last-child { border-bottom-width: 0; border-bottom-right-radius: var(--bs-card-inner-border-radius); border-bottom-left-radius: var(--bs-card-inner-border-radius); } .card > .card-header + .list-group, .card > .list-group + .card-footer { border-top: 0; } .card-body { flex: 1 1 auto; padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); color: var(--bs-card-color); } .card-title { margin-bottom: var(--bs-card-title-spacer-y); } .card-subtitle { margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); margin-bottom: 0; } .card-text:last-child { margin-bottom: 0; } .card-link + .card-link { margin-left: var(--bs-card-spacer-x); } .card-header { padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); margin-bottom: 0; color: var(--bs-card-cap-color); background-color: var(--bs-card-cap-bg); border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); } .card-header:first-child { border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; } .card-footer { padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); color: var(--bs-card-cap-color); background-color: var(--bs-card-cap-bg); border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); } .card-footer:last-child { border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); } .card-header-tabs { margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); border-bottom: 0; } .card-header-tabs .nav-link.active { background-color: var(--bs-card-bg); border-bottom-color: var(--bs-card-bg); } .card-header-pills { margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); } .card-img-overlay { position: absolute; top: 0; right: 0; bottom: 0; left: 0; padding: var(--bs-card-img-overlay-padding); border-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-top, .card-img-bottom { width: 100%; } .card-img, .card-img-top { border-top-left-radius: var(--bs-card-inner-border-radius); border-top-right-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-bottom { border-bottom-right-radius: var(--bs-card-inner-border-radius); border-bottom-left-radius: var(--bs-card-inner-border-radius); } .card-group > .card { margin-bottom: var(--bs-card-group-margin); } @media (min-width: 576px) { .card-group { display: flex; flex-flow: row wrap; } .card-group > .card { flex: 1 0 0%; margin-bottom: 0; } .card-group > .card + .card { margin-left: 0; border-left: 0; } .card-group > .card:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-top, .card-group > .card:not(:last-child) .card-header { border-top-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-bottom, .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } .card-group > .card:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-top, .card-group > .card:not(:first-child) .card-header { border-top-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-bottom, .card-group > .card:not(:first-child) .card-footer { border-bottom-left-radius: 0; } } .accordion { --bs-accordion-color: #212529; --bs-accordion-bg: #fff; --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; --bs-accordion-border-color: var(--bs-border-color); --bs-accordion-border-width: 0.125rem; --bs-accordion-border-radius: 0.5rem; --bs-accordion-inner-border-radius: 0.375rem; --bs-accordion-btn-padding-x: 1.25rem; --bs-accordion-btn-padding-y: 1rem; --bs-accordion-btn-color: #212529; --bs-accordion-btn-bg: var(--bs-accordion-bg); --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); --bs-accordion-btn-icon-width: 1.25rem; --bs-accordion-btn-icon-transform: rotate(-180deg); --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2317a98c'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); --bs-accordion-btn-focus-border-color: #8ddece; --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); --bs-accordion-body-padding-x: 1.25rem; --bs-accordion-body-padding-y: 1rem; --bs-accordion-active-color: #17a98c; --bs-accordion-active-bg: #e8f8f5; } .accordion-button { position: relative; display: flex; align-items: center; width: 100%; padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); font-size: 1rem; color: var(--bs-accordion-btn-color); text-align: left; background-color: var(--bs-accordion-btn-bg); border: 0; border-radius: 0; overflow-anchor: none; transition: var(--bs-accordion-transition); } @media (prefers-reduced-motion: reduce) { .accordion-button { transition: none; } } .accordion-button:not(.collapsed) { color: var(--bs-accordion-active-color); background-color: var(--bs-accordion-active-bg); box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); } .accordion-button:not(.collapsed)::after { background-image: var(--bs-accordion-btn-active-icon); transform: var(--bs-accordion-btn-icon-transform); } .accordion-button::after { flex-shrink: 0; width: var(--bs-accordion-btn-icon-width); height: var(--bs-accordion-btn-icon-width); margin-left: auto; content: ""; background-image: var(--bs-accordion-btn-icon); background-repeat: no-repeat; background-size: var(--bs-accordion-btn-icon-width); transition: var(--bs-accordion-btn-icon-transition); } @media (prefers-reduced-motion: reduce) { .accordion-button::after { transition: none; } } .accordion-button:hover { z-index: 2; } .accordion-button:focus { z-index: 3; border-color: var(--bs-accordion-btn-focus-border-color); outline: 0; box-shadow: var(--bs-accordion-btn-focus-box-shadow); } .accordion-header { margin-bottom: 0; } .accordion-item { color: var(--bs-accordion-color); background-color: var(--bs-accordion-bg); border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); } .accordion-item:first-of-type { border-top-left-radius: var(--bs-accordion-border-radius); border-top-right-radius: var(--bs-accordion-border-radius); } .accordion-item:first-of-type .accordion-button { border-top-left-radius: var(--bs-accordion-inner-border-radius); border-top-right-radius: var(--bs-accordion-inner-border-radius); } .accordion-item:not(:first-of-type) { border-top: 0; } .accordion-item:last-of-type { border-bottom-right-radius: var(--bs-accordion-border-radius); border-bottom-left-radius: var(--bs-accordion-border-radius); } .accordion-item:last-of-type .accordion-button.collapsed { border-bottom-right-radius: var(--bs-accordion-inner-border-radius); border-bottom-left-radius: var(--bs-accordion-inner-border-radius); } .accordion-item:last-of-type .accordion-collapse { border-bottom-right-radius: var(--bs-accordion-border-radius); border-bottom-left-radius: var(--bs-accordion-border-radius); } .accordion-body { padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); } .accordion-flush .accordion-collapse { border-width: 0; } .accordion-flush .accordion-item { border-right: 0; border-left: 0; border-radius: 0; } .accordion-flush .accordion-item:first-child { border-top: 0; } .accordion-flush .accordion-item:last-child { border-bottom: 0; } .accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed { border-radius: 0; } .breadcrumb { --bs-breadcrumb-padding-x: 0; --bs-breadcrumb-padding-y: 0; --bs-breadcrumb-margin-bottom: 1rem; --bs-breadcrumb-bg: ; --bs-breadcrumb-border-radius: ; --bs-breadcrumb-divider-color: #6c757d; --bs-breadcrumb-item-padding-x: 0.5rem; --bs-breadcrumb-item-active-color: #6c757d; display: flex; flex-wrap: wrap; padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); margin-bottom: var(--bs-breadcrumb-margin-bottom); font-size: var(--bs-breadcrumb-font-size); list-style: none; background-color: var(--bs-breadcrumb-bg); border-radius: var(--bs-breadcrumb-border-radius); } .breadcrumb-item + .breadcrumb-item { padding-left: var(--bs-breadcrumb-item-padding-x); } .breadcrumb-item + .breadcrumb-item::before { float: left; padding-right: var(--bs-breadcrumb-item-padding-x); color: var(--bs-breadcrumb-divider-color); content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; } .breadcrumb-item.active { color: var(--bs-breadcrumb-item-active-color); } .pagination { --bs-pagination-padding-x: 0.75rem; --bs-pagination-padding-y: 0.375rem; --bs-pagination-font-size: 1rem; --bs-pagination-color: var(--bs-link-color); --bs-pagination-bg: #fff; --bs-pagination-border-width: 0.125rem; --bs-pagination-border-color: #dee2e6; --bs-pagination-border-radius: 0.5rem; --bs-pagination-hover-color: var(--bs-link-hover-color); --bs-pagination-hover-bg: #e9ecef; --bs-pagination-hover-border-color: #dee2e6; --bs-pagination-focus-color: var(--bs-link-hover-color); --bs-pagination-focus-bg: #e9ecef; --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); --bs-pagination-active-color: #fff; --bs-pagination-active-bg: #1abc9c; --bs-pagination-active-border-color: #1abc9c; --bs-pagination-disabled-color: #6c757d; --bs-pagination-disabled-bg: #fff; --bs-pagination-disabled-border-color: #dee2e6; display: flex; padding-left: 0; list-style: none; } .page-link { position: relative; display: block; padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); font-size: var(--bs-pagination-font-size); color: var(--bs-pagination-color); text-decoration: none; background-color: var(--bs-pagination-bg); border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .page-link { transition: none; } } .page-link:hover { z-index: 2; color: var(--bs-pagination-hover-color); background-color: var(--bs-pagination-hover-bg); border-color: var(--bs-pagination-hover-border-color); } .page-link:focus { z-index: 3; color: var(--bs-pagination-focus-color); background-color: var(--bs-pagination-focus-bg); outline: 0; box-shadow: var(--bs-pagination-focus-box-shadow); } .page-link.active, .active > .page-link { z-index: 3; color: var(--bs-pagination-active-color); background-color: var(--bs-pagination-active-bg); border-color: var(--bs-pagination-active-border-color); } .page-link.disabled, .disabled > .page-link { color: var(--bs-pagination-disabled-color); pointer-events: none; background-color: var(--bs-pagination-disabled-bg); border-color: var(--bs-pagination-disabled-border-color); } .page-item:not(:first-child) .page-link { margin-left: -0.125rem; } .page-item:first-child .page-link { border-top-left-radius: var(--bs-pagination-border-radius); border-bottom-left-radius: var(--bs-pagination-border-radius); } .page-item:last-child .page-link { border-top-right-radius: var(--bs-pagination-border-radius); border-bottom-right-radius: var(--bs-pagination-border-radius); } .pagination-lg { --bs-pagination-padding-x: 1.5rem; --bs-pagination-padding-y: 0.75rem; --bs-pagination-font-size: 1.25rem; --bs-pagination-border-radius: 0.75rem; } .pagination-sm { --bs-pagination-padding-x: 0.5rem; --bs-pagination-padding-y: 0.25rem; --bs-pagination-font-size: 0.875rem; --bs-pagination-border-radius: 0.25rem; } .badge { --bs-badge-padding-x: 0.65em; --bs-badge-padding-y: 0.35em; --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; --bs-badge-border-radius: 0.5rem; display: inline-block; padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); font-size: var(--bs-badge-font-size); font-weight: var(--bs-badge-font-weight); line-height: 1; color: var(--bs-badge-color); text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: var(--bs-badge-border-radius); } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .alert { --bs-alert-bg: transparent; --bs-alert-padding-x: 1rem; --bs-alert-padding-y: 1rem; --bs-alert-margin-bottom: 1rem; --bs-alert-color: inherit; --bs-alert-border-color: transparent; --bs-alert-border: 0.125rem solid var(--bs-alert-border-color); --bs-alert-border-radius: 0.5rem; position: relative; padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); margin-bottom: var(--bs-alert-margin-bottom); color: var(--bs-alert-color); background-color: var(--bs-alert-bg); border: var(--bs-alert-border); border-radius: var(--bs-alert-border-radius); } .alert-heading { color: inherit; } .alert-link { font-weight: 700; } .alert-dismissible { padding-right: 3rem; } .alert-dismissible .btn-close { position: absolute; top: 0; right: 0; z-index: 2; padding: 1.25rem 1rem; } .alert-primary { --bs-alert-color: #10715e; --bs-alert-bg: #d1f2eb; --bs-alert-border-color: #baebe1; } .alert-primary .alert-link { color: #0d5a4b; } .alert-secondary { --bs-alert-color: #1a2530; --bs-alert-bg: #d5d8dc; --bs-alert-border-color: #c0c5cb; } .alert-secondary .alert-link { color: #151e26; } .alert-success { --bs-alert-color: #0f5132; --bs-alert-bg: #d1e7dd; --bs-alert-border-color: #badbcc; } .alert-success .alert-link { color: #0c4128; } .alert-info { --bs-alert-color: #087990; --bs-alert-bg: #cff4fc; --bs-alert-border-color: #b6effb; } .alert-info .alert-link { color: #066173; } .alert-warning { --bs-alert-color: #997404; --bs-alert-bg: #fff3cd; --bs-alert-border-color: #ffecb5; } .alert-warning .alert-link { color: #7a5d03; } .alert-danger { --bs-alert-color: #842029; --bs-alert-bg: #f8d7da; --bs-alert-border-color: #f5c2c7; } .alert-danger .alert-link { color: #6a1a21; } .alert-light { --bs-alert-color: #959596; --bs-alert-bg: #fefefe; --bs-alert-border-color: #fdfdfe; } .alert-light .alert-link { color: #777778; } .alert-dark { --bs-alert-color: #141619; --bs-alert-bg: #d3d3d4; --bs-alert-border-color: #bcbebf; } .alert-dark .alert-link { color: #101214; } @keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } .progress { --bs-progress-height: 1rem; --bs-progress-font-size: 0.75rem; --bs-progress-bg: #e9ecef; --bs-progress-border-radius: 0.5rem; --bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075); --bs-progress-bar-color: #fff; --bs-progress-bar-bg: #1abc9c; --bs-progress-bar-transition: width 0.6s ease; display: flex; height: var(--bs-progress-height); overflow: hidden; font-size: var(--bs-progress-font-size); background-color: var(--bs-progress-bg); border-radius: var(--bs-progress-border-radius); } .progress-bar { display: flex; flex-direction: column; justify-content: center; overflow: hidden; color: var(--bs-progress-bar-color); text-align: center; white-space: nowrap; background-color: var(--bs-progress-bar-bg); transition: var(--bs-progress-bar-transition); } @media (prefers-reduced-motion: reduce) { .progress-bar { transition: none; } } .progress-bar-striped { background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: var(--bs-progress-height) var(--bs-progress-height); } .progress-bar-animated { animation: 1s linear infinite progress-bar-stripes; } @media (prefers-reduced-motion: reduce) { .progress-bar-animated { animation: none; } } .list-group { --bs-list-group-color: #212529; --bs-list-group-bg: #fff; --bs-list-group-border-color: rgba(0, 0, 0, 0.125); --bs-list-group-border-width: 0.125rem; --bs-list-group-border-radius: 0.5rem; --bs-list-group-item-padding-x: 1rem; --bs-list-group-item-padding-y: 0.5rem; --bs-list-group-action-color: #495057; --bs-list-group-action-hover-color: #495057; --bs-list-group-action-hover-bg: #f8f9fa; --bs-list-group-action-active-color: #212529; --bs-list-group-action-active-bg: #e9ecef; --bs-list-group-disabled-color: #6c757d; --bs-list-group-disabled-bg: #fff; --bs-list-group-active-color: #fff; --bs-list-group-active-bg: #1abc9c; --bs-list-group-active-border-color: #1abc9c; display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; border-radius: var(--bs-list-group-border-radius); } .list-group-numbered { list-style-type: none; counter-reset: section; } .list-group-numbered > .list-group-item::before { content: counters(section, ".") ". "; counter-increment: section; } .list-group-item-action { width: 100%; color: var(--bs-list-group-action-color); text-align: inherit; } .list-group-item-action:hover, .list-group-item-action:focus { z-index: 1; color: var(--bs-list-group-action-hover-color); text-decoration: none; background-color: var(--bs-list-group-action-hover-bg); } .list-group-item-action:active { color: var(--bs-list-group-action-active-color); background-color: var(--bs-list-group-action-active-bg); } .list-group-item { position: relative; display: block; padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); color: var(--bs-list-group-color); text-decoration: none; background-color: var(--bs-list-group-bg); border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); } .list-group-item:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; } .list-group-item:last-child { border-bottom-right-radius: inherit; border-bottom-left-radius: inherit; } .list-group-item.disabled, .list-group-item:disabled { color: var(--bs-list-group-disabled-color); pointer-events: none; background-color: var(--bs-list-group-disabled-bg); } .list-group-item.active { z-index: 2; color: var(--bs-list-group-active-color); background-color: var(--bs-list-group-active-bg); border-color: var(--bs-list-group-active-border-color); } .list-group-item + .list-group-item { border-top-width: 0; } .list-group-item + .list-group-item.active { margin-top: calc(-1 * var(--bs-list-group-border-width)); border-top-width: var(--bs-list-group-border-width); } .list-group-horizontal { flex-direction: row; } .list-group-horizontal > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal > .list-group-item.active { margin-top: 0; } .list-group-horizontal > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal-sm > .list-group-item.active { margin-top: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal-md > .list-group-item.active { margin-top: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal-lg > .list-group-item.active { margin-top: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal-xl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 1400px) { .list-group-horizontal-xxl { flex-direction: row; } .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { border-bottom-left-radius: var(--bs-list-group-border-radius); border-top-right-radius: 0; } .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { border-top-right-radius: var(--bs-list-group-border-radius); border-bottom-left-radius: 0; } .list-group-horizontal-xxl > .list-group-item.active { margin-top: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item { border-top-width: var(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } .list-group-flush { border-radius: 0; } .list-group-flush > .list-group-item { border-width: 0 0 var(--bs-list-group-border-width); } .list-group-flush > .list-group-item:last-child { border-bottom-width: 0; } .list-group-item-primary { color: #10715e; background-color: #d1f2eb; } .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { color: #10715e; background-color: #bcdad4; } .list-group-item-primary.list-group-item-action.active { color: #fff; background-color: #10715e; border-color: #10715e; } .list-group-item-secondary { color: #1a2530; background-color: #d5d8dc; } .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { color: #1a2530; background-color: #c0c2c6; } .list-group-item-secondary.list-group-item-action.active { color: #fff; background-color: #1a2530; border-color: #1a2530; } .list-group-item-success { color: #0f5132; background-color: #d1e7dd; } .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { color: #0f5132; background-color: #bcd0c7; } .list-group-item-success.list-group-item-action.active { color: #fff; background-color: #0f5132; border-color: #0f5132; } .list-group-item-info { color: #087990; background-color: #cff4fc; } .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { color: #087990; background-color: #badce3; } .list-group-item-info.list-group-item-action.active { color: #fff; background-color: #087990; border-color: #087990; } .list-group-item-warning { color: #997404; background-color: #fff3cd; } .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { color: #997404; background-color: #e6dbb9; } .list-group-item-warning.list-group-item-action.active { color: #fff; background-color: #997404; border-color: #997404; } .list-group-item-danger { color: #842029; background-color: #f8d7da; } .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { color: #842029; background-color: #dfc2c4; } .list-group-item-danger.list-group-item-action.active { color: #fff; background-color: #842029; border-color: #842029; } .list-group-item-light { color: #959596; background-color: #fefefe; } .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { color: #959596; background-color: #e5e5e5; } .list-group-item-light.list-group-item-action.active { color: #fff; background-color: #959596; border-color: #959596; } .list-group-item-dark { color: #141619; background-color: #d3d3d4; } .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { color: #141619; background-color: #bebebf; } .list-group-item-dark.list-group-item-action.active { color: #fff; background-color: #141619; border-color: #141619; } .btn-close { box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; color: #000; background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat; border: 0; border-radius: 0.5rem; opacity: 0.5; } .btn-close:hover { color: #000; text-decoration: none; opacity: 0.75; } .btn-close:focus { outline: 0; box-shadow: 0 0 0 0.25rem rgba(26, 188, 156, 0.25); opacity: 1; } .btn-close:disabled, .btn-close.disabled { pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; opacity: 0.25; } .btn-close-white { filter: invert(1) grayscale(100%) brightness(200%); } .toast { --bs-toast-zindex: 1090; --bs-toast-padding-x: 0.75rem; --bs-toast-padding-y: 0.5rem; --bs-toast-spacing: 1.5rem; --bs-toast-max-width: 350px; --bs-toast-font-size: 0.875rem; --bs-toast-color: ; --bs-toast-bg: rgba(255, 255, 255, 0.85); --bs-toast-border-width: 0.125rem; --bs-toast-border-color: var(--bs-border-color-translucent); --bs-toast-border-radius: 0.5rem; --bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-toast-header-color: #6c757d; --bs-toast-header-bg: rgba(255, 255, 255, 0.85); --bs-toast-header-border-color: rgba(0, 0, 0, 0.05); width: var(--bs-toast-max-width); max-width: 100%; font-size: var(--bs-toast-font-size); color: var(--bs-toast-color); pointer-events: auto; background-color: var(--bs-toast-bg); background-clip: padding-box; border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); box-shadow: var(--bs-toast-box-shadow); border-radius: var(--bs-toast-border-radius); } .toast.showing { opacity: 0; } .toast:not(.show) { display: none; } .toast-container { --bs-toast-zindex: 1090; position: absolute; z-index: var(--bs-toast-zindex); width: -moz-max-content; width: max-content; max-width: 100%; pointer-events: none; } .toast-container > :not(:last-child) { margin-bottom: var(--bs-toast-spacing); } .toast-header { display: flex; align-items: center; padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); color: var(--bs-toast-header-color); background-color: var(--bs-toast-header-bg); background-clip: padding-box; border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); } .toast-header .btn-close { margin-right: calc(-0.5 * var(--bs-toast-padding-x)); margin-left: var(--bs-toast-padding-x); } .toast-body { padding: var(--bs-toast-padding-x); word-wrap: break-word; } .modal { --bs-modal-zindex: 1055; --bs-modal-width: 500px; --bs-modal-padding: 1rem; --bs-modal-margin: 0.5rem; --bs-modal-color: ; --bs-modal-bg: #fff; --bs-modal-border-color: var(--bs-border-color-translucent); --bs-modal-border-width: 0.125rem; --bs-modal-border-radius: 0.75rem; --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); --bs-modal-inner-border-radius: 0.625rem; --bs-modal-header-padding-x: 1rem; --bs-modal-header-padding-y: 1rem; --bs-modal-header-padding: 1rem 1rem; --bs-modal-header-border-color: var(--bs-border-color); --bs-modal-header-border-width: 0.125rem; --bs-modal-title-line-height: 1.5; --bs-modal-footer-gap: 0.5rem; --bs-modal-footer-bg: ; --bs-modal-footer-border-color: var(--bs-border-color); --bs-modal-footer-border-width: 0.125rem; position: fixed; top: 0; left: 0; z-index: var(--bs-modal-zindex); display: none; width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; outline: 0; } .modal-dialog { position: relative; width: auto; margin: var(--bs-modal-margin); pointer-events: none; } .modal.fade .modal-dialog { transition: transform 0.3s ease-out; transform: translate(0, -50px); } @media (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog { transition: none; } } .modal.show .modal-dialog { transform: none; } .modal.modal-static .modal-dialog { transform: scale(1.02); } .modal-dialog-scrollable { height: calc(100% - var(--bs-modal-margin) * 2); } .modal-dialog-scrollable .modal-content { max-height: 100%; overflow: hidden; } .modal-dialog-scrollable .modal-body { overflow-y: auto; } .modal-dialog-centered { display: flex; align-items: center; min-height: calc(100% - var(--bs-modal-margin) * 2); } .modal-content { position: relative; display: flex; flex-direction: column; width: 100%; color: var(--bs-modal-color); pointer-events: auto; background-color: var(--bs-modal-bg); background-clip: padding-box; border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); border-radius: var(--bs-modal-border-radius); outline: 0; } .modal-backdrop { --bs-backdrop-zindex: 1050; --bs-backdrop-bg: #000; --bs-backdrop-opacity: 0.5; position: fixed; top: 0; left: 0; z-index: var(--bs-backdrop-zindex); width: 100vw; height: 100vh; background-color: var(--bs-backdrop-bg); } .modal-backdrop.fade { opacity: 0; } .modal-backdrop.show { opacity: var(--bs-backdrop-opacity); } .modal-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: var(--bs-modal-header-padding); border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); border-top-left-radius: var(--bs-modal-inner-border-radius); border-top-right-radius: var(--bs-modal-inner-border-radius); } .modal-header .btn-close { padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto; } .modal-title { margin-bottom: 0; line-height: var(--bs-modal-title-line-height); } .modal-body { position: relative; flex: 1 1 auto; padding: var(--bs-modal-padding); } .modal-footer { display: flex; flex-shrink: 0; flex-wrap: wrap; align-items: center; justify-content: flex-end; padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); background-color: var(--bs-modal-footer-bg); border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); border-bottom-right-radius: var(--bs-modal-inner-border-radius); border-bottom-left-radius: var(--bs-modal-inner-border-radius); } .modal-footer > * { margin: calc(var(--bs-modal-footer-gap) * 0.5); } @media (min-width: 576px) { .modal { --bs-modal-margin: 1.75rem; --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); } .modal-dialog { max-width: var(--bs-modal-width); margin-right: auto; margin-left: auto; } .modal-sm { --bs-modal-width: 300px; } } @media (min-width: 992px) { .modal-lg, .modal-xl { --bs-modal-width: 800px; } } @media (min-width: 1200px) { .modal-xl { --bs-modal-width: 1140px; } } .modal-fullscreen { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen .modal-header, .modal-fullscreen .modal-footer { border-radius: 0; } .modal-fullscreen .modal-body { overflow-y: auto; } @media (max-width: 575.98px) { .modal-fullscreen-sm-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-sm-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-sm-down .modal-header, .modal-fullscreen-sm-down .modal-footer { border-radius: 0; } .modal-fullscreen-sm-down .modal-body { overflow-y: auto; } } @media (max-width: 767.98px) { .modal-fullscreen-md-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-md-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-md-down .modal-header, .modal-fullscreen-md-down .modal-footer { border-radius: 0; } .modal-fullscreen-md-down .modal-body { overflow-y: auto; } } @media (max-width: 991.98px) { .modal-fullscreen-lg-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-lg-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-lg-down .modal-header, .modal-fullscreen-lg-down .modal-footer { border-radius: 0; } .modal-fullscreen-lg-down .modal-body { overflow-y: auto; } } @media (max-width: 1199.98px) { .modal-fullscreen-xl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xl-down .modal-header, .modal-fullscreen-xl-down .modal-footer { border-radius: 0; } .modal-fullscreen-xl-down .modal-body { overflow-y: auto; } } @media (max-width: 1399.98px) { .modal-fullscreen-xxl-down { width: 100vw; max-width: none; height: 100%; margin: 0; } .modal-fullscreen-xxl-down .modal-content { height: 100%; border: 0; border-radius: 0; } .modal-fullscreen-xxl-down .modal-header, .modal-fullscreen-xxl-down .modal-footer { border-radius: 0; } .modal-fullscreen-xxl-down .modal-body { overflow-y: auto; } } .tooltip { --bs-tooltip-zindex: 1080; --bs-tooltip-max-width: 200px; --bs-tooltip-padding-x: 0.5rem; --bs-tooltip-padding-y: 0.25rem; --bs-tooltip-margin: ; --bs-tooltip-font-size: 0.875rem; --bs-tooltip-color: #fff; --bs-tooltip-bg: #000; --bs-tooltip-border-radius: 0.5rem; --bs-tooltip-opacity: 0.9; --bs-tooltip-arrow-width: 0.8rem; --bs-tooltip-arrow-height: 0.4rem; z-index: var(--bs-tooltip-zindex); display: block; padding: var(--bs-tooltip-arrow-height); margin: var(--bs-tooltip-margin); font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; white-space: normal; word-spacing: normal; line-break: auto; font-size: var(--bs-tooltip-font-size); word-wrap: break-word; opacity: 0; } .tooltip.show { opacity: var(--bs-tooltip-opacity); } .tooltip .tooltip-arrow { display: block; width: var(--bs-tooltip-arrow-width); height: var(--bs-tooltip-arrow-height); } .tooltip .tooltip-arrow::before { position: absolute; content: ""; border-color: transparent; border-style: solid; } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { bottom: 0; } .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { top: -1px; border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; border-top-color: var(--bs-tooltip-bg); } /* rtl:begin:ignore */ .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { left: 0; width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } .bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { right: -1px; border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; border-right-color: var(--bs-tooltip-bg); } /* rtl:end:ignore */ .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { top: 0; } .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { bottom: -1px; border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); border-bottom-color: var(--bs-tooltip-bg); } /* rtl:begin:ignore */ .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { right: 0; width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } .bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { left: -1px; border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); border-left-color: var(--bs-tooltip-bg); } /* rtl:end:ignore */ .tooltip-inner { max-width: var(--bs-tooltip-max-width); padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); color: var(--bs-tooltip-color); text-align: center; background-color: var(--bs-tooltip-bg); border-radius: var(--bs-tooltip-border-radius); } .popover { --bs-popover-zindex: 1070; --bs-popover-max-width: 276px; --bs-popover-font-size: 0.875rem; --bs-popover-bg: #fff; --bs-popover-border-width: 0.125rem; --bs-popover-border-color: var(--bs-border-color-translucent); --bs-popover-border-radius: 0.75rem; --bs-popover-inner-border-radius: 0.625rem; --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-popover-header-padding-x: 1rem; --bs-popover-header-padding-y: 0.5rem; --bs-popover-header-font-size: 1rem; --bs-popover-header-color: ; --bs-popover-header-bg: #f0f0f0; --bs-popover-body-padding-x: 1rem; --bs-popover-body-padding-y: 1rem; --bs-popover-body-color: #212529; --bs-popover-arrow-width: 1rem; --bs-popover-arrow-height: 0.5rem; --bs-popover-arrow-border: var(--bs-popover-border-color); z-index: var(--bs-popover-zindex); display: block; max-width: var(--bs-popover-max-width); font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; text-align: left; text-align: start; text-decoration: none; text-shadow: none; text-transform: none; letter-spacing: normal; word-break: normal; white-space: normal; word-spacing: normal; line-break: auto; font-size: var(--bs-popover-font-size); word-wrap: break-word; background-color: var(--bs-popover-bg); background-clip: padding-box; border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); border-radius: var(--bs-popover-border-radius); } .popover .popover-arrow { display: block; width: var(--bs-popover-arrow-width); height: var(--bs-popover-arrow-height); } .popover .popover-arrow::before, .popover .popover-arrow::after { position: absolute; display: block; content: ""; border-color: transparent; border-style: solid; border-width: 0; } .bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { bottom: 0; border-top-color: var(--bs-popover-arrow-border); } .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { bottom: var(--bs-popover-border-width); border-top-color: var(--bs-popover-bg); } /* rtl:begin:ignore */ .bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); width: var(--bs-popover-arrow-height); height: var(--bs-popover-arrow-width); } .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { left: 0; border-right-color: var(--bs-popover-arrow-border); } .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { left: var(--bs-popover-border-width); border-right-color: var(--bs-popover-bg); } /* rtl:end:ignore */ .bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { top: 0; border-bottom-color: var(--bs-popover-arrow-border); } .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { top: var(--bs-popover-border-width); border-bottom-color: var(--bs-popover-bg); } .bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { position: absolute; top: 0; left: 50%; display: block; width: var(--bs-popover-arrow-width); margin-left: calc(-0.5 * var(--bs-popover-arrow-width)); content: ""; border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); } /* rtl:begin:ignore */ .bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); width: var(--bs-popover-arrow-height); height: var(--bs-popover-arrow-width); } .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { right: 0; border-left-color: var(--bs-popover-arrow-border); } .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { right: var(--bs-popover-border-width); border-left-color: var(--bs-popover-bg); } /* rtl:end:ignore */ .popover-header { padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); margin-bottom: 0; font-size: var(--bs-popover-header-font-size); color: var(--bs-popover-header-color); background-color: var(--bs-popover-header-bg); border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); border-top-left-radius: var(--bs-popover-inner-border-radius); border-top-right-radius: var(--bs-popover-inner-border-radius); } .popover-header:empty { display: none; } .popover-body { padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); color: var(--bs-popover-body-color); } .carousel { position: relative; } .carousel.pointer-event { touch-action: pan-y; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner::after { display: block; clear: both; content: ""; } .carousel-item { position: relative; display: none; float: left; width: 100%; margin-right: -100%; -webkit-backface-visibility: hidden; backface-visibility: hidden; transition: transform 0.6s ease-in-out; } @media (prefers-reduced-motion: reduce) { .carousel-item { transition: none; } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { display: block; } .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { transform: translateX(100%); } .carousel-item-prev:not(.carousel-item-end), .active.carousel-item-start { transform: translateX(-100%); } .carousel-fade .carousel-item { opacity: 0; transition-property: opacity; transform: none; } .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end { z-index: 1; opacity: 1; } .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { z-index: 0; opacity: 0; transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { transition: none; } } .carousel-control-prev, .carousel-control-next { position: absolute; top: 0; bottom: 0; z-index: 1; display: flex; align-items: center; justify-content: center; width: 15%; padding: 0; color: #fff; text-align: center; background: none; border: 0; opacity: 0.5; transition: opacity 0.15s ease; } @media (prefers-reduced-motion: reduce) { .carousel-control-prev, .carousel-control-next { transition: none; } } .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { color: #fff; text-decoration: none; outline: 0; opacity: 0.9; } .carousel-control-prev { left: 0; } .carousel-control-next { right: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { display: inline-block; width: 2rem; height: 2rem; background-repeat: no-repeat; background-position: 50%; background-size: 100% 100%; } /* rtl:options: { "autoRename": true, "stringMap":[ { "name" : "prev-next", "search" : "prev", "replace" : "next" } ] } */ .carousel-control-prev-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; right: 0; bottom: 0; left: 0; z-index: 2; display: flex; justify-content: center; padding: 0; margin-right: 15%; margin-bottom: 1rem; margin-left: 15%; list-style: none; } .carousel-indicators [data-bs-target] { box-sizing: content-box; flex: 0 1 auto; width: 30px; height: 3px; padding: 0; margin-right: 3px; margin-left: 3px; text-indent: -999px; cursor: pointer; background-color: #fff; background-clip: padding-box; border: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent; opacity: 0.5; transition: opacity 0.6s ease; } @media (prefers-reduced-motion: reduce) { .carousel-indicators [data-bs-target] { transition: none; } } .carousel-indicators .active { opacity: 1; } .carousel-caption { position: absolute; right: 15%; bottom: 1.25rem; left: 15%; padding-top: 1.25rem; padding-bottom: 1.25rem; color: #fff; text-align: center; } .carousel-dark .carousel-control-prev-icon, .carousel-dark .carousel-control-next-icon { filter: invert(1) grayscale(100); } .carousel-dark .carousel-indicators [data-bs-target] { background-color: #000; } .carousel-dark .carousel-caption { color: #000; } .spinner-grow, .spinner-border { display: inline-block; width: var(--bs-spinner-width); height: var(--bs-spinner-height); vertical-align: var(--bs-spinner-vertical-align); border-radius: 50%; animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); } @keyframes spinner-border { to { transform: rotate(360deg) /* rtl:ignore */; } } .spinner-border { --bs-spinner-width: 2rem; --bs-spinner-height: 2rem; --bs-spinner-vertical-align: -0.125em; --bs-spinner-border-width: 0.25em; --bs-spinner-animation-speed: 0.75s; --bs-spinner-animation-name: spinner-border; border: var(--bs-spinner-border-width) solid currentcolor; border-right-color: transparent; } .spinner-border-sm { --bs-spinner-width: 1rem; --bs-spinner-height: 1rem; --bs-spinner-border-width: 0.2em; } @keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } .spinner-grow { --bs-spinner-width: 2rem; --bs-spinner-height: 2rem; --bs-spinner-vertical-align: -0.125em; --bs-spinner-animation-speed: 0.75s; --bs-spinner-animation-name: spinner-grow; background-color: currentcolor; opacity: 0; } .spinner-grow-sm { --bs-spinner-width: 1rem; --bs-spinner-height: 1rem; } @media (prefers-reduced-motion: reduce) { .spinner-border, .spinner-grow { --bs-spinner-animation-speed: 1.5s; } } .offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm { --bs-offcanvas-zindex: 1045; --bs-offcanvas-width: 400px; --bs-offcanvas-height: 30vh; --bs-offcanvas-padding-x: 1rem; --bs-offcanvas-padding-y: 1rem; --bs-offcanvas-color: ; --bs-offcanvas-bg: #fff; --bs-offcanvas-border-width: 0.125rem; --bs-offcanvas-border-color: var(--bs-border-color-translucent); --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); } @media (max-width: 575.98px) { .offcanvas-sm { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } } @media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { .offcanvas-sm { transition: none; } } @media (max-width: 575.98px) { .offcanvas-sm.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } } @media (max-width: 575.98px) { .offcanvas-sm.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } } @media (max-width: 575.98px) { .offcanvas-sm.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } } @media (max-width: 575.98px) { .offcanvas-sm.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } } @media (max-width: 575.98px) { .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { transform: none; } } @media (max-width: 575.98px) { .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { visibility: visible; } } @media (min-width: 576px) { .offcanvas-sm { --bs-offcanvas-height: auto; --bs-offcanvas-border-width: 0; background-color: transparent !important; } .offcanvas-sm .offcanvas-header { display: none; } .offcanvas-sm .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; background-color: transparent !important; } } @media (max-width: 767.98px) { .offcanvas-md { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } } @media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { .offcanvas-md { transition: none; } } @media (max-width: 767.98px) { .offcanvas-md.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } } @media (max-width: 767.98px) { .offcanvas-md.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } } @media (max-width: 767.98px) { .offcanvas-md.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } } @media (max-width: 767.98px) { .offcanvas-md.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } } @media (max-width: 767.98px) { .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { transform: none; } } @media (max-width: 767.98px) { .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { visibility: visible; } } @media (min-width: 768px) { .offcanvas-md { --bs-offcanvas-height: auto; --bs-offcanvas-border-width: 0; background-color: transparent !important; } .offcanvas-md .offcanvas-header { display: none; } .offcanvas-md .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; background-color: transparent !important; } } @media (max-width: 991.98px) { .offcanvas-lg { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } } @media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { .offcanvas-lg { transition: none; } } @media (max-width: 991.98px) { .offcanvas-lg.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } } @media (max-width: 991.98px) { .offcanvas-lg.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } } @media (max-width: 991.98px) { .offcanvas-lg.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } } @media (max-width: 991.98px) { .offcanvas-lg.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } } @media (max-width: 991.98px) { .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { transform: none; } } @media (max-width: 991.98px) { .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { visibility: visible; } } @media (min-width: 992px) { .offcanvas-lg { --bs-offcanvas-height: auto; --bs-offcanvas-border-width: 0; background-color: transparent !important; } .offcanvas-lg .offcanvas-header { display: none; } .offcanvas-lg .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; background-color: transparent !important; } } @media (max-width: 1199.98px) { .offcanvas-xl { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } } @media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { .offcanvas-xl { transition: none; } } @media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } } @media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } } @media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } } @media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } } @media (max-width: 1199.98px) { .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { transform: none; } } @media (max-width: 1199.98px) { .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { visibility: visible; } } @media (min-width: 1200px) { .offcanvas-xl { --bs-offcanvas-height: auto; --bs-offcanvas-border-width: 0; background-color: transparent !important; } .offcanvas-xl .offcanvas-header { display: none; } .offcanvas-xl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; background-color: transparent !important; } } @media (max-width: 1399.98px) { .offcanvas-xxl { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } } @media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { .offcanvas-xxl { transition: none; } } @media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } } @media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } } @media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } } @media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } } @media (max-width: 1399.98px) { .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { transform: none; } } @media (max-width: 1399.98px) { .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { visibility: visible; } } @media (min-width: 1400px) { .offcanvas-xxl { --bs-offcanvas-height: auto; --bs-offcanvas-border-width: 0; background-color: transparent !important; } .offcanvas-xxl .offcanvas-header { display: none; } .offcanvas-xxl .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; background-color: transparent !important; } } .offcanvas { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: transform 0.3s ease-in-out; } @media (prefers-reduced-motion: reduce) { .offcanvas { transition: none; } } .offcanvas.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas.showing, .offcanvas.show:not(.hiding) { transform: none; } .offcanvas.showing, .offcanvas.hiding, .offcanvas.show { visibility: visible; } .offcanvas-backdrop { position: fixed; top: 0; left: 0; z-index: 1040; width: 100vw; height: 100vh; background-color: #000; } .offcanvas-backdrop.fade { opacity: 0; } .offcanvas-backdrop.show { opacity: 0.5; } .offcanvas-header { display: flex; align-items: center; justify-content: space-between; padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } .offcanvas-header .btn-close { padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y)); margin-right: calc(-0.5 * var(--bs-offcanvas-padding-x)); margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y)); } .offcanvas-title { margin-bottom: 0; line-height: 1.5; } .offcanvas-body { flex-grow: 1; padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); overflow-y: auto; } .placeholder { display: inline-block; min-height: 1em; vertical-align: middle; cursor: wait; background-color: currentcolor; opacity: 0.5; } .placeholder.btn::before { display: inline-block; content: ""; } .placeholder-xs { min-height: 0.6em; } .placeholder-sm { min-height: 0.8em; } .placeholder-lg { min-height: 1.2em; } .placeholder-glow .placeholder { animation: placeholder-glow 2s ease-in-out infinite; } @keyframes placeholder-glow { 50% { opacity: 0.2; } } .placeholder-wave { -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); -webkit-mask-size: 200% 100%; mask-size: 200% 100%; animation: placeholder-wave 2s linear infinite; } @keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } .clearfix::after { display: block; clear: both; content: ""; } .text-bg-primary { color: #fff !important; background-color: RGBA(26, 188, 156, var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { color: #fff !important; background-color: RGBA(44, 62, 80, var(--bs-bg-opacity, 1)) !important; } .text-bg-success { color: #fff !important; background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important; } .text-bg-info { color: #000 !important; background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { color: #000 !important; background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { color: #fff !important; background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important; } .text-bg-light { color: #000 !important; background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { color: #fff !important; background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important; } .link-primary { color: #1abc9c !important; } .link-primary:hover, .link-primary:focus { color: #15967d !important; } .link-secondary { color: #2c3e50 !important; } .link-secondary:hover, .link-secondary:focus { color: #233240 !important; } .link-success { color: #198754 !important; } .link-success:hover, .link-success:focus { color: #146c43 !important; } .link-info { color: #0dcaf0 !important; } .link-info:hover, .link-info:focus { color: #3dd5f3 !important; } .link-warning { color: #ffc107 !important; } .link-warning:hover, .link-warning:focus { color: #ffcd39 !important; } .link-danger { color: #dc3545 !important; } .link-danger:hover, .link-danger:focus { color: #b02a37 !important; } .link-light { color: #f8f9fa !important; } .link-light:hover, .link-light:focus { color: #f9fafb !important; } .link-dark { color: #212529 !important; } .link-dark:hover, .link-dark:focus { color: #1a1e21 !important; } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: 1030; } .fixed-bottom { position: fixed; right: 0; bottom: 0; left: 0; z-index: 1030; } .sticky-top { position: sticky; top: 0; z-index: 1020; } .sticky-bottom { position: sticky; bottom: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: sticky; top: 0; z-index: 1020; } .sticky-sm-bottom { position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: sticky; top: 0; z-index: 1020; } .sticky-md-bottom { position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: sticky; top: 0; z-index: 1020; } .sticky-lg-bottom { position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: sticky; top: 0; z-index: 1020; } .sticky-xl-bottom { position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: sticky; top: 0; z-index: 1020; } .sticky-xxl-bottom { position: sticky; bottom: 0; z-index: 1020; } } .hstack { display: flex; flex-direction: row; align-items: center; align-self: stretch; } .vstack { display: flex; flex: 1 1 auto; flex-direction: column; align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; } .stretched-link::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentcolor; opacity: 0.25; } .align-baseline { vertical-align: baseline !important; } .align-top { vertical-align: top !important; } .align-middle { vertical-align: middle !important; } .align-bottom { vertical-align: bottom !important; } .align-text-bottom { vertical-align: text-bottom !important; } .align-text-top { vertical-align: text-top !important; } .float-start { float: left !important; } .float-end { float: right !important; } .float-none { float: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.75 !important; } .opacity-100 { opacity: 1 !important; } .overflow-auto { overflow: auto !important; } .overflow-hidden { overflow: hidden !important; } .overflow-visible { overflow: visible !important; } .overflow-scroll { overflow: scroll !important; } .d-inline { display: inline !important; } .d-inline-block { display: inline-block !important; } .d-block { display: block !important; } .d-grid { display: grid !important; } .d-table { display: table !important; } .d-table-row { display: table-row !important; } .d-table-cell { display: table-cell !important; } .d-flex { display: flex !important; } .d-inline-flex { display: inline-flex !important; } .d-none { display: none !important; } .shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .position-static { position: static !important; } .position-relative { position: relative !important; } .position-absolute { position: absolute !important; } .position-fixed { position: fixed !important; } .position-sticky { position: sticky !important; } .top-0 { top: 0 !important; } .top-50 { top: 50% !important; } .top-100 { top: 100% !important; } .bottom-0 { bottom: 0 !important; } .bottom-50 { bottom: 50% !important; } .bottom-100 { bottom: 100% !important; } .start-0 { left: 0 !important; } .start-50 { left: 50% !important; } .start-100 { left: 100% !important; } .end-0 { right: 0 !important; } .end-50 { right: 50% !important; } .end-100 { right: 100% !important; } .translate-middle { transform: translate(-50%, -50%) !important; } .translate-middle-x { transform: translateX(-50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { border: 0 !important; } .border-top { border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { border-right: 0 !important; } .border-bottom { border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { border-left: 0 !important; } .border-primary { --bs-border-opacity: 1; border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } .border-secondary { --bs-border-opacity: 1; border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } .border-success { --bs-border-opacity: 1; border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } .border-info { --bs-border-opacity: 1; border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { --bs-border-opacity: 1; border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } .border-danger { --bs-border-opacity: 1; border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { --bs-border-opacity: 1; border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { --bs-border-opacity: 1; border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-white { --bs-border-opacity: 1; border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-1 { --bs-border-width: 1px; } .border-2 { --bs-border-width: 2px; } .border-3 { --bs-border-width: 3px; } .border-4 { --bs-border-width: 4px; } .border-5 { --bs-border-width: 5px; } .border-opacity-10 { --bs-border-opacity: 0.1; } .border-opacity-25 { --bs-border-opacity: 0.25; } .border-opacity-50 { --bs-border-opacity: 0.5; } .border-opacity-75 { --bs-border-opacity: 0.75; } .border-opacity-100 { --bs-border-opacity: 1; } .w-25 { width: 25% !important; } .w-50 { width: 50% !important; } .w-75 { width: 75% !important; } .w-100 { width: 100% !important; } .w-auto { width: auto !important; } .mw-100 { max-width: 100% !important; } .vw-100 { width: 100vw !important; } .min-vw-100 { min-width: 100vw !important; } .h-25 { height: 25% !important; } .h-50 { height: 50% !important; } .h-75 { height: 75% !important; } .h-100 { height: 100% !important; } .h-auto { height: auto !important; } .mh-100 { max-height: 100% !important; } .vh-100 { height: 100vh !important; } .min-vh-100 { min-height: 100vh !important; } .flex-fill { flex: 1 1 auto !important; } .flex-row { flex-direction: row !important; } .flex-column { flex-direction: column !important; } .flex-row-reverse { flex-direction: row-reverse !important; } .flex-column-reverse { flex-direction: column-reverse !important; } .flex-grow-0 { flex-grow: 0 !important; } .flex-grow-1 { flex-grow: 1 !important; } .flex-shrink-0 { flex-shrink: 0 !important; } .flex-shrink-1 { flex-shrink: 1 !important; } .flex-wrap { flex-wrap: wrap !important; } .flex-nowrap { flex-wrap: nowrap !important; } .flex-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-start { justify-content: flex-start !important; } .justify-content-end { justify-content: flex-end !important; } .justify-content-center { justify-content: center !important; } .justify-content-between { justify-content: space-between !important; } .justify-content-around { justify-content: space-around !important; } .justify-content-evenly { justify-content: space-evenly !important; } .align-items-start { align-items: flex-start !important; } .align-items-end { align-items: flex-end !important; } .align-items-center { align-items: center !important; } .align-items-baseline { align-items: baseline !important; } .align-items-stretch { align-items: stretch !important; } .align-content-start { align-content: flex-start !important; } .align-content-end { align-content: flex-end !important; } .align-content-center { align-content: center !important; } .align-content-between { align-content: space-between !important; } .align-content-around { align-content: space-around !important; } .align-content-stretch { align-content: stretch !important; } .align-self-auto { align-self: auto !important; } .align-self-start { align-self: flex-start !important; } .align-self-end { align-self: flex-end !important; } .align-self-center { align-self: center !important; } .align-self-baseline { align-self: baseline !important; } .align-self-stretch { align-self: stretch !important; } .order-first { order: -1 !important; } .order-0 { order: 0 !important; } .order-1 { order: 1 !important; } .order-2 { order: 2 !important; } .order-3 { order: 3 !important; } .order-4 { order: 4 !important; } .order-5 { order: 5 !important; } .order-last { order: 6 !important; } .m-0 { margin: 0 !important; } .m-1 { margin: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-auto { margin-right: auto !important; margin-left: auto !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-right: 0 !important; } .me-1 { margin-right: 0.25rem !important; } .me-2 { margin-right: 0.5rem !important; } .me-3 { margin-right: 1rem !important; } .me-4 { margin-right: 1.5rem !important; } .me-5 { margin-right: 3rem !important; } .me-auto { margin-right: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-left: 0 !important; } .ms-1 { margin-left: 0.25rem !important; } .ms-2 { margin-left: 0.5rem !important; } .ms-3 { margin-left: 1rem !important; } .ms-4 { margin-left: 1.5rem !important; } .ms-5 { margin-left: 3rem !important; } .ms-auto { margin-left: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-right: 0 !important; } .pe-1 { padding-right: 0.25rem !important; } .pe-2 { padding-right: 0.5rem !important; } .pe-3 { padding-right: 1rem !important; } .pe-4 { padding-right: 1.5rem !important; } .pe-5 { padding-right: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-left: 0 !important; } .ps-1 { padding-left: 0.25rem !important; } .ps-2 { padding-left: 0.5rem !important; } .ps-3 { padding-left: 1rem !important; } .ps-4 { padding-left: 1.5rem !important; } .ps-5 { padding-left: 3rem !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !important; } .fst-italic { font-style: italic !important; } .fst-normal { font-style: normal !important; } .fw-light { font-weight: 300 !important; } .fw-lighter { font-weight: lighter !important; } .fw-normal { font-weight: 400 !important; } .fw-bold { font-weight: 700 !important; } .fw-semibold { font-weight: 600 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !important; } .text-start { text-align: left !important; } .text-end { text-align: right !important; } .text-center { text-align: center !important; } .text-decoration-none { text-decoration: none !important; } .text-decoration-underline { text-decoration: underline !important; } .text-decoration-line-through { text-decoration: line-through !important; } .text-lowercase { text-transform: lowercase !important; } .text-uppercase { text-transform: uppercase !important; } .text-capitalize { text-transform: capitalize !important; } .text-wrap { white-space: normal !important; } .text-nowrap { white-space: nowrap !important; } /* rtl:begin:remove */ .text-break { word-wrap: break-word !important; word-break: break-word !important; } /* rtl:end:remove */ .text-primary { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: #6c757d !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: var(--bs-border-radius) !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { border-radius: var(--bs-border-radius) !important; } .rounded-3 { border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { border-radius: var(--bs-border-radius-2xl) !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { border-top-left-radius: var(--bs-border-radius) !important; border-top-right-radius: var(--bs-border-radius) !important; } .rounded-end { border-top-right-radius: var(--bs-border-radius) !important; border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-bottom { border-bottom-right-radius: var(--bs-border-radius) !important; border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-start { border-bottom-left-radius: var(--bs-border-radius) !important; border-top-left-radius: var(--bs-border-radius) !important; } .visible { visibility: visible !important; } .invisible { visibility: hidden !important; } @media (min-width: 576px) { .float-sm-start { float: left !important; } .float-sm-end { float: right !important; } .float-sm-none { float: none !important; } .d-sm-inline { display: inline !important; } .d-sm-inline-block { display: inline-block !important; } .d-sm-block { display: block !important; } .d-sm-grid { display: grid !important; } .d-sm-table { display: table !important; } .d-sm-table-row { display: table-row !important; } .d-sm-table-cell { display: table-cell !important; } .d-sm-flex { display: flex !important; } .d-sm-inline-flex { display: inline-flex !important; } .d-sm-none { display: none !important; } .flex-sm-fill { flex: 1 1 auto !important; } .flex-sm-row { flex-direction: row !important; } .flex-sm-column { flex-direction: column !important; } .flex-sm-row-reverse { flex-direction: row-reverse !important; } .flex-sm-column-reverse { flex-direction: column-reverse !important; } .flex-sm-grow-0 { flex-grow: 0 !important; } .flex-sm-grow-1 { flex-grow: 1 !important; } .flex-sm-shrink-0 { flex-shrink: 0 !important; } .flex-sm-shrink-1 { flex-shrink: 1 !important; } .flex-sm-wrap { flex-wrap: wrap !important; } .flex-sm-nowrap { flex-wrap: nowrap !important; } .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-sm-start { justify-content: flex-start !important; } .justify-content-sm-end { justify-content: flex-end !important; } .justify-content-sm-center { justify-content: center !important; } .justify-content-sm-between { justify-content: space-between !important; } .justify-content-sm-around { justify-content: space-around !important; } .justify-content-sm-evenly { justify-content: space-evenly !important; } .align-items-sm-start { align-items: flex-start !important; } .align-items-sm-end { align-items: flex-end !important; } .align-items-sm-center { align-items: center !important; } .align-items-sm-baseline { align-items: baseline !important; } .align-items-sm-stretch { align-items: stretch !important; } .align-content-sm-start { align-content: flex-start !important; } .align-content-sm-end { align-content: flex-end !important; } .align-content-sm-center { align-content: center !important; } .align-content-sm-between { align-content: space-between !important; } .align-content-sm-around { align-content: space-around !important; } .align-content-sm-stretch { align-content: stretch !important; } .align-self-sm-auto { align-self: auto !important; } .align-self-sm-start { align-self: flex-start !important; } .align-self-sm-end { align-self: flex-end !important; } .align-self-sm-center { align-self: center !important; } .align-self-sm-baseline { align-self: baseline !important; } .align-self-sm-stretch { align-self: stretch !important; } .order-sm-first { order: -1 !important; } .order-sm-0 { order: 0 !important; } .order-sm-1 { order: 1 !important; } .order-sm-2 { order: 2 !important; } .order-sm-3 { order: 3 !important; } .order-sm-4 { order: 4 !important; } .order-sm-5 { order: 5 !important; } .order-sm-last { order: 6 !important; } .m-sm-0 { margin: 0 !important; } .m-sm-1 { margin: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-sm-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-sm-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-sm-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-sm-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-sm-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-sm-auto { margin-right: auto !important; margin-left: auto !important; } .my-sm-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-sm-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-sm-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-sm-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-sm-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-right: 0 !important; } .me-sm-1 { margin-right: 0.25rem !important; } .me-sm-2 { margin-right: 0.5rem !important; } .me-sm-3 { margin-right: 1rem !important; } .me-sm-4 { margin-right: 1.5rem !important; } .me-sm-5 { margin-right: 3rem !important; } .me-sm-auto { margin-right: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-left: 0 !important; } .ms-sm-1 { margin-left: 0.25rem !important; } .ms-sm-2 { margin-left: 0.5rem !important; } .ms-sm-3 { margin-left: 1rem !important; } .ms-sm-4 { margin-left: 1.5rem !important; } .ms-sm-5 { margin-left: 3rem !important; } .ms-sm-auto { margin-left: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-sm-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-sm-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-sm-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-sm-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-sm-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-sm-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-sm-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-sm-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-right: 0 !important; } .pe-sm-1 { padding-right: 0.25rem !important; } .pe-sm-2 { padding-right: 0.5rem !important; } .pe-sm-3 { padding-right: 1rem !important; } .pe-sm-4 { padding-right: 1.5rem !important; } .pe-sm-5 { padding-right: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-left: 0 !important; } .ps-sm-1 { padding-left: 0.25rem !important; } .ps-sm-2 { padding-left: 0.5rem !important; } .ps-sm-3 { padding-left: 1rem !important; } .ps-sm-4 { padding-left: 1.5rem !important; } .ps-sm-5 { padding-left: 3rem !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .text-sm-start { text-align: left !important; } .text-sm-end { text-align: right !important; } .text-sm-center { text-align: center !important; } } @media (min-width: 768px) { .float-md-start { float: left !important; } .float-md-end { float: right !important; } .float-md-none { float: none !important; } .d-md-inline { display: inline !important; } .d-md-inline-block { display: inline-block !important; } .d-md-block { display: block !important; } .d-md-grid { display: grid !important; } .d-md-table { display: table !important; } .d-md-table-row { display: table-row !important; } .d-md-table-cell { display: table-cell !important; } .d-md-flex { display: flex !important; } .d-md-inline-flex { display: inline-flex !important; } .d-md-none { display: none !important; } .flex-md-fill { flex: 1 1 auto !important; } .flex-md-row { flex-direction: row !important; } .flex-md-column { flex-direction: column !important; } .flex-md-row-reverse { flex-direction: row-reverse !important; } .flex-md-column-reverse { flex-direction: column-reverse !important; } .flex-md-grow-0 { flex-grow: 0 !important; } .flex-md-grow-1 { flex-grow: 1 !important; } .flex-md-shrink-0 { flex-shrink: 0 !important; } .flex-md-shrink-1 { flex-shrink: 1 !important; } .flex-md-wrap { flex-wrap: wrap !important; } .flex-md-nowrap { flex-wrap: nowrap !important; } .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-md-start { justify-content: flex-start !important; } .justify-content-md-end { justify-content: flex-end !important; } .justify-content-md-center { justify-content: center !important; } .justify-content-md-between { justify-content: space-between !important; } .justify-content-md-around { justify-content: space-around !important; } .justify-content-md-evenly { justify-content: space-evenly !important; } .align-items-md-start { align-items: flex-start !important; } .align-items-md-end { align-items: flex-end !important; } .align-items-md-center { align-items: center !important; } .align-items-md-baseline { align-items: baseline !important; } .align-items-md-stretch { align-items: stretch !important; } .align-content-md-start { align-content: flex-start !important; } .align-content-md-end { align-content: flex-end !important; } .align-content-md-center { align-content: center !important; } .align-content-md-between { align-content: space-between !important; } .align-content-md-around { align-content: space-around !important; } .align-content-md-stretch { align-content: stretch !important; } .align-self-md-auto { align-self: auto !important; } .align-self-md-start { align-self: flex-start !important; } .align-self-md-end { align-self: flex-end !important; } .align-self-md-center { align-self: center !important; } .align-self-md-baseline { align-self: baseline !important; } .align-self-md-stretch { align-self: stretch !important; } .order-md-first { order: -1 !important; } .order-md-0 { order: 0 !important; } .order-md-1 { order: 1 !important; } .order-md-2 { order: 2 !important; } .order-md-3 { order: 3 !important; } .order-md-4 { order: 4 !important; } .order-md-5 { order: 5 !important; } .order-md-last { order: 6 !important; } .m-md-0 { margin: 0 !important; } .m-md-1 { margin: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-md-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-md-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-md-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-md-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-md-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-md-auto { margin-right: auto !important; margin-left: auto !important; } .my-md-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-md-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-md-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-md-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-md-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-right: 0 !important; } .me-md-1 { margin-right: 0.25rem !important; } .me-md-2 { margin-right: 0.5rem !important; } .me-md-3 { margin-right: 1rem !important; } .me-md-4 { margin-right: 1.5rem !important; } .me-md-5 { margin-right: 3rem !important; } .me-md-auto { margin-right: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-left: 0 !important; } .ms-md-1 { margin-left: 0.25rem !important; } .ms-md-2 { margin-left: 0.5rem !important; } .ms-md-3 { margin-left: 1rem !important; } .ms-md-4 { margin-left: 1.5rem !important; } .ms-md-5 { margin-left: 3rem !important; } .ms-md-auto { margin-left: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-md-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-md-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-md-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-md-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-md-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-md-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-md-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-right: 0 !important; } .pe-md-1 { padding-right: 0.25rem !important; } .pe-md-2 { padding-right: 0.5rem !important; } .pe-md-3 { padding-right: 1rem !important; } .pe-md-4 { padding-right: 1.5rem !important; } .pe-md-5 { padding-right: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-left: 0 !important; } .ps-md-1 { padding-left: 0.25rem !important; } .ps-md-2 { padding-left: 0.5rem !important; } .ps-md-3 { padding-left: 1rem !important; } .ps-md-4 { padding-left: 1.5rem !important; } .ps-md-5 { padding-left: 3rem !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .text-md-start { text-align: left !important; } .text-md-end { text-align: right !important; } .text-md-center { text-align: center !important; } } @media (min-width: 992px) { .float-lg-start { float: left !important; } .float-lg-end { float: right !important; } .float-lg-none { float: none !important; } .d-lg-inline { display: inline !important; } .d-lg-inline-block { display: inline-block !important; } .d-lg-block { display: block !important; } .d-lg-grid { display: grid !important; } .d-lg-table { display: table !important; } .d-lg-table-row { display: table-row !important; } .d-lg-table-cell { display: table-cell !important; } .d-lg-flex { display: flex !important; } .d-lg-inline-flex { display: inline-flex !important; } .d-lg-none { display: none !important; } .flex-lg-fill { flex: 1 1 auto !important; } .flex-lg-row { flex-direction: row !important; } .flex-lg-column { flex-direction: column !important; } .flex-lg-row-reverse { flex-direction: row-reverse !important; } .flex-lg-column-reverse { flex-direction: column-reverse !important; } .flex-lg-grow-0 { flex-grow: 0 !important; } .flex-lg-grow-1 { flex-grow: 1 !important; } .flex-lg-shrink-0 { flex-shrink: 0 !important; } .flex-lg-shrink-1 { flex-shrink: 1 !important; } .flex-lg-wrap { flex-wrap: wrap !important; } .flex-lg-nowrap { flex-wrap: nowrap !important; } .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-lg-start { justify-content: flex-start !important; } .justify-content-lg-end { justify-content: flex-end !important; } .justify-content-lg-center { justify-content: center !important; } .justify-content-lg-between { justify-content: space-between !important; } .justify-content-lg-around { justify-content: space-around !important; } .justify-content-lg-evenly { justify-content: space-evenly !important; } .align-items-lg-start { align-items: flex-start !important; } .align-items-lg-end { align-items: flex-end !important; } .align-items-lg-center { align-items: center !important; } .align-items-lg-baseline { align-items: baseline !important; } .align-items-lg-stretch { align-items: stretch !important; } .align-content-lg-start { align-content: flex-start !important; } .align-content-lg-end { align-content: flex-end !important; } .align-content-lg-center { align-content: center !important; } .align-content-lg-between { align-content: space-between !important; } .align-content-lg-around { align-content: space-around !important; } .align-content-lg-stretch { align-content: stretch !important; } .align-self-lg-auto { align-self: auto !important; } .align-self-lg-start { align-self: flex-start !important; } .align-self-lg-end { align-self: flex-end !important; } .align-self-lg-center { align-self: center !important; } .align-self-lg-baseline { align-self: baseline !important; } .align-self-lg-stretch { align-self: stretch !important; } .order-lg-first { order: -1 !important; } .order-lg-0 { order: 0 !important; } .order-lg-1 { order: 1 !important; } .order-lg-2 { order: 2 !important; } .order-lg-3 { order: 3 !important; } .order-lg-4 { order: 4 !important; } .order-lg-5 { order: 5 !important; } .order-lg-last { order: 6 !important; } .m-lg-0 { margin: 0 !important; } .m-lg-1 { margin: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-lg-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-lg-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-lg-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-lg-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-lg-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-lg-auto { margin-right: auto !important; margin-left: auto !important; } .my-lg-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-lg-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-lg-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-lg-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-lg-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-right: 0 !important; } .me-lg-1 { margin-right: 0.25rem !important; } .me-lg-2 { margin-right: 0.5rem !important; } .me-lg-3 { margin-right: 1rem !important; } .me-lg-4 { margin-right: 1.5rem !important; } .me-lg-5 { margin-right: 3rem !important; } .me-lg-auto { margin-right: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-left: 0 !important; } .ms-lg-1 { margin-left: 0.25rem !important; } .ms-lg-2 { margin-left: 0.5rem !important; } .ms-lg-3 { margin-left: 1rem !important; } .ms-lg-4 { margin-left: 1.5rem !important; } .ms-lg-5 { margin-left: 3rem !important; } .ms-lg-auto { margin-left: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-lg-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-lg-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-lg-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-lg-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-lg-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-lg-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-lg-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-lg-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-right: 0 !important; } .pe-lg-1 { padding-right: 0.25rem !important; } .pe-lg-2 { padding-right: 0.5rem !important; } .pe-lg-3 { padding-right: 1rem !important; } .pe-lg-4 { padding-right: 1.5rem !important; } .pe-lg-5 { padding-right: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-left: 0 !important; } .ps-lg-1 { padding-left: 0.25rem !important; } .ps-lg-2 { padding-left: 0.5rem !important; } .ps-lg-3 { padding-left: 1rem !important; } .ps-lg-4 { padding-left: 1.5rem !important; } .ps-lg-5 { padding-left: 3rem !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .text-lg-start { text-align: left !important; } .text-lg-end { text-align: right !important; } .text-lg-center { text-align: center !important; } } @media (min-width: 1200px) { .float-xl-start { float: left !important; } .float-xl-end { float: right !important; } .float-xl-none { float: none !important; } .d-xl-inline { display: inline !important; } .d-xl-inline-block { display: inline-block !important; } .d-xl-block { display: block !important; } .d-xl-grid { display: grid !important; } .d-xl-table { display: table !important; } .d-xl-table-row { display: table-row !important; } .d-xl-table-cell { display: table-cell !important; } .d-xl-flex { display: flex !important; } .d-xl-inline-flex { display: inline-flex !important; } .d-xl-none { display: none !important; } .flex-xl-fill { flex: 1 1 auto !important; } .flex-xl-row { flex-direction: row !important; } .flex-xl-column { flex-direction: column !important; } .flex-xl-row-reverse { flex-direction: row-reverse !important; } .flex-xl-column-reverse { flex-direction: column-reverse !important; } .flex-xl-grow-0 { flex-grow: 0 !important; } .flex-xl-grow-1 { flex-grow: 1 !important; } .flex-xl-shrink-0 { flex-shrink: 0 !important; } .flex-xl-shrink-1 { flex-shrink: 1 !important; } .flex-xl-wrap { flex-wrap: wrap !important; } .flex-xl-nowrap { flex-wrap: nowrap !important; } .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xl-start { justify-content: flex-start !important; } .justify-content-xl-end { justify-content: flex-end !important; } .justify-content-xl-center { justify-content: center !important; } .justify-content-xl-between { justify-content: space-between !important; } .justify-content-xl-around { justify-content: space-around !important; } .justify-content-xl-evenly { justify-content: space-evenly !important; } .align-items-xl-start { align-items: flex-start !important; } .align-items-xl-end { align-items: flex-end !important; } .align-items-xl-center { align-items: center !important; } .align-items-xl-baseline { align-items: baseline !important; } .align-items-xl-stretch { align-items: stretch !important; } .align-content-xl-start { align-content: flex-start !important; } .align-content-xl-end { align-content: flex-end !important; } .align-content-xl-center { align-content: center !important; } .align-content-xl-between { align-content: space-between !important; } .align-content-xl-around { align-content: space-around !important; } .align-content-xl-stretch { align-content: stretch !important; } .align-self-xl-auto { align-self: auto !important; } .align-self-xl-start { align-self: flex-start !important; } .align-self-xl-end { align-self: flex-end !important; } .align-self-xl-center { align-self: center !important; } .align-self-xl-baseline { align-self: baseline !important; } .align-self-xl-stretch { align-self: stretch !important; } .order-xl-first { order: -1 !important; } .order-xl-0 { order: 0 !important; } .order-xl-1 { order: 1 !important; } .order-xl-2 { order: 2 !important; } .order-xl-3 { order: 3 !important; } .order-xl-4 { order: 4 !important; } .order-xl-5 { order: 5 !important; } .order-xl-last { order: 6 !important; } .m-xl-0 { margin: 0 !important; } .m-xl-1 { margin: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-right: 0 !important; } .me-xl-1 { margin-right: 0.25rem !important; } .me-xl-2 { margin-right: 0.5rem !important; } .me-xl-3 { margin-right: 1rem !important; } .me-xl-4 { margin-right: 1.5rem !important; } .me-xl-5 { margin-right: 3rem !important; } .me-xl-auto { margin-right: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-left: 0 !important; } .ms-xl-1 { margin-left: 0.25rem !important; } .ms-xl-2 { margin-left: 0.5rem !important; } .ms-xl-3 { margin-left: 1rem !important; } .ms-xl-4 { margin-left: 1.5rem !important; } .ms-xl-5 { margin-left: 3rem !important; } .ms-xl-auto { margin-left: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-right: 0 !important; } .pe-xl-1 { padding-right: 0.25rem !important; } .pe-xl-2 { padding-right: 0.5rem !important; } .pe-xl-3 { padding-right: 1rem !important; } .pe-xl-4 { padding-right: 1.5rem !important; } .pe-xl-5 { padding-right: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-left: 0 !important; } .ps-xl-1 { padding-left: 0.25rem !important; } .ps-xl-2 { padding-left: 0.5rem !important; } .ps-xl-3 { padding-left: 1rem !important; } .ps-xl-4 { padding-left: 1.5rem !important; } .ps-xl-5 { padding-left: 3rem !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .text-xl-start { text-align: left !important; } .text-xl-end { text-align: right !important; } .text-xl-center { text-align: center !important; } } @media (min-width: 1400px) { .float-xxl-start { float: left !important; } .float-xxl-end { float: right !important; } .float-xxl-none { float: none !important; } .d-xxl-inline { display: inline !important; } .d-xxl-inline-block { display: inline-block !important; } .d-xxl-block { display: block !important; } .d-xxl-grid { display: grid !important; } .d-xxl-table { display: table !important; } .d-xxl-table-row { display: table-row !important; } .d-xxl-table-cell { display: table-cell !important; } .d-xxl-flex { display: flex !important; } .d-xxl-inline-flex { display: inline-flex !important; } .d-xxl-none { display: none !important; } .flex-xxl-fill { flex: 1 1 auto !important; } .flex-xxl-row { flex-direction: row !important; } .flex-xxl-column { flex-direction: column !important; } .flex-xxl-row-reverse { flex-direction: row-reverse !important; } .flex-xxl-column-reverse { flex-direction: column-reverse !important; } .flex-xxl-grow-0 { flex-grow: 0 !important; } .flex-xxl-grow-1 { flex-grow: 1 !important; } .flex-xxl-shrink-0 { flex-shrink: 0 !important; } .flex-xxl-shrink-1 { flex-shrink: 1 !important; } .flex-xxl-wrap { flex-wrap: wrap !important; } .flex-xxl-nowrap { flex-wrap: nowrap !important; } .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } .justify-content-xxl-start { justify-content: flex-start !important; } .justify-content-xxl-end { justify-content: flex-end !important; } .justify-content-xxl-center { justify-content: center !important; } .justify-content-xxl-between { justify-content: space-between !important; } .justify-content-xxl-around { justify-content: space-around !important; } .justify-content-xxl-evenly { justify-content: space-evenly !important; } .align-items-xxl-start { align-items: flex-start !important; } .align-items-xxl-end { align-items: flex-end !important; } .align-items-xxl-center { align-items: center !important; } .align-items-xxl-baseline { align-items: baseline !important; } .align-items-xxl-stretch { align-items: stretch !important; } .align-content-xxl-start { align-content: flex-start !important; } .align-content-xxl-end { align-content: flex-end !important; } .align-content-xxl-center { align-content: center !important; } .align-content-xxl-between { align-content: space-between !important; } .align-content-xxl-around { align-content: space-around !important; } .align-content-xxl-stretch { align-content: stretch !important; } .align-self-xxl-auto { align-self: auto !important; } .align-self-xxl-start { align-self: flex-start !important; } .align-self-xxl-end { align-self: flex-end !important; } .align-self-xxl-center { align-self: center !important; } .align-self-xxl-baseline { align-self: baseline !important; } .align-self-xxl-stretch { align-self: stretch !important; } .order-xxl-first { order: -1 !important; } .order-xxl-0 { order: 0 !important; } .order-xxl-1 { order: 1 !important; } .order-xxl-2 { order: 2 !important; } .order-xxl-3 { order: 3 !important; } .order-xxl-4 { order: 4 !important; } .order-xxl-5 { order: 5 !important; } .order-xxl-last { order: 6 !important; } .m-xxl-0 { margin: 0 !important; } .m-xxl-1 { margin: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xxl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xxl-2 { margin-right: 0.5rem !important; margin-left: 0.5rem !important; } .mx-xxl-3 { margin-right: 1rem !important; margin-left: 1rem !important; } .mx-xxl-4 { margin-right: 1.5rem !important; margin-left: 1.5rem !important; } .mx-xxl-5 { margin-right: 3rem !important; margin-left: 3rem !important; } .mx-xxl-auto { margin-right: auto !important; margin-left: auto !important; } .my-xxl-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-xxl-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; } .my-xxl-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-xxl-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; } .my-xxl-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-right: 0 !important; } .me-xxl-1 { margin-right: 0.25rem !important; } .me-xxl-2 { margin-right: 0.5rem !important; } .me-xxl-3 { margin-right: 1rem !important; } .me-xxl-4 { margin-right: 1.5rem !important; } .me-xxl-5 { margin-right: 3rem !important; } .me-xxl-auto { margin-right: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-left: 0 !important; } .ms-xxl-1 { margin-left: 0.25rem !important; } .ms-xxl-2 { margin-left: 0.5rem !important; } .ms-xxl-3 { margin-left: 1rem !important; } .ms-xxl-4 { margin-left: 1.5rem !important; } .ms-xxl-5 { margin-left: 3rem !important; } .ms-xxl-auto { margin-left: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xxl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xxl-2 { padding-right: 0.5rem !important; padding-left: 0.5rem !important; } .px-xxl-3 { padding-right: 1rem !important; padding-left: 1rem !important; } .px-xxl-4 { padding-right: 1.5rem !important; padding-left: 1.5rem !important; } .px-xxl-5 { padding-right: 3rem !important; padding-left: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; } .py-xxl-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-xxl-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; } .py-xxl-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-right: 0 !important; } .pe-xxl-1 { padding-right: 0.25rem !important; } .pe-xxl-2 { padding-right: 0.5rem !important; } .pe-xxl-3 { padding-right: 1rem !important; } .pe-xxl-4 { padding-right: 1.5rem !important; } .pe-xxl-5 { padding-right: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-left: 0 !important; } .ps-xxl-1 { padding-left: 0.25rem !important; } .ps-xxl-2 { padding-left: 0.5rem !important; } .ps-xxl-3 { padding-left: 1rem !important; } .ps-xxl-4 { padding-left: 1.5rem !important; } .ps-xxl-5 { padding-left: 3rem !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .text-xxl-start { text-align: left !important; } .text-xxl-end { text-align: right !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !important; } } @media print { .d-print-inline { display: inline !important; } .d-print-inline-block { display: inline-block !important; } .d-print-block { display: block !important; } .d-print-grid { display: grid !important; } .d-print-table { display: table !important; } .d-print-table-row { display: table-row !important; } .d-print-table-cell { display: table-cell !important; } .d-print-flex { display: flex !important; } .d-print-inline-flex { display: inline-flex !important; } .d-print-none { display: none !important; } } html { height: 100%; scroll-padding-top: calc(4.5rem - 1px); } .page-section { padding: 6rem 0; } .page-section .page-section-heading { font-size: 2.25rem; line-height: 2rem; } @media (min-width: 992px) { .page-section .page-section-heading { font-size: 3rem; line-height: 2.5rem; } } .divider-custom { margin: 1.25rem 0 1.5rem; width: 100%; display: flex; justify-content: center; align-items: center; } .divider-custom .divider-custom-line { width: 100%; max-width: 7rem; height: 0.25rem; background-color: #2c3e50; border-radius: 1rem; border-color: #2c3e50; } .divider-custom .divider-custom-line:first-child { margin-right: 1rem; } .divider-custom .divider-custom-line:last-child { margin-left: 1rem; } .divider-custom .divider-custom-icon { color: #2c3e50; font-size: 2rem; } .divider-custom.divider-light .divider-custom-line { background-color: #fff; } .divider-custom.divider-light .divider-custom-icon { color: #fff; } .btn-xl { padding: 1rem 1.75rem; font-size: 1.25rem; } .btn-social { border-radius: 100%; display: inline-flex; width: 3.25rem; height: 3.25rem; font-size: 1.25rem; justify-content: center; align-items: center; } #mainNav { padding-top: 1rem; padding-bottom: 1rem; font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-weight: 700; } #mainNav .navbar-brand { color: #fff; } #mainNav .navbar-nav { margin-top: 1rem; } #mainNav .navbar-nav li.nav-item a.nav-link { color: #fff; } #mainNav .navbar-nav li.nav-item a.nav-link:hover { color: #1abc9c; } #mainNav .navbar-nav li.nav-item a.nav-link:active, #mainNav .navbar-nav li.nav-item a.nav-link:focus { color: #fff; } #mainNav .navbar-nav li.nav-item a.nav-link.active { color: #1abc9c; } #mainNav .navbar-toggler { font-size: 80%; padding: 0.8rem; } @media (min-width: 992px) { #mainNav { padding-top: 1.5rem; padding-bottom: 1.5rem; transition: padding-top 0.3s, padding-bottom 0.3s; } #mainNav .navbar-brand { font-size: 1.75em; transition: font-size 0.3s; } #mainNav .navbar-nav { margin-top: 0; } #mainNav .navbar-nav > li.nav-item > a.nav-link.active { color: #fff; background: #1abc9c; } #mainNav .navbar-nav > li.nav-item > a.nav-link.active:active, #mainNav .navbar-nav > li.nav-item > a.nav-link.active:focus, #mainNav .navbar-nav > li.nav-item > a.nav-link.active:hover { color: #fff; background: #1abc9c; } #mainNav.navbar-shrink { padding-top: 0.5rem; padding-bottom: 0.5rem; } #mainNav.navbar-shrink .navbar-brand { font-size: 1.5em; } } .form-floating input.form-control, .form-floating textarea.form-control { font-size: 1.5rem; border-left: 0; border-right: 0; border-top: 0; border-radius: 0; border-width: 1px; } .form-floating input.form-control:focus, .form-floating textarea.form-control:focus { box-shadow: none; } .form-floating label { font-size: 1.5rem; color: #6c757d; } .masthead { padding-top: calc(6rem + 74px); padding-bottom: 6rem; } .masthead .masthead-heading { font-size: 2.75rem; line-height: 2.75rem; } .masthead .masthead-subheading { font-size: 1.25rem; } .masthead .masthead-avatar { width: 15rem; } @media (min-width: 992px) { .masthead { padding-top: calc(6rem + 104px); padding-bottom: 6rem; } .masthead .masthead-heading { font-size: 4rem; line-height: 3.5rem; } .masthead .masthead-subheading { font-size: 1.5rem; } } .portfolio .portfolio-item { cursor: pointer; position: relative; display: block; max-width: 25rem; border-radius: 0.5rem; overflow: hidden; } .portfolio .portfolio-item .portfolio-item-caption { position: absolute; top: 0; left: 0; transition: all 0.2s ease-in-out; opacity: 0; background-color: rgba(26, 188, 156, 0.9); } .portfolio .portfolio-item .portfolio-item-caption:hover { opacity: 1; } .portfolio .portfolio-item .portfolio-item-caption .portfolio-item-caption-content { font-size: 1.5rem; } .portfolio-modal .btn-close { color: #1abc9c; font-size: 2rem; padding: 1rem; } .portfolio-modal .portfolio-modal-title { font-size: 2.25rem; line-height: 2rem; } @media (min-width: 992px) { .portfolio-modal .portfolio-modal-title { font-size: 3rem; line-height: 2.5rem; } } .footer { padding-top: 5rem; padding-bottom: 5rem; background-color: #2c3e50; color: #fff; } .copyright { background-color: #1a252f; } ================================================ FILE: samples/s3-cloudfront-static-website/website/error.html ================================================ Freelancer - Start Bootstrap Theme
...

404 Error

Error Occured!

Location

2215 John Daniel Drive
Clark, MO 65243

Around the Web

About Freelancer

Freelance is a free to use, MIT licensed Bootstrap theme created by Start Bootstrap .

================================================ FILE: samples/s3-cloudfront-static-website/website/index.html ================================================ Freelancer - Start Bootstrap Theme
...

Start Bootstrap

Graphic Artist - Web Designer - Illustrator

Portfolio

...
...
...
...
...
...

About

Freelancer is a free bootstrap theme created by Start Bootstrap. The download includes the complete source files including HTML, CSS, and JavaScript as well as optional SASS stylesheets for easy customization.

You can create your own custom avatar for the masthead, change the icon in the dividers, and add your email address to the contact form to make it fully functional!

Contact Me

A name is required.
An email is required.
Email is not valid.
A phone number is required.
A message is required.
Form submission successful!
To activate this form, sign up at
https://startbootstrap.com/solution/contact-forms
Error sending message!

Location

2215 John Daniel Drive
Clark, MO 65243

Around the Web

About Freelancer

Freelance is a free to use, MIT licensed Bootstrap theme created by Start Bootstrap .

================================================ FILE: samples/s3-cloudfront-static-website/website/js/scripts.js ================================================ /*! * Start Bootstrap - Freelancer v7.0.7 (https://startbootstrap.com/theme/freelancer) * Copyright 2013-2023 Start Bootstrap * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-freelancer/blob/master/LICENSE) */ // // Scripts // window.addEventListener('DOMContentLoaded', event => { // Navbar shrink function var navbarShrink = function () { const navbarCollapsible = document.body.querySelector('#mainNav'); if (!navbarCollapsible) { return; } if (window.scrollY === 0) { navbarCollapsible.classList.remove('navbar-shrink') } else { navbarCollapsible.classList.add('navbar-shrink') } }; // Shrink the navbar navbarShrink(); // Shrink the navbar when page is scrolled document.addEventListener('scroll', navbarShrink); // Activate Bootstrap scrollspy on the main nav element const mainNav = document.body.querySelector('#mainNav'); if (mainNav) { new bootstrap.ScrollSpy(document.body, { target: '#mainNav', rootMargin: '0px 0px -40%', }); }; // Collapse responsive navbar when toggler is visible const navbarToggler = document.body.querySelector('.navbar-toggler'); const responsiveNavItems = [].slice.call( document.querySelectorAll('#navbarResponsive .nav-link') ); responsiveNavItems.map(function (responsiveNavItem) { responsiveNavItem.addEventListener('click', () => { if (window.getComputedStyle(navbarToggler).display !== 'none') { navbarToggler.click(); } }); }); });