fix: enable automatic testing and refactor tests (#51)

Signed-off-by: matttrach <matt.trachier@suse.com>
This commit is contained in:
Matt Trachier 2025-01-13 07:57:45 -06:00 committed by GitHub
parent 051b959168
commit 5f3c4e9825
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 2361 additions and 883 deletions

27
.envrc
View File

@ -4,17 +4,39 @@ nf () {
nix --extra-experimental-features nix-command --extra-experimental-features flakes "$@"
}
get_repo_basename() {
basename "$(git rev-parse --show-toplevel)"
}
get_profile() {
basename="$(get_repo_basename)"
echo "$HOME/.config/nix/profiles/$basename"
}
cleanup() {
echo "Cleaning Up..."
nix-collect-garbage --delete-older-than 30d
basename="$(get_repo_basename)"
profile="$(get_profile)"
if [ -z "$basename" ]; then echo "basename is empty"; exit 1; fi
export NIX_PATH="$profile"
export NIX_PROFILE="$profile"
nix-env --profile "$profile" --delete-generations +3
nix-env --profile "$profile" --list-generations
}
if ! which "$0" | grep -q nix; then
print 'Entering Environment...'
basename="$(get_repo_basename)"
profile="$(get_profile)"
export NIX_PROFILE="$profile"
print 'Updating Nix Cache...'
nf flake update
echo 'Installing Nix Profile...'
nf profile install . --profile "$profile"
nf profile list --profile "$profile"
print 'Starting...'
# --impure allows Nix to reuse previously built paths
# --ignore-environment ignores the environment variables and paths to tools not installed by nix
@ -34,7 +56,8 @@ if ! which "$0" | grep -q nix; then
--keep TERM \
--keep XDG_DATA_DIRS \
--keep NIX_SSL_CERT_FILE \
. \
--keep NIX_PROFILE \
--profile "$profile" \
--command bash -c "bash --rcfile .envrc"
print 'Exiting Dev Environment...'

View File

@ -71,13 +71,15 @@ jobs:
with:
token: ${{secrets.GITHUB_TOKEN}}
fetch-depth: 0
- uses: aws-actions/configure-aws-credentials@v4
- id: aws-creds
uses: aws-actions/configure-aws-credentials@v4
if: steps.release-please.outputs.pr
with:
role-to-assume: ${{env.AWS_ROLE}}
role-session-name: ${{github.run_id}}
aws-region: ${{env.AWS_REGION}}
role-duration-seconds: 7200 # 2 hours
output-credentials: true
- name: install-nix
run: |
curl -L https://nixos.org/nix/install | sh
@ -88,6 +90,9 @@ jobs:
if: steps.release-please.outputs.pr
shell: '/home/runner/.nix-profile/bin/nix develop --ignore-environment --extra-experimental-features nix-command --extra-experimental-features flakes --keep HOME --keep SSH_AUTH_SOCK --keep IDENTIFIER --keep GITHUB_TOKEN --keep GITHUB_OWNER --keep ZONE --keep AWS_ROLE --keep AWS_REGION --keep AWS_DEFAULT_REGION --keep AWS_ACCESS_KEY_ID --keep AWS_SECRET_ACCESS_KEY --keep AWS_SESSION_TOKEN --keep UPDATECLI_GPGTOKEN --keep UPDATECLI_GITHUB_TOKEN --keep UPDATECLI_GITHUB_ACTOR --keep GPG_SIGNING_KEY --keep NIX_SSL_CERT_FILE --keep NIX_ENV_LOADED --keep TERM --command bash -e {0}'
env:
AWS_ACCESS_KEY_ID: ${{ steps.aws-creds.outputs.aws-access-key-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.aws-creds.outputs.aws-secret-access-key }}
AWS_SESSION_TOKEN: ${{ steps.aws-creds.outputs.aws-session-token }}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
GITHUB_OWNER: rancher
IDENTIFIER: ${{github.run_id}}

View File

@ -75,6 +75,6 @@ These tools are not necessary, but they can make it much simpler to collaborate.
#### Automated Tests
Our continuous integration tests using the GitHub [ubuntu-latest runner](https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md) which has many different things installed and does not rely on Nix.
Our continuous integration tests using the GitHub [ubuntu-latest runner](https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md), we then rely on Nix to deploy the additional dependencies.
It also has special integrations with AWS to allow secure authentication, see https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services for more information.

View File

@ -0,0 +1,6 @@
# Deploy RKE2 Example
This basic example shows a normal path to deploying Rancher with a downstream RKE2 with IAC.
Because this is considered a normal deployment path it is useful when troubleshooting user issues that come up from time to time.
However, due to the security concerns and the way we authenticate our automation it won't be automatically tested.

View File

@ -0,0 +1,271 @@
provider "aws" {
default_tags {
tags = {
Id = local.identifier
Owner = local.owner
}
}
region = local.aws_region
}
provider "acme" {
server_url = "https://acme-v02.api.letsencrypt.org/directory"
}
provider "github" {}
provider "kubernetes" {} # make sure you set the env variable KUBE_CONFIG_PATH to local_file_path (file_path variable)
provider "helm" {} # make sure you set the env variable KUBE_CONFIG_PATH to local_file_path (file_path variable)
provider "rancher2" {
alias = "authenticate"
bootstrap = true
api_url = "https://${local.domain}.${local.zone}"
timeout = "300s"
}
resource "rancher2_bootstrap" "authenticate" {
provider = rancher2.authenticate
initial_password = module.rancher.admin_password
password = module.rancher.admin_password
token_update = true
token_ttl = 7200 # 2 hours
}
provider "rancher2" {
alias = "default"
api_url = "https://${local.domain}.${local.zone}"
token_key = rancher2_bootstrap.authenticate.token
timeout = "300s"
}
locals {
identifier = var.identifier
example = "basic"
project_name = "tf-${substr(md5(join("-", [local.example, local.identifier])), 0, 5)}"
username = local.project_name
domain = local.project_name
zone = var.zone
key_name = var.key_name
key = var.key
owner = var.owner
rke2_version = var.rke2_version
local_file_path = var.file_path
runner_ip = chomp(data.http.myip.response_body) # "runner" is the server running Terraform
rancher_version = var.rancher_version
rancher_helm_repository = "https://releases.rancher.com/server-charts/stable"
cert_manager_version = "1.13.1"
os = "sle-micro-60"
aws_access_key_id = var.aws_access_key_id
aws_secret_access_key = var.aws_secret_access_key
aws_region = var.aws_region
email = (var.email != "" ? var.email : "${local.identifier}@${local.zone}")
private_ip = replace(module.rancher.private_endpoint, "http://", "")
hostname_prefix = "n10y02rke2"
}
data "http" "myip" {
url = "https://ipinfo.io/ip"
}
module "rancher" {
source = "../../"
# project
identifier = local.identifier
owner = local.owner
project_name = local.project_name
domain = local.domain
zone = local.zone
skip_project_cert_generation = true
# access
key_name = local.key_name
key = local.key
username = local.username
admin_ip = local.runner_ip
# rke2
rke2_version = local.rke2_version
local_file_path = local.local_file_path
install_method = "rpm" # rpm only for now, need to figure out local helm chart installs otherwise
cni = "canal"
node_configuration = {
"rancher" = {
type = "all-in-one"
size = "large"
os = local.os
indirect_access = true
initial = true
}
}
# rancher
rancher_version = local.rancher_version
rancher_helm_repository = local.rancher_helm_repository
cert_manager_version = local.cert_manager_version
configure_cert_manager = true
cert_manager_configuration = {
aws_access_key_id = local.aws_access_key_id
aws_secret_access_key = local.aws_secret_access_key
aws_region = local.aws_region
email = local.email
}
}
module "rke2_image" {
source = "rancher/server/aws"
version = "v1.3.1"
server_use_strategy = "skip"
image_use_strategy = "find"
image_type = local.os
}
# this adds the private (10.) IP to the domain
# the private IP communicates to the agents where to find Rancher
resource "aws_route53_record" "modified" {
depends_on = [
module.rancher,
]
zone_id = module.rancher.domain_object.zone_id
name = module.rancher.domain_object.name
type = module.rancher.domain_object.type
ttl = 30
records = concat([local.private_ip], tolist(module.rancher.domain_object.records))
allow_overwrite = true
}
resource "rancher2_cloud_credential" "aws" {
depends_on = [
rancher2_bootstrap.authenticate,
module.rancher,
aws_route53_record.modified,
]
provider = rancher2.default
name = "aws"
description = "amazon ec2"
amazonec2_credential_config {
access_key = local.aws_access_key_id
secret_key = local.aws_secret_access_key
default_region = local.aws_region
}
}
resource "rancher2_machine_config_v2" "core" {
depends_on = [
rancher2_bootstrap.authenticate,
module.rancher,
aws_route53_record.modified,
rancher2_cloud_credential.aws,
]
provider = rancher2.default
generate_name = "core-rke2-template"
amazonec2_config {
ami = module.rke2_image.image.id
region = local.aws_region
security_group = [module.rancher.security_group.name]
subnet_id = module.rancher.subnets[keys(module.rancher.subnets)[0]].id
vpc_id = module.rancher.vpc.id
zone = replace( # it is looking for just the last letter of the availability zone, eg. for us-west-2a it just wants 'a'
module.rancher.subnets[keys(module.rancher.subnets)[0]].availability_zone,
local.aws_region,
""
)
instance_type = "m5.large"
ssh_user = "ec2-user"
userdata = <<-EOT
#cloud-config
bootcmd:
- echo ${local.private_ip} ${local.domain}.${local.zone} >> /etc/hosts
EOT
tags = join(",", ["Id", local.identifier, "Owner", local.owner])
}
}
resource "rancher2_machine_config_v2" "worker" {
depends_on = [
rancher2_bootstrap.authenticate,
module.rancher,
aws_route53_record.modified,
rancher2_cloud_credential.aws,
]
provider = rancher2.default
generate_name = "worker-rke2-template"
amazonec2_config {
ami = module.rke2_image.image.id
region = local.aws_region
security_group = [module.rancher.security_group.name]
subnet_id = module.rancher.subnets[keys(module.rancher.subnets)[0]].id
vpc_id = module.rancher.vpc.id
zone = replace( # it is looking for just the last letter of the availability zone, eg. for us-west-2a it just wants 'a'
module.rancher.subnets[keys(module.rancher.subnets)[0]].availability_zone,
local.aws_region,
""
)
instance_type = "m5.large"
ssh_user = "ec2-user"
userdata = <<-EOT
#cloud-config
bootcmd:
- echo ${local.private_ip} ${local.domain}.${local.zone} >> /etc/hosts
EOT
tags = join(",", ["Id", local.identifier, "Owner", local.owner])
}
}
resource "rancher2_cluster_v2" "rke2_cluster" {
depends_on = [
module.rancher,
rancher2_bootstrap.authenticate,
module.rancher,
aws_route53_record.modified,
rancher2_cloud_credential.aws,
rancher2_machine_config_v2.core,
rancher2_machine_config_v2.worker,
]
provider = rancher2.default
name = "${local.project_name}-s1-cluster"
kubernetes_version = local.rke2_version
enable_network_policy = true
rke_config {
machine_pools {
name = "${local.hostname_prefix}cp001"
control_plane_role = true
etcd_role = true
worker_role = true
quantity = 1
cloud_credential_secret_name = rancher2_cloud_credential.aws.id
machine_config {
kind = rancher2_machine_config_v2.core.kind
name = rancher2_machine_config_v2.core.name
}
}
machine_pools {
name = "${local.hostname_prefix}wk001"
control_plane_role = true
etcd_role = true
worker_role = true
quantity = 1
cloud_credential_secret_name = rancher2_cloud_credential.aws.id
machine_config {
kind = rancher2_machine_config_v2.worker.kind
name = rancher2_machine_config_v2.worker.name
}
}
rotate_certificates {
generation = 1
}
}
timeouts {
create = "120m" # 2 hours
}
}
resource "rancher2_cluster_sync" "sync" {
depends_on = [
module.rancher,
rancher2_bootstrap.authenticate,
module.rancher,
aws_route53_record.modified,
rancher2_cloud_credential.aws,
rancher2_machine_config_v2.core,
rancher2_machine_config_v2.worker,
rancher2_cluster_v2.rke2_cluster,
]
provider = rancher2.default
cluster_id = rancher2_cluster_v2.rke2_cluster.cluster_v1_id
}

View File

@ -0,0 +1,22 @@
output "kubeconfig" {
value = module.rancher.kubeconfig
description = <<-EOT
The kubeconfig for the server.
EOT
sensitive = true
}
output "address" {
value = module.rancher.address
}
# output "admin_token" {
# value = module.rancher.admin_token
# sensitive = true
# }
output "admin_password" {
value = module.rancher.admin_password
sensitive = true
}
# output "rke2_cluster_subnet" {
# value = module.rke2_cluster_access.subnets[keys(module.rke2_cluster_access.subnets)[0]]
# }

View File

@ -0,0 +1,84 @@
variable "key_name" {
type = string
description = <<-EOT
The name of an AWS key pair to use for SSH access to the instance.
This key should already be added to your ssh agent for server authentication.
EOT
}
variable "key" {
type = string
description = <<-EOT
The contents of an AWS key pair to use for SSH access to the instance.
This is necessary for installing rke2 on the nodes and will be removed after installation.
EOT
}
variable "identifier" {
type = string
description = <<-EOT
A unique identifier for the project, this helps when generating names for infrastructure items."
EOT
}
variable "owner" {
type = string
description = <<-EOT
The owner of the project, this helps when generating names for infrastructure items."
EOT
}
variable "zone" {
type = string
description = <<-EOT
The Route53 DNS zone to deploy the cluster into.
This is used to generate the DNS name for the cluster.
The zone must already exist.
EOT
}
variable "rke2_version" {
type = string
description = <<-EOT
The version of rke2 to install on the nodes.
eg. v1.30.2+rke2r1
EOT
}
variable "rancher_version" {
type = string
description = <<-EOT
The version of rancher to install on the rke2 cluster.
EOT
default = "2.9.2"
}
variable "file_path" {
type = string
description = <<-EOT
The path to the file containing the rke2 install script.
EOT
default = "./rke2"
}
variable "aws_access_key_id" {
type = string
description = <<-EOT
AWS access key ID.
EOT
sensitive = true
}
variable "aws_secret_access_key" {
type = string
description = <<-EOT
AWS secret key for EC2 services.
EOT
sensitive = true
}
variable "aws_region" {
type = string
description = <<-EOT
AWS region EC2 services.
EOT
sensitive = true
}
variable "email" {
type = string
description = <<-EOT
Email used for TLS certification registration.
If left blank this will be <identifier>@<zone>.
EOT
default = ""
}

View File

@ -0,0 +1,53 @@
terraform {
required_version = ">= 1.5.0, < 1.6"
required_providers {
local = {
source = "hashicorp/local"
version = ">= 2.4"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.1"
}
github = {
source = "integrations/github"
version = ">= 5.44"
}
aws = {
source = "hashicorp/aws"
version = ">= 5.11"
}
http = {
source = "hashicorp/http"
version = ">= 3.4"
}
null = {
source = "hashicorp/null"
version = ">=3"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
}
cloudinit = {
source = "hashicorp/cloudinit"
version = ">= 2.3.3"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.14"
}
rancher2 = {
source = "rancher/rancher2"
version = ">= 5.0.0, < 5.1"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.31.0"
}
}
}

View File

@ -0,0 +1,78 @@
# provider "aws" {
# default_tags {
# tags = {
# Id = local.identifier
# Owner = local.owner
# }
# }
# }
# provider "acme" {
# server_url = "https://acme-v02.api.letsencrypt.org/directory"
# }
# provider "github" {}
# provider "kubernetes" {} # make sure you set the env variable KUBE_CONFIG_PATH to local_file_path (file_path variable)
# provider "helm" {} # make sure you set the env variable KUBE_CONFIG_PATH to local_file_path (file_path variable)
# provider "rancher2" {
# alias = "authenticate"
# bootstrap = true
# api_url = "https://${local.domain}.${local.zone}"
# timeout = "300s"
# }
# resource "rancher2_bootstrap" "authenticate" {
# provider = rancher2.authenticate
# initial_password = module.rancher.admin_password
# password = module.rancher.admin_password
# token_update = true
# token_ttl = 7200 # 2 hours
# }
# provider "rancher2" {
# alias = "default"
# api_url = "https://${local.domain}.${local.zone}"
# token_key = rancher2_bootstrap.authenticate.token
# timeout = "300s"
# }
# locals {
# identifier = var.identifier
# example = "basic"
# project_name = "tf-${substr(md5(join("-", [local.example, local.identifier])), 0, 5)}"
# username = local.project_name
# domain = local.project_name
# zone = var.zone
# key_name = var.key_name
# key = var.key
# owner = var.owner
# rke2_version = var.rke2_version
# local_file_path = var.file_path
# runner_ip = chomp(data.http.myip.response_body) # "runner" is the server running Terraform
# rancher_version = var.rancher_version
# rancher_helm_repository = "https://releases.rancher.com/server-charts/stable"
# cert_manager_version = "v1.13.1"
# os = "sle-micro-60"
# }
# data "http" "myip" {
# url = "https://ipinfo.io/ip"
# }
# module "imported_rke2" {
# source = "./modules/cluster"
# }
# module "rancher" {
# source = "./modules/rancher"
# }
# # test catalog entry
# resource "rancher2_catalog" "foo" {
# depends_on = [
# module.rancher,
# ]
# provider = rancher2.default
# name = "test"
# url = "http://foo.com:8080"
# }

View File

@ -0,0 +1,4 @@
cni: calico
disable:
- rke2-canal
- rke2-kube-proxy

View File

@ -0,0 +1 @@
cni: cilium

View File

@ -0,0 +1,389 @@
locals {
# project
identifier = var.identifier # this is a random unique string that can be used to identify resources in the cloud provider
owner = var.owner
project_name = var.project_name
domain = var.domain
zone = var.zone # DNS zone
ip_family = var.ip_family
skip_cert = var.skip_cert_creation
# access
ssh_key_name = var.key_name
ssh_key = trimspace(var.key)
username = var.username
runner_ip = var.runner_ip
#rke2
rke2_version = var.rke2_version
local_file_path = (
var.file_path != "" ? (var.file_path == path.root ? "${abspath(path.root)}/rke2" : abspath(var.file_path)) :
"${abspath(path.root)}/rke2"
)
install_method = var.install_method
download = (local.install_method == "tar" ? "download" : "skip")
cni = var.cni
# tflint-ignore: terraform_unused_declarations
ingress_controller = var.ingress_controller # not currently in use, TODO: add traefik functionality
cni_file = (local.cni == "cilium" ? "${path.root}/cilium.yaml" : (local.cni == "calico" ? "${path.root}/calico.yaml" : ""))
cni_config = (local.cni_file != "" ? file(local.cni_file) : "")
api_config = <<-EOT
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
disable-etcd: true
${local.cni_config}
EOT
database_config = <<-EOT
disable-apiserver: true
disable-controller-manager: true
disable-scheduler: true
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
EOT
control_plane_config = <<-EOT
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
${local.cni_config}
EOT
all_in_one_config = <<-EOT
${local.cni_config}
EOT
ino = module.initial[keys(local.initial_node)[0]]
subnets = local.ino.project_subnets
node_configuration = var.node_configuration
full_node_configs = { for key, node in local.node_configuration :
key => {
name = substr("${local.project_name}-${md5(key)}", 0, 25)
domain = substr("${local.project_name}-${md5(key)}", 0, 25)
indirect_access = (node.indirect_access ? "enable" : "skip")
file_path = "${local.local_file_path}/${substr("${local.project_name}-${md5(key)}", 0, 25)}/data"
path = "${local.local_file_path}/${substr("${local.project_name}-${md5(key)}", 0, 25)}"
config = (
strcontains(node.type, "all-in-one") ? local.all_in_one_config :
strcontains(node.type, "control-plane") ? local.control_plane_config :
strcontains(node.type, "api") ? local.api_config :
strcontains(node.type, "database") ? local.database_config :
"" # worker nodes don't need additional config
)
config_strategy = "merge"
role = node.type
type = (strcontains(node.type, "worker") ? "agent" : "server")
size = node.size
image = node.os
prep_script = (
strcontains(node.os, "sles") ? templatefile("${path.root}/suse_prep.sh", {
install_method = local.install_method,
ip_family = local.ip_family,
image = node.os,
}) :
strcontains(node.os, "rhel") ? templatefile("${path.root}/rhel_prep.sh", {
install_method = local.install_method,
ip_family = local.ip_family,
image = node.os,
}) :
strcontains(node.os, "ubuntu") ? templatefile("${path.root}/ubuntu_prep.sh", {
install_method = local.install_method,
ip_family = local.ip_family,
image = node.os,
}) :
""
)
initial = node.initial
workfolder = strcontains(node.os, "cis") ? "/var/tmp" : "/home/${local.username}"
cloudinit_strategy = (node.os == "sle-micro-55" || node.os == "cis-rhel-8") ? "skip" : "default"
# CIS images are not supported on IPv6 only deployments due to kernel modifications with how AWS IPv6 works (dhcpv6)
# tflint-ignore: terraform_unused_declarations
fail_cis_ipv6 = ((node.os == "rhel-8-cis" && local.ip_family == "ipv6") ? one([local.ip_family, "cis_ipv6_incompatible"]) : false)
# Ubuntu images do not support rpm install method
# tflint-ignore: terraform_unused_declarations
fail_ubuntu_rpm = ((strcontains(node.os, "ubuntu") && local.install_method == "rpm") ? one([local.install_method, "ubuntu_rpm_incompatible"]) : false)
}
}
target_groups = {
kubectl = substr(lower("${local.project_name}-kubectl"), 0, 32)
application-secure = substr(lower("${local.project_name}-application-secure"), 0, 32)
application-insecure = substr(lower("${local.project_name}-application-insecure"), 0, 32)
}
initial_node = { for k, v in local.full_node_configs : k => v if v.initial == true }
additional_nodes = {
for k, v in local.full_node_configs :
k => merge(
v,
tomap({ az = local.subnets[keys(local.subnets)[index(keys(local.full_node_configs), k) % length(local.subnets)]].availability_zone }),
tomap({ subnet = local.subnets[keys(local.subnets)[index(keys(local.full_node_configs), k) % length(local.subnets)]].tags.Name })
)
if v.initial != true
}
}
data "aws_availability_zones" "available" {
state = "available"
}
module "initial" {
depends_on = [
data.aws_availability_zones.available,
]
source = "rancher/rke2/aws"
version = "1.1.11"
for_each = local.initial_node
project_use_strategy = "create"
project_vpc_use_strategy = "create"
project_vpc_name = "${local.project_name}-vpc"
project_vpc_zones = data.aws_availability_zones.available.names
project_vpc_type = local.ip_family
project_vpc_public = local.ip_family == "ipv6" ? false : true # ipv6 addresses assigned by AWS are always public
project_subnet_use_strategy = "create"
project_subnet_names = [for z in data.aws_availability_zones.available.names : "${local.project_name}-subnet-${z}"]
project_security_group_use_strategy = "create"
project_security_group_name = "${local.project_name}-sg"
project_security_group_type = "egress" # in the future we should allow this to be variable, but we need to figure out airgap first
project_load_balancer_use_strategy = "create"
project_load_balancer_name = "${local.project_name}-lb"
project_load_balancer_access_cidrs = {
"kubectl" = {
port = "6443"
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
target_name = local.target_groups.kubectl
}
"application-secure" = {
port = "443"
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
target_name = local.target_groups.application-secure
}
"application-insecure" = {
port = "80"
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
target_name = local.target_groups.application-insecure
}
}
project_domain_use_strategy = "create"
project_domain = local.domain
project_domain_zone = local.zone
project_domain_cert_use_strategy = (local.skip_cert ? "skip" : "create")
server_use_strategy = "create"
server_name = each.value.name
server_type = each.value.size
server_availability_zone = data.aws_availability_zones.available.names[0]
server_image_use_strategy = "find"
server_image_type = each.value.image
server_ip_family = local.ip_family
server_cloudinit_use_strategy = each.value.cloudinit_strategy
server_indirect_access_use_strategy = each.value.indirect_access
server_load_balancer_target_groups = values(local.target_groups)
server_direct_access_use_strategy = "ssh" # configure the servers for direct ssh access
#https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade/installation-requirements/port-requirements#rancher-aws-ec2-security-group
server_access_addresses = { # you must include ssh access here to enable setup
ssh = {
port = 22 # allow runner access on ssh port
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
}
api = {
port = 6443 # allow runner IP access to API
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
}
application-secure = {
port = 443 # allow runner IP access to https
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
}
application-insecure = {
port = 80 # allow runner IP access to http
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["${local.runner_ip}/128"] : ["${local.runner_ip}/32"])
}
}
#server_load_balancer_target_groups = values(local.target_groups) first node is an etcd node, not an API node, we use the internal LB for that
server_user = {
user = local.username
aws_keypair_use_strategy = "select"
ssh_key_name = local.ssh_key_name
public_ssh_key = local.ssh_key
user_workfolder = each.value.workfolder
timeout = 10
}
server_add_domain = false
server_domain_name = each.value.domain
server_domain_zone = local.zone
server_add_eip = false
install_use_strategy = local.install_method
local_file_use_strategy = local.download
local_file_path = each.value.file_path
install_rke2_version = local.rke2_version
install_rpm_channel = "stable"
install_remote_file_path = "${each.value.workfolder}/rke2"
install_role = each.value.type
install_start = true
install_prep_script = each.value.prep_script
install_start_timeout = 10
config_use_strategy = each.value.config_strategy
config_join_strategy = "skip"
config_default_name = "50-default-config.yaml"
config_supplied_name = "51-config.yaml"
config_supplied_content = each.value.config
retrieve_kubeconfig = true
}
# There are many ways to orchestrate Terraform configurations with the goal of breaking it down
# In this example I am using Terraform resources to orchestrate Terraform
# I felt this was the best way to accomplish the goal without incurring additional dependencies
# The configuration we are orchestrating isn't hard coded, we will be generating the config from a templatefile
# see "local_file.cp_main"
resource "terraform_data" "path" {
depends_on = [
module.initial,
]
for_each = local.additional_nodes
triggers_replace = {
initial_token = local.ino.join_token
initial_url = local.ino.join_url
}
provisioner "local-exec" {
command = <<-EOT
install -d ${each.value.path}
cp ${abspath(path.module)}/variables.tf ${each.value.path}
cp ${abspath(path.module)}/versions.tf ${each.value.path}
EOT
}
}
resource "local_file" "main" {
depends_on = [
module.initial,
terraform_data.path,
]
for_each = local.additional_nodes
content = templatefile(
"${abspath(path.module)}/main.tf.tftpl",
{
project_security_group_name = local.ino.project_security_group.name
project_subnets = jsonencode(local.ino.project_subnets)
join_url = local.ino.join_url
join_token = local.ino.join_token
cluster_cidr = jsonencode(local.ino.cluster_cidr)
service_cidr = jsonencode(local.ino.service_cidr)
server_info = jsonencode(each.value)
role = each.value.role # worker, control-plane, database, all-in-one, etc
target_groups = jsonencode(local.target_groups)
}
)
filename = "${each.value.path}/main.tf"
}
resource "local_file" "inputs" {
depends_on = [
module.initial,
terraform_data.path,
local_file.main,
]
for_each = local.additional_nodes
content = <<-EOT
identifier = "${local.identifier}"
owner = "${local.owner}"
project_name = "${local.project_name}"
domain = "${local.domain}"
zone = "${local.zone}"
key_name = "${local.ssh_key_name}"
key = "${local.ssh_key}"
username = "${local.username}"
runner_ip = "${local.runner_ip}"
rke2_version = "${local.rke2_version}"
file_path = "${each.value.file_path}"
install_method = "${local.install_method}"
cni = "${local.cni}"
ip_family = "${local.ip_family}"
ingress_controller = "${local.ingress_controller}"
EOT
filename = "${each.value.path}/inputs.tfvars"
}
resource "terraform_data" "create" {
depends_on = [
module.initial,
terraform_data.path,
local_file.main,
local_file.inputs,
]
for_each = local.additional_nodes
triggers_replace = {
initial = local.ino.join_url
path = each.value.path
}
provisioner "local-exec" {
command = <<-EOT
cd ${self.triggers_replace.path}
TF_DATA_DIR="${self.triggers_replace.path}"
terraform init -upgrade=true
EXITCODE=1
ATTEMPTS=0
MAX=3
while [ $EXITCODE -gt 0 ] && [ $ATTEMPTS -lt $MAX ]; do
echo "Starting attempt $ATTEMPTS..."
timeout 3600 terraform apply -var-file="inputs.tfvars" -auto-approve -state="${self.triggers_replace.path}/tfstate"
EXITCODE=$?
ATTEMPTS=$((ATTEMPTS + 1))
echo "Exit code $EXITCODE..."
done
exit $EXITCODE
EOT
}
provisioner "local-exec" {
# warning! this is only triggered on destroy, not refresh/taint
when = destroy
command = <<-EOT
cd ${self.triggers_replace.path}
TF_DATA_DIR="${self.triggers_replace.path}"
EXITCODE=1
ATTEMPTS=0
MAX=3
while [ $EXITCODE -gt 0 ] && [ $ATTEMPTS -lt $MAX ]; do
echo "Starting attempt $ATTEMPTS..."
timeout 3600 terraform destroy -var-file="inputs.tfvars" -no-color -auto-approve -state="${self.triggers_replace.path}/tfstate"
EXITCODE=$?
ATTEMPTS=$((ATTEMPTS + 1))
echo "Exit code $EXITCODE..."
done
exit $EXITCODE
EOT
}
}
resource "local_file" "kubeconfig" {
depends_on = [
module.initial,
terraform_data.path,
local_file.main,
local_file.inputs,
terraform_data.create,
]
content = local.ino.kubeconfig
filename = "${local.local_file_path}/kubeconfig"
}
# commented for performance, leaving to show how you can export the state if necessary
# data "terraform_remote_state" "additional_node_states" {
# depends_on = [
# module.initial,
# terraform_data.path,
# local_file.main,
# local_file.inputs,
# terraform_data.create,
# ]
# for_each = local.additional_nodes
# backend = "local"
# config = {
# path = "${each.value.path}/tfstate"
# }
# }

View File

@ -0,0 +1,135 @@
# control plane terraform templatefile
provider "aws" {
default_tags {
tags = {
Id = local.identifier
Owner = local.owner
}
}
}
locals {
# tflint-ignore: terraform_unused_declarations
ingress_controller = var.ingress_controller # not currently in use, TODO: add traefik functionality
identifier = var.identifier # this is a random unique string that can be used to identify resources in the cloud provider
owner = var.owner
project_name = var.project_name
username = var.username
ip_family = var.ip_family
runner_ip = var.runner_ip
ssh_key = var.key
ssh_key_name = var.key_name
domain = var.domain
zone = var.zone # DNS zone
rke2_version = var.rke2_version
install_method = var.install_method
download = (local.install_method == "tar" ? "download" : "skip")
server_info = jsondecode(<<-EOT
${server_info}
EOT
)
install_prep_script = local.server_info.prep_script
local_file_path = local.server_info.file_path
workfolder = local.server_info.workfolder
cloudinit_strategy = local.server_info.cloudinit_strategy
config = local.server_info.config
target_groups = jsondecode(<<-EOT
${target_groups}
EOT
)
cluster_cidr = jsondecode(<<-EOT
${cluster_cidr}
EOT
)
service_cidr = jsondecode(<<-EOT
${service_cidr}
EOT
)
}
data "http" "myip" {
url = "https://ipinfo.io/ip"
}
module "${role}_node" {
source = "rancher/rke2/aws"
version = "1.1.11"
project_use_strategy = "skip"
project_domain = local.domain
project_domain_zone = local.zone
server_use_strategy = "create"
server_name = local.server_info.name
server_type = local.server_info.size
server_security_group_name = "${project_security_group_name}" # should always match project security group
server_availability_zone = local.server_info.az
server_image_use_strategy = "find"
server_image_type = local.server_info.image
server_ip_family = local.ip_family
server_cloudinit_use_strategy = local.server_info.cloudinit_strategy
server_indirect_access_use_strategy = local.server_info.indirect_access
server_load_balancer_target_groups = values(local.target_groups)
server_subnet_name = local.server_info.subnet
server_direct_access_use_strategy = "ssh" # configure the servers for direct ssh access
# remember these are external access objects, internal access is enabled by default
server_access_addresses = { # you must include ssh access here to enable setup
ssh = {
port = 22 # allow access on ssh port
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["$${local.runner_ip}/128"] : ["$${local.runner_ip}/32"])
}
api = {
port = 6443 # allow runner IP access to API
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["$${local.runner_ip}/128"] : ["$${local.runner_ip}/32"])
}
application-secure = {
port = 443 # allow runner IP access to https
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["$${local.runner_ip}/128"] : ["$${local.runner_ip}/32"])
}
application-insecure = {
port = 80 # allow runner IP access to http
protocol = "tcp"
ip_family = (local.ip_family == "ipv6" ? "ipv6" : "ipv4")
cidrs = (local.ip_family == "ipv6" ? ["$${local.runner_ip}/128"] : ["$${local.runner_ip}/32"])
}
}
server_user = {
user = local.username
aws_keypair_use_strategy = "select"
ssh_key_name = local.ssh_key_name
public_ssh_key = local.ssh_key
user_workfolder = local.server_info.workfolder
timeout = 10
}
server_add_domain = false
server_domain_name = local.server_info.domain
server_domain_zone = local.zone
server_add_eip = false
install_use_strategy = local.install_method
local_file_use_strategy = local.download
local_file_path = local.server_info.file_path
install_rke2_version = local.rke2_version
install_rpm_channel = "stable"
install_remote_file_path = "$${local.server_info.workfolder}/rke2"
install_role = local.server_info.type
install_start = true
install_prep_script = local.server_info.prep_script
install_start_timeout = 10
config_use_strategy = local.server_info.config_strategy
config_join_strategy = "join"
config_join_url = "${join_url}"
config_join_token = "${join_token}"
config_cluster_cidr = local.cluster_cidr
config_service_cidr = local.service_cidr
config_supplied_content = local.server_info.config
config_supplied_name = "51-config.yaml"
retrieve_kubeconfig = false # use initial
}

View File

@ -0,0 +1,13 @@
output "kubeconfig" {
value = module.initial[0].kubeconfig
description = "Kubernetes config file contents for the cluster."
sensitive = true
}
output "api" {
value = yamldecode(module.initial[0].kubeconfig).clusters[0].cluster.server
description = "Address to use to connect to the cluster's API service."
sensitive = true
}
output "cert" {
value = module.initial[0].project_domain_tls_certificate
}

View File

@ -0,0 +1,39 @@
output "kubeconfig" {
value = local.ino.kubeconfig
description = "Kubernetes config file contents for the cluster."
sensitive = true
}
output "api" {
value = yamldecode(local.ino.kubeconfig).clusters[0].cluster.server
description = "Address to use to connect to the cluster's API service."
sensitive = true
}
output "cert" {
value = local.ino.project_domain_tls_certificate
description = "Information about the certificate that was generated with the domain"
sensitive = true
}
# commented for performance, leaving to show how you can export the state if necessary
# output "additional_node_states" {
# value = data.terraform_remote_state.additional_node_states
# description = "The states for the orchestrated modules which produce nodes."
# sensitive = true
# }
output "vpc" {
value = module.initial[keys(local.initial_node)[0]].project_vpc
}
output "subnets" {
value = module.initial[keys(local.initial_node)[0]].project_subnets
}
output "join_url" {
value = local.ino.join_url
}
output "initial_node_private_ip" {
value = replace(replace(local.ino.join_url, ":9345", ""), "https://", "")
}
output "project_domain_object" {
value = module.initial[keys(local.initial_node)[0]].project_domain_object
}
output "project_security_group" {
value = module.initial[keys(local.initial_node)[0]].project_security_group
}

View File

@ -0,0 +1,14 @@
#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root" >&2
exit 1
fi
#https://docs.rke2.io/known_issues
systemctl disable --now firewalld || true
systemctl stop firewalld || true
systemctl stop nm-cloud-setup.service
systemctl disable nm-cloud-setup.service
systemctl stop nm-cloud-setup.timer
systemctl disable nm-cloud-setup.timer

View File

@ -0,0 +1,55 @@
#!/bin/sh
set -e
set -x
# Ensure the script is run as root
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root" >&2
exit 1
fi
# shellcheck disable=SC2154
if [ "rpm" = "${install_method}" ]; then
wget https://download.opensuse.org/distribution/leap/15.6/repo/oss/repodata/repomd.xml.key || true
rpm --import repomd.xml.key || true
zypper ar -f https://download.opensuse.org/distribution/leap/15.6/repo/oss/ leap-oss || true
zypper install -y curl
rpm --import https://download.opensuse.org/repositories/security:/SELinux_legacy/15.5/repodata/repomd.xml.key || true
zypper ar -f https://download.opensuse.org/repositories/security:/SELinux_legacy/15.5/security:SELinux_legacy.repo || true
rpm --import https://rpm.rancher.io/public.key || true
fi
# shellcheck disable=SC2154
if [ "ipv6" = "${ip_family}" ]; then
IPV6="$(ip -6 a show eth0 | grep inet6 | head -n1 | awk '{ print $2 }' | awk -F/ '{ print $1 }')"
IPV6_GW="$(echo "$IPV6" | awk -F: '{gw=$1":"$2":"$3":"$4"::1"; print gw}')"
cat > /etc/sysconfig/network/ifcfg-eth0 << EOT
STARTMODE='auto'
BOOTPROTO='static'
IPADDR="$IPV6"
PREFIXLEN='64'
DHCLIENT6_MODE='info'
EOT
[ ! -f /etc/sysconfig/network/routes ] && touch /etc/sysconfig/network/routes
echo "default $IPV6_GW - -" >> /etc/sysconfig/network/routes
CONFIG_FILE="/etc/sysconfig/network/config"
IPV6_DNS1="2001:4860:4860::8888"
IPV6_DNS2="2606:4700:4700::1111"
sed -i "s|^NETCONFIG_DNS_STATIC_SERVERS=.*|NETCONFIG_DNS_STATIC_SERVERS=\"$IPV6_DNS1 $IPV6_DNS2\"|" "$CONFIG_FILE"
sed -i "s|^NETWORKMANAGER_DISABLE_IPV6=.*|NETWORKMANAGER_DISABLE_IPV6=\"no\"|" "$CONFIG_FILE"
grep -q "^NETCONFIG_DNS_STATIC_SERVERS=" "$CONFIG_FILE" || echo "NETCONFIG_DNS_STATIC_SERVERS=\"$IPV6_DNS1 $IPV6_DNS2\"" >> "$CONFIG_FILE"
grep -q "^NETWORKMANAGER_DISABLE_IPV6=" "$CONFIG_FILE" || echo "NETWORKMANAGER_DISABLE_IPV6=\"no\"" >> "$CONFIG_FILE"
netconfig update -f
echo "Updated /etc/resolv.conf:"
cat /etc/resolv.conf
echo "Testing IPv6 DNS resolution:"
dig AAAA google.com
fi

View File

@ -0,0 +1,18 @@
#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root" >&2
exit 1
fi
#https://docs.rke2.io/known_issues
systemctl disable --now firewalld || true
systemctl stop firewalld || true
if [ -d /etc/NetworkManager ]; then
touch /etc/NetworkManager/conf.d/rke2-canal.conf
cat <<EOF > /etc/NetworkManager/conf.d/rke2-canal.conf
[keyfile]
unmanaged-devices=interface-name:cali*;interface-name:flannel*
EOF
systemctl reload NetworkManager || true
fi

View File

@ -0,0 +1,123 @@
# project
variable "identifier" {
type = string
description = "A random alphanumeric string that is unique and less than 10 characters."
}
variable "owner" {
type = string
description = <<-EOT
An identifier for the person or group responsible for the resources created.
A tag 'Owner' will be added to the servers with this value.
EOT
}
variable "project_name" {
type = string
description = "The name for the project, resources will be given a tag 'Name' with this value as a prefix."
}
variable "domain" {
type = string
description = <<-EOT
The dns domain for the project, the zone must already exist in AWS Route53.
Example: test.example.com where example.com is a zone that is already available.
EOT
}
variable "zone" {
type = string
description = "The dns zone to add domains under, must already exist in AWS Route53."
}
# access
variable "key_name" {
type = string
description = "The name of an ssh key that already exists in AWS of that you want to create."
}
variable "key" {
type = string
description = "The content of an ssh key for server access. The key must be loaded into the running ssh agent."
}
variable "username" {
type = string
description = <<-EOT
The username to add to the server for Terraform to configure it.
This user will have passwordless sudo, but login only from the 'runner_ip' address
and only with the appropriate key (which must be in your ssh agent).
EOT
}
variable "runner_ip" {
type = string
description = "The IP address of the computer running terraform."
}
# rke2
variable "rke2_version" {
type = string
description = "The rke2 version to install."
}
variable "file_path" {
type = string
description = "The local file path to stage or retrieve files."
}
variable "install_method" {
type = string
description = "The method used to install RKE2 on the nodes. Must be either 'tar' or 'rpm'."
}
variable "cni" {
type = string
description = "Which CNI configuration file to add."
}
variable "node_configuration" {
type = map(object({
type = string
size = string
os = string
indirect_access = bool
initial = bool
}))
description = <<-EOT
A map of configuration options for the nodes to constitute the cluster.
Only one node should have the "initial" attribute set to true.
Be careful which node you decide to start the cluster,
it must host the database for others to be able to join properly.
There are 5 types of node: 'all-in-one', 'control-plane', 'worker', 'database', 'api'.
'all-in-one' nodes have all roles (control-plane, worker, etcd)
'control-plane' nodes have the api (control-plane) and database (etcd) roles
'worker' nodes have just the 'worker' role
'database' nodes have only the database (etcd) role
'api' nodes have only the api (control-plane) server role
By default we will set taints to prevent non-component workloads
from running on database, api, and control-plane nodes.
Size correlates to the server size options from the server module:
https://github.com/rancher/terraform-aws-server/blob/main/modules/server/types.tf
We recommend using the size nodes that best fit your use case:
https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade/installation-requirements#rke2-kubernetes
OS correlates to the server image options from the server module:
https://github.com/rancher/terraform-aws-server/blob/main/modules/image/types.tf
We recommend using the same os for all servers, we don't currently test for clusters with mixed OS types.
Indirect access refers to how the cluster will be load balanced,
some admins are ok with every server in the cluster responding to inbound requests since the built in proxy will redirect,
but that isn't always the best choice since some nodes (like database nodes and secure workers)
are better to restrict to internal access only.
Setting this value to true will allow the network load balancer to direct traffic to the node.
Setting this value to false will prevent the load balancer from directing traffic to the node.
EOT
default = {
"initial" = {
type = "all-in-one"
size = "medium"
os = "sle-micro-60"
indirect_access = true
initial = true
}
}
}
variable "ip_family" {
type = string
description = "The IP family to use. Must be 'ipv4', 'ipv6', or 'dualstack'."
}
variable "ingress_controller" {
type = string
description = "The ingress controller to use. Must be 'nginx' or 'traefik'. Currently only supports 'nginx'."
}
variable "skip_cert_creation" {
type = bool
description = "Skip the generation of a certificate, useful when configuring cert manager."
default = false
}

View File

@ -0,0 +1,29 @@
terraform {
required_version = ">= 1.5.7, < 1.6"
required_providers {
local = {
source = "hashicorp/local"
version = ">= 2.4"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.1"
}
aws = {
source = "hashicorp/aws"
version = ">= 5.11"
}
http = {
source = "hashicorp/http"
version = ">= 3.4"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
}
github = {
source = "integrations/github"
version = ">= 6.3"
}
}
}

View File

@ -0,0 +1,65 @@
locals {
identifier = var.identifier
example = "basic"
project_name = "tf-${substr(md5(join("-", [local.example, local.identifier])), 0, 5)}"
username = local.project_name
domain = local.project_name
zone = var.zone
key_name = var.key_name
key = var.key
owner = var.owner
rke2_version = var.rke2_version
local_file_path = var.file_path
runner_ip = chomp(data.http.myip.response_body) # "runner" is the server running Terraform
rancher_version = var.rancher_version
rancher_helm_repository = "https://releases.rancher.com/server-charts/stable"
cert_manager_version = "v1.13.1"
os = "sle-micro-60"
}
data "http" "myip" {
url = "https://ipinfo.io/ip"
}
module "rancher" {
source = "../../../../"
# project
identifier = local.identifier
owner = local.owner
project_name = local.project_name
domain = local.domain
zone = local.zone
# access
key_name = local.key_name
key = local.key
username = local.username
admin_ip = local.runner_ip
# rke2
rke2_version = local.rke2_version
local_file_path = local.local_file_path
install_method = "rpm" # rpm only for now, need to figure out local helm chart installs otherwise
cni = "canal"
node_configuration = {
"rancher" = {
type = "all-in-one"
size = "medium"
os = local.os
indirect_access = true
initial = true
}
}
# rancher
cert_manager_version = local.cert_manager_version
rancher_version = local.rancher_version
rancher_helm_repository = local.rancher_helm_repository
}
# test catalog entry
resource "rancher2_catalog" "foo" {
depends_on = [
module.rancher,
]
provider = rancher2.default
name = "test"
url = "http://foo.com:8080"
}

View File

@ -0,0 +1,18 @@
output "kubeconfig" {
value = module.rancher.kubeconfig
description = <<-EOT
The kubeconfig for the server.
EOT
sensitive = true
}
output "address" {
value = module.rancher.address
}
output "admin_token" {
value = module.rancher.admin_token
sensitive = true
}
output "admin_password" {
value = module.rancher.admin_password
sensitive = true
}

View File

@ -0,0 +1,54 @@
variable "key_name" {
type = string
description = <<-EOT
The name of an AWS key pair to use for SSH access to the instance.
This key should already be added to your ssh agent for server authentication.
EOT
}
variable "key" {
type = string
description = <<-EOT
The contents of an AWS key pair to use for SSH access to the instance.
This is necessary for installing rke2 on the nodes and will be removed after installation.
EOT
}
variable "identifier" {
type = string
description = <<-EOT
A unique identifier for the project, this helps when generating names for infrastructure items."
EOT
}
variable "owner" {
type = string
description = <<-EOT
The owner of the project, this helps when generating names for infrastructure items."
EOT
}
variable "zone" {
type = string
description = <<-EOT
The Route53 DNS zone to deploy the cluster into.
This is used to generate the DNS name for the cluster.
The zone must already exist.
EOT
}
variable "rke2_version" {
type = string
description = <<-EOT
The version of rke2 to install on the nodes.
EOT
}
variable "rancher_version" {
type = string
description = <<-EOT
The version of rancher to install on the rke2 cluster.
EOT
default = "2.9.1"
}
variable "file_path" {
type = string
description = <<-EOT
The path to the file containing the rke2 install script.
EOT
default = "./rke2"
}

View File

@ -0,0 +1,53 @@
terraform {
required_version = ">= 1.5.0, < 1.6"
required_providers {
local = {
source = "hashicorp/local"
version = ">= 2.4"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.1"
}
github = {
source = "integrations/github"
version = ">= 5.44"
}
aws = {
source = "hashicorp/aws"
version = ">= 5.11"
}
http = {
source = "hashicorp/http"
version = ">= 3.4"
}
null = {
source = "hashicorp/null"
version = ">=3"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
}
cloudinit = {
source = "hashicorp/cloudinit"
version = ">= 2.3.3"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.14"
}
rancher2 = {
source = "rancher/rancher2"
version = ">= 4.1.0, < 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.31.0"
}
}
}

View File

@ -0,0 +1,18 @@
# output "kubeconfig" {
# value = module.rancher.kubeconfig
# description = <<-EOT
# The kubeconfig for the server.
# EOT
# sensitive = true
# }
# output "address" {
# value = module.rancher.address
# }
# output "admin_token" {
# value = module.rancher.admin_token
# sensitive = true
# }
# output "admin_password" {
# value = module.rancher.admin_password
# sensitive = true
# }

View File

@ -0,0 +1,54 @@
# variable "key_name" {
# type = string
# description = <<-EOT
# The name of an AWS key pair to use for SSH access to the instance.
# This key should already be added to your ssh agent for server authentication.
# EOT
# }
# variable "key" {
# type = string
# description = <<-EOT
# The contents of an AWS key pair to use for SSH access to the instance.
# This is necessary for installing rke2 on the nodes and will be removed after installation.
# EOT
# }
# variable "identifier" {
# type = string
# description = <<-EOT
# A unique identifier for the project, this helps when generating names for infrastructure items."
# EOT
# }
# variable "owner" {
# type = string
# description = <<-EOT
# The owner of the project, this helps when generating names for infrastructure items."
# EOT
# }
# variable "zone" {
# type = string
# description = <<-EOT
# The Route53 DNS zone to deploy the cluster into.
# This is used to generate the DNS name for the cluster.
# The zone must already exist.
# EOT
# }
# variable "rke2_version" {
# type = string
# description = <<-EOT
# The version of rke2 to install on the nodes.
# EOT
# }
# variable "rancher_version" {
# type = string
# description = <<-EOT
# The version of rancher to install on the rke2 cluster.
# EOT
# default = "2.9.1"
# }
# variable "file_path" {
# type = string
# description = <<-EOT
# The path to the file containing the rke2 install script.
# EOT
# default = "./rke2"
# }

View File

@ -0,0 +1,53 @@
terraform {
required_version = ">= 1.5.0, < 1.6"
required_providers {
local = {
source = "hashicorp/local"
version = ">= 2.4"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.1"
}
github = {
source = "integrations/github"
version = ">= 5.44"
}
aws = {
source = "hashicorp/aws"
version = ">= 5.11"
}
http = {
source = "hashicorp/http"
version = ">= 3.4"
}
null = {
source = "hashicorp/null"
version = ">=3"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
}
cloudinit = {
source = "hashicorp/cloudinit"
version = ">= 2.3.3"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.14"
}
rancher2 = {
source = "rancher/rancher2"
version = ">= 4.1.0, < 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.31.0"
}
}
}

View File

@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1731245184,
"narHash": "sha256-vmLS8+x+gHRv1yzj3n+GTAEObwmhxmkkukB2DwtJRdU=",
"lastModified": 1736166416,
"narHash": "sha256-U47xeACNBpkSO6IcCm0XvahsVXpJXzjPIQG7TZlOToU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "aebe249544837ce42588aa4b2e7972222ba12e8f",
"rev": "b30f97d8c32d804d2d832ee837d0f1ca0695faa5",
"type": "github"
},
"original": {

View File

@ -69,6 +69,7 @@
less
openssh
openssl
pup
shellcheck
tflint
tfsec
@ -76,6 +77,7 @@
updatecli
vim
which
yq-go
];
};

View File

@ -1,71 +0,0 @@
module github.com/rancher/terraform-rancher2-aws
go 1.22
require (
github.com/aws/aws-sdk-go v1.44.122
github.com/google/go-github/v53 v53.2.0
github.com/gruntwork-io/terratest v0.43.8
github.com/stretchr/testify v1.8.1
golang.org/x/oauth2 v0.8.0
)
require (
cloud.google.com/go v0.105.0 // indirect
cloud.google.com/go/compute v1.12.1 // indirect
cloud.google.com/go/compute/metadata v0.2.1 // indirect
cloud.google.com/go/iam v0.7.0 // indirect
cloud.google.com/go/storage v1.27.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/gruntwork-io/go-commons v0.8.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.1 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl/v2 v2.9.1 // indirect
github.com/hashicorp/terraform-json v0.13.0 // indirect
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tmccombs/hcl2json v0.3.3 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/urfave/cli v1.22.2 // indirect
github.com/zclconf/go-cty v1.9.1 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.103.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

78
test/tests/go.mod Normal file
View File

@ -0,0 +1,78 @@
module github.com/rancher/terraform-rancher2-aws/test/tests
go 1.23.4
require (
github.com/aws/aws-sdk-go v1.55.5
github.com/google/go-github/v53 v53.2.0
github.com/gruntwork-io/terratest v0.47.2
golang.org/x/oauth2 v0.25.0
)
require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/storage v1.28.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
github.com/gruntwork-io/go-commons v0.8.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.6 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl/v2 v2.22.0 // indirect
github.com/hashicorp/terraform-json v0.23.0 // indirect
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.4.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tmccombs/hcl2json v0.6.4 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/urfave/cli v1.22.16 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.22.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.114.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
@ -68,10 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0=
cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
@ -109,14 +107,14 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs=
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
@ -172,8 +170,9 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ=
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
@ -186,21 +185,22 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@ -228,8 +228,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -253,11 +253,12 @@ github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRH
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -273,7 +274,6 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -310,8 +310,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI=
github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@ -320,8 +321,9 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@ -338,12 +340,14 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@ -353,33 +357,33 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro=
github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78=
github.com/gruntwork-io/terratest v0.43.8 h1:6JNZE1REO5B1AGbTvBvMDNBX/gxiO4LTkth5uFqBCbU=
github.com/gruntwork-io/terratest v0.43.8/go.mod h1:Tw+6/fcJFiBPpsx9NNSkLG5oHKIeaqiJHVLpQ+ORIfQ=
github.com/gruntwork-io/terratest v0.47.2 h1:t6iWwsqJH7Gx0RwXleU/vjc+2c0JXRMdj3DxYXTBssQ=
github.com/gruntwork-io/terratest v0.47.2/go.mod h1:LnYX8BN5WxUMpDr8rtD39oToSL4CBERWSCusbJ0d/64=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY=
github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-getter v1.7.6 h1:5jHuM+aH373XNtXl9TNTUH5Qd69Trve11tHIrB+6yj4=
github.com/hashicorp/go-getter v1.7.6/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl/v2 v2.9.1 h1:eOy4gREY0/ZQHNItlfuEZqtcQbXIxzojlP301hDpnac=
github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniyEi5CM+FWGY=
github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk=
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
@ -390,20 +394,20 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@ -413,36 +417,34 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg=
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -451,29 +453,27 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tmccombs/hcl2json v0.3.3 h1:+DLNYqpWE0CsOQiEZu+OZm5ZBImake3wtITYxQ8uLFQ=
github.com/tmccombs/hcl2json v0.3.3/go.mod h1:Y2chtz2x9bAeRTvSibVRVgbLJhLJXKlUeIvjeVdnm4w=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw=
github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.9.1 h1:viqrgQwFl5UpSxc046qblj78wZXVDFnSOufaOTER+cc=
github.com/zclconf/go-cty v1.9.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -485,14 +485,13 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -529,8 +528,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -577,8 +577,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -604,8 +604,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -620,6 +620,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -627,7 +629,6 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -688,13 +689,13 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -705,8 +706,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -747,6 +748,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@ -755,6 +757,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@ -762,6 +765,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -819,8 +824,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -930,8 +935,8 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c h1:S34D59DS2GWOEwWNt4fYmTcFrtlOgukG2k9WsomZ7tg=
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -967,8 +972,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -985,11 +990,12 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,407 +1,103 @@
package one
import (
"cmp"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"os"
"path/filepath"
"testing"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/google/go-github/v53/github"
aws "github.com/gruntwork-io/terratest/modules/aws"
g "github.com/gruntwork-io/terratest/modules/git"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/ssh"
"github.com/gruntwork-io/terratest/modules/terraform"
"golang.org/x/oauth2"
aws "github.com/gruntwork-io/terratest/modules/aws"
g "github.com/gruntwork-io/terratest/modules/git"
"github.com/gruntwork-io/terratest/modules/ssh"
"github.com/gruntwork-io/terratest/modules/terraform"
util "github.com/rancher/terraform-rancher2-aws/test/tests"
)
func TestOneBasic(t *testing.T) {
t.Parallel()
id := getId()
region := getRegion()
directory := "one"
owner := "terraform-ci@suse.com"
setAcmeServer()
t.Parallel()
id := util.GetId()
region := util.GetRegion()
directory := "one"
owner := "terraform-ci@suse.com"
util.SetAcmeServer()
repoRoot, err := filepath.Abs(g.GetRepoRoot(t))
if err != nil {
t.Fatalf("Error getting git root directory: %v", err)
}
repoRoot, err := filepath.Abs(g.GetRepoRoot(t))
if err != nil {
t.Fatalf("Error getting git root directory: %v", err)
}
exampleDir := repoRoot + "/examples/" + directory
testDir := repoRoot + "/test/tests/data/" + id
exampleDir := repoRoot + "/examples/" + directory
testDir := repoRoot + "/test/tests/data/" + id
err = createTestDirectories(t, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test data directories: %s", err)
}
keyPair, err := createKeypair(t, region, owner, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test key pair: %s", err)
}
sshAgent := ssh.SshAgentWithKeyPair(t, keyPair.KeyPair)
t.Logf("Key %s created and added to agent", keyPair.Name)
err = util.CreateTestDirectories(t, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test data directories: %s", err)
}
keyPair, err := util.CreateKeypair(t, region, owner, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test key pair: %s", err)
}
sshAgent := ssh.SshAgentWithKeyPair(t, keyPair.KeyPair)
t.Logf("Key %s created and added to agent", keyPair.Name)
_, _, rke2Version, err := GetRke2Releases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
sshAgent.Stop()
t.Fatalf("Error getting Rke2 release version: %s", err)
}
// use oldest RKE2, remember it releases much more than Rancher
_, _, rke2Version, err := util.GetRke2Releases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
sshAgent.Stop()
t.Fatalf("Error getting Rke2 release version: %s", err)
}
// use latest Rancher, due to community patch issue
rancherVersion, _, _, err := util.GetRancherReleases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
sshAgent.Stop()
t.Fatalf("Error getting Rancher release version: %s", err)
}
rancherVersion, _, _, err := GetRancherReleases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
sshAgent.Stop()
t.Fatalf("Error getting Rancher release version: %s", err)
}
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: exampleDir,
// Variables to pass to our Terraform code using -var options
Vars: map[string]interface{}{
"identifier": id,
"owner": owner,
"key_name": keyPair.Name,
"key": keyPair.KeyPair.PublicKey,
"zone": os.Getenv("ZONE"),
"rke2_version": rke2Version,
"rancher_version": rancherVersion,
"file_path": testDir,
},
// Environment variables to set when running Terraform
EnvVars: map[string]string{
"AWS_DEFAULT_REGION": region,
"AWS_REGION": region,
"TF_DATA_DIR": testDir,
"TF_IN_AUTOMATION": "1",
"TF_CLI_ARGS_plan": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_apply": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_destroy": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_output": "-no-color -state=" + testDir + "/tfstate",
},
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
NoColor: true,
SshAgent: sshAgent,
Upgrade: true,
})
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: exampleDir,
// Variables to pass to our Terraform code using -var options
Vars: map[string]interface{}{
"identifier": id,
"owner": owner,
"key_name": keyPair.Name,
"key": keyPair.KeyPair.PublicKey,
"zone": os.Getenv("ZONE"),
"rke2_version": rke2Version,
"rancher_version": rancherVersion,
"file_path": testDir,
},
// Environment variables to set when running Terraform
EnvVars: map[string]string{
"AWS_DEFAULT_REGION": region,
"AWS_REGION": region,
"TF_DATA_DIR": testDir,
"TF_IN_AUTOMATION": "1",
"TF_CLI_ARGS_plan": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_apply": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_destroy": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_output": "-no-color -state=" + testDir + "/tfstate",
},
RetryableTerraformErrors: getRetryableTerraformErrors(),
NoColor: true,
SshAgent: sshAgent,
Upgrade: true,
})
_, err = terraform.InitAndApplyE(t, terraformOptions)
if err != nil {
teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
t.Fatalf("Error creating cluster: %s", err)
}
t.Log("Test passed, tearing down...")
teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
}
func createKeypair(t *testing.T, region string, owner string, id string) (*aws.Ec2Keypair, error) {
t.Log("Creating keypair...")
// Create an EC2 KeyPair that we can use for SSH access
keyPairName := fmt.Sprintf("terraform-ci-%s", id)
keyPair := aws.CreateAndImportEC2KeyPair(t, region, keyPairName)
// tag the key pair so we can find in the access module
client, err := aws.NewEc2ClientE(t, region)
if err != nil {
return nil, err
}
k := "key-name"
keyNameFilter := ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input := &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err := client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
err = aws.AddTagsToResourceE(t, region, *result.KeyPairs[0].KeyPairId, map[string]string{"Name": keyPairName, "Owner": owner})
if err != nil {
return nil, err
}
// Verify that the name and owner tags were placed properly
k = "tag:Name"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
k = "tag:Owner"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&owner},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
return keyPair, nil
}
func getRetryableTerraformErrors() map[string]string {
retryableTerraformErrors := map[string]string{
// The reason is unknown, but eventually these succeed after a few retries.
".*unable to verify signature.*": "Failed due to transient network error.",
".*unable to verify checksum.*": "Failed due to transient network error.",
".*no provider exists with the given name.*": "Failed due to transient network error.",
".*registry service is unreachable.*": "Failed due to transient network error.",
".*connection reset by peer.*": "Failed due to transient network error.",
".*TLS handshake timeout.*": "Failed due to transient network error.",
".*Error: disassociating EC2 EIP.*does not exist.*": "Failed to delete EIP because interface is already gone",
".*context deadline exceeded.*": "Failed due to kubernetes timeout, retrying.",
}
return retryableTerraformErrors
}
func setAcmeServer() string {
acmeserver := os.Getenv("ACME_SERVER_URL")
if acmeserver == "" {
os.Setenv("ACME_SERVER_URL", "https://acme-staging-v02.api.letsencrypt.org/directory")
}
return acmeserver
}
func getRegion() string {
region := os.Getenv("AWS_REGION")
if region == "" {
region = os.Getenv("AWS_DEFAULT_REGION")
}
if region == "" {
region = "us-west-2"
}
return region
}
func getId() string {
id := os.Getenv("IDENTIFIER")
if id == "" {
id = random.UniqueId()
}
id += "-" + random.UniqueId()
return id
}
func createTestDirectories(t *testing.T, id string) error {
gwd := g.GetRepoRoot(t)
fwd, err := filepath.Abs(gwd)
if err != nil {
return err
}
tdd := fwd + "/test/tests/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id + "/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
return nil
}
func teardown(t *testing.T, directory string, options *terraform.Options, keyPair *aws.Ec2Keypair) {
directoryExists := true
_, err := os.Stat(directory)
if err != nil {
if os.IsNotExist(err) {
directoryExists = false
}
}
if directoryExists {
terraform.Destroy(t, options)
err := os.RemoveAll(directory)
if err != nil {
t.Logf("Failed to delete test data directory: %v", err)
}
}
aws.DeleteEC2KeyPair(t, keyPair)
}
func GetRancherReleases() (string, string, string, error) {
releases, err := getRancherReleases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
sortVersions(&versions)
latest := versions[0]
stable := latest
lts := stable
if len(versions) > 1 {
stable = versions[1]
}
if len(versions) > 2 {
lts = versions[2]
}
return latest, stable, lts, nil
}
func getRancherReleases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rancher", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func GetRke2Releases() (string, string, string, error) {
releases, err := getRke2Releases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
sortVersions(&versions)
v := filterDuplicateMinors(versions)
latest := v[0]
stable := latest
lts := stable
if len(v) > 1 {
stable = v[1]
}
if len(v) > 2 {
lts = v[2]
}
return latest, stable, lts, nil
}
func getRke2Releases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rke2", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func filterPrerelease(r []*github.RepositoryRelease) []string {
var versions []string
for _, release := range r {
version := release.GetTagName()
if !release.GetPrerelease() {
versions = append(versions, version)
// [
// "v1.28.14+rke2r1",
// "v1.30.1+rke2r3",
// "v1.29.4+rke2r1",
// "v1.30.1+rke2r2",
// "v1.29.5+rke2r2",
// "v1.30.1+rke2r1",
// "v1.27.20+rke2r1",
// "v1.30.0+rke2r1",
// "v1.29.5+rke2r1",
// "v1.28.17+rke2r1",
// ]
}
}
return versions
}
func sortVersions(v *[]string) {
slices.SortFunc(*v, func(a, b string) int {
return cmp.Compare(b, a)
//[
// v1.30.1+rke2r3,
// v1.30.1+rke2r2,
// v1.30.1+rke2r1,
// v1.30.0+rke2r1,
// v1.29.5+rke2r2,
// v1.29.5+rke2r1,
// v1.29.4+rke2r1,
// v1.28.17+rke2r1,
// v1.28.14+rke2r1,
// v1.27.20+rke2r1,
//]
})
}
func filterDuplicateMinors(vers []string) []string {
var fv []string
fv = append(fv, vers[0])
for i := 1; i < len(vers); i++ {
p := vers[i-1]
v := vers[i]
vp := strings.Split(v[1:], "+") //["1.30.1","rke2r3"]
pp := strings.Split(p[1:], "+") //["1.30.1","rke2r2"]
if vp[0] != pp[0] {
vpp := strings.Split(vp[0], ".") //["1","30","1]
ppp := strings.Split(pp[0], ".") //["1","30","1]
if vpp[1] != ppp[1] {
fv = append(fv, v)
//[
// v1.30.1+rke2r3,
// v1.29.5+rke2r2,
// v1.28.17+rke2r1,
// v1.27.20+rke2r1,
//]
}
}
}
return fv
_, err = terraform.InitAndApplyE(t, terraformOptions)
if err != nil {
util.Teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
t.Fatalf("Error creating cluster: %s", err)
}
t.Log("Test passed, tearing down...")
util.Teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
}

View File

@ -1,33 +1,24 @@
package prod
import (
"cmp"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/google/go-github/v53/github"
aws "github.com/gruntwork-io/terratest/modules/aws"
g "github.com/gruntwork-io/terratest/modules/git"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/ssh"
"github.com/gruntwork-io/terratest/modules/terraform"
"golang.org/x/oauth2"
util "github.com/rancher/terraform-rancher2-aws/test/tests"
)
func TestProdBasic(t *testing.T) {
t.Parallel()
id := getId()
region := getRegion()
id := util.GetId()
region := util.GetRegion()
directory := "prod"
owner := "terraform-ci@suse.com"
setAcmeServer()
util.SetAcmeServer()
repoRoot, err := filepath.Abs(g.GetRepoRoot(t))
if err != nil {
@ -37,12 +28,12 @@ func TestProdBasic(t *testing.T) {
exampleDir := repoRoot + "/examples/" + directory
testDir := repoRoot + "/test/tests/data/" + id
err = createTestDirectories(t, id)
err = util.CreateTestDirectories(t, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test data directories: %s", err)
}
keyPair, err := createKeypair(t, region, owner, id)
keyPair, err := util.CreateKeypair(t, region, owner, id)
if err != nil {
os.RemoveAll(testDir)
t.Fatalf("Error creating test key pair: %s", err)
@ -50,7 +41,8 @@ func TestProdBasic(t *testing.T) {
sshAgent := ssh.SshAgentWithKeyPair(t, keyPair.KeyPair)
t.Logf("Key %s created and added to agent", keyPair.Name)
_, _, rke2Version, err := GetRke2Releases()
// use oldest RKE2, remember it releases much more than Rancher
_, _, rke2Version, err := util.GetRke2Releases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
@ -58,7 +50,8 @@ func TestProdBasic(t *testing.T) {
t.Fatalf("Error getting Rke2 release version: %s", err)
}
rancherVersion, _, _, err := GetRancherReleases()
// use latest Rancher, due to community patch policy
rancherVersion, _, _, err := util.GetRancherReleases()
if err != nil {
os.RemoveAll(testDir)
aws.DeleteEC2KeyPair(t, keyPair)
@ -90,318 +83,20 @@ func TestProdBasic(t *testing.T) {
"TF_CLI_ARGS_destroy": "-no-color -state=" + testDir + "/tfstate",
"TF_CLI_ARGS_output": "-no-color -state=" + testDir + "/tfstate",
},
RetryableTerraformErrors: getRetryableTerraformErrors(),
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
NoColor: true,
SshAgent: sshAgent,
Upgrade: true,
})
_, err = terraform.InitAndApplyE(t, terraformOptions)
if err != nil {
teardown(t, testDir, terraformOptions, keyPair)
util.Teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
t.Fatalf("Error creating cluster: %s", err)
}
t.Log("Test passed, tearing down...")
teardown(t, testDir, terraformOptions, keyPair)
util.Teardown(t, testDir, terraformOptions, keyPair)
os.Remove(exampleDir + ".terraform.lock.hcl")
sshAgent.Stop()
}
func createKeypair(t *testing.T, region string, owner string, id string) (*aws.Ec2Keypair, error) {
t.Log("Creating keypair...")
// Create an EC2 KeyPair that we can use for SSH access
keyPairName := fmt.Sprintf("terraform-ci-%s", id)
keyPair := aws.CreateAndImportEC2KeyPair(t, region, keyPairName)
// tag the key pair so we can find in the access module
client, err := aws.NewEc2ClientE(t, region)
if err != nil {
return nil, err
}
k := "key-name"
keyNameFilter := ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input := &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err := client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
err = aws.AddTagsToResourceE(t, region, *result.KeyPairs[0].KeyPairId, map[string]string{"Name": keyPairName, "Owner": owner})
if err != nil {
return nil, err
}
// Verify that the name and owner tags were placed properly
k = "tag:Name"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
k = "tag:Owner"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&owner},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
return keyPair, nil
}
func getRetryableTerraformErrors() map[string]string {
retryableTerraformErrors := map[string]string{
// The reason is unknown, but eventually these succeed after a few retries.
".*unable to verify signature.*": "Failed due to transient network error.",
".*unable to verify checksum.*": "Failed due to transient network error.",
".*no provider exists with the given name.*": "Failed due to transient network error.",
".*registry service is unreachable.*": "Failed due to transient network error.",
".*connection reset by peer.*": "Failed due to transient network error.",
".*TLS handshake timeout.*": "Failed due to transient network error.",
".*Error: disassociating EC2 EIP.*does not exist.*": "Failed to delete EIP because interface is already gone",
".*context deadline exceeded.*": "Failed due to kubernetes timeout, retrying.",
}
return retryableTerraformErrors
}
func setAcmeServer() string {
acmeserver := os.Getenv("ACME_SERVER_URL")
if acmeserver == "" {
os.Setenv("ACME_SERVER_URL", "https://acme-staging-v02.api.letsencrypt.org/directory")
}
return acmeserver
}
func getRegion() string {
region := os.Getenv("AWS_REGION")
if region == "" {
region = os.Getenv("AWS_DEFAULT_REGION")
}
if region == "" {
region = "us-west-2"
}
return region
}
func getId() string {
id := os.Getenv("IDENTIFIER")
if id == "" {
id = random.UniqueId()
}
id += "-" + random.UniqueId()
return id
}
func createTestDirectories(t *testing.T, id string) error {
gwd := g.GetRepoRoot(t)
fwd, err := filepath.Abs(gwd)
if err != nil {
return err
}
tdd := fwd + "/test/tests/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id + "/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
return nil
}
func teardown(t *testing.T, directory string, options *terraform.Options, keyPair *aws.Ec2Keypair) {
directoryExists := true
_, err := os.Stat(directory)
if err != nil {
if os.IsNotExist(err) {
directoryExists = false
}
}
if directoryExists {
terraform.Destroy(t, options)
err := os.RemoveAll(directory)
if err != nil {
t.Logf("Failed to delete test data directory: %v", err)
}
}
aws.DeleteEC2KeyPair(t, keyPair)
}
func GetRancherReleases() (string, string, string, error) {
releases, err := getRancherReleases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
sortVersions(&versions)
latest := versions[0]
stable := latest
lts := stable
if len(versions) > 1 {
stable = versions[1]
}
if len(versions) > 2 {
lts = versions[2]
}
return latest, stable, lts, nil
}
func getRancherReleases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rancher", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func GetRke2Releases() (string, string, string, error) {
releases, err := getRke2Releases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
sortVersions(&versions)
v := filterDuplicateMinors(versions)
latest := v[0]
stable := latest
lts := stable
if len(v) > 1 {
stable = v[1]
}
if len(v) > 2 {
lts = v[2]
}
return latest, stable, lts, nil
}
func getRke2Releases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rke2", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func filterPrerelease(r []*github.RepositoryRelease) []string {
var versions []string
for _, release := range r {
version := release.GetTagName()
if !release.GetPrerelease() {
versions = append(versions, version)
// [
// "v1.28.14+rke2r1",
// "v1.30.1+rke2r3",
// "v1.29.4+rke2r1",
// "v1.30.1+rke2r2",
// "v1.29.5+rke2r2",
// "v1.30.1+rke2r1",
// "v1.27.20+rke2r1",
// "v1.30.0+rke2r1",
// "v1.29.5+rke2r1",
// "v1.28.17+rke2r1",
// ]
}
}
return versions
}
func sortVersions(v *[]string) {
slices.SortFunc(*v, func(a, b string) int {
return cmp.Compare(b, a)
//[
// v1.30.1+rke2r3,
// v1.30.1+rke2r2,
// v1.30.1+rke2r1,
// v1.30.0+rke2r1,
// v1.29.5+rke2r2,
// v1.29.5+rke2r1,
// v1.29.4+rke2r1,
// v1.28.17+rke2r1,
// v1.28.14+rke2r1,
// v1.27.20+rke2r1,
//]
})
}
func filterDuplicateMinors(vers []string) []string {
var fv []string
fv = append(fv, vers[0])
for i := 1; i < len(vers); i++ {
p := vers[i-1]
v := vers[i]
vp := strings.Split(v[1:], "+") //["1.30.1","rke2r3"]
pp := strings.Split(p[1:], "+") //["1.30.1","rke2r2"]
if vp[0] != pp[0] {
vpp := strings.Split(vp[0], ".") //["1","30","1]
ppp := strings.Split(pp[0], ".") //["1","30","1]
if vpp[1] != ppp[1] {
fv = append(fv, v)
//[
// v1.30.1+rke2r3,
// v1.29.5+rke2r2,
// v1.28.17+rke2r1,
// v1.27.20+rke2r1,
//]
}
}
}
return fv
}

395
test/tests/util.go Normal file
View File

@ -0,0 +1,395 @@
package tests
import (
"cmp"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
ec2 "github.com/aws/aws-sdk-go/service/ec2"
"github.com/google/go-github/v53/github"
aws "github.com/gruntwork-io/terratest/modules/aws"
g "github.com/gruntwork-io/terratest/modules/git"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
"golang.org/x/oauth2"
)
func GetRancherReleases() (string, string, string, error) {
releases, err := getRancherReleases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
zeroPadVersionNumbers(&versions)
sortVersions(&versions)
filterDuplicateMinors(&versions)
removeZeroPadding(&versions)
latest := versions[0]
stable := latest
lts := stable
if len(versions) > 1 {
stable = versions[1]
}
if len(versions) > 2 {
lts = versions[2]
}
return latest, stable, lts, nil
}
func getRancherReleases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rancher", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func GetRke2Releases() (string, string, string, error) {
releases, err := getRke2Releases()
if err != nil {
return "", "", "", err
}
versions := filterPrerelease(releases)
if len(versions) == 0 {
return "", "", "", errors.New("no eligible versions found")
}
zeroPadVersionNumbers(&versions)
sortVersions(&versions)
filterDuplicateMinors(&versions)
removeZeroPadding(&versions)
latest := versions[0]
stable := latest
lts := stable
if len(versions) > 1 {
stable = versions[1]
}
if len(versions) > 2 {
lts = versions[2]
}
return latest, stable, lts, nil
}
func getRke2Releases() ([]*github.RepositoryRelease, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
fmt.Println("GITHUB_TOKEN environment variable not set")
return nil, errors.New("GITHUB_TOKEN environment variable not set")
}
// Create a new OAuth2 token using the GitHub token
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
// Create a new GitHub client using the authenticated HTTP client
client := github.NewClient(tokenClient)
var releases []*github.RepositoryRelease
releases, _, err := client.Repositories.ListReleases(context.Background(), "rancher", "rke2", &github.ListOptions{})
if err != nil {
return nil, err
}
return releases, nil
}
func filterReleaseCandidate(v *[]string) {
var fv []string
versions := *v
for i := 1; i < len(versions); i++ {
if strings.Contains(versions[i], "-") != true {
fv = append(fv, versions[i])
}
}
*v = fv
}
func filterPrerelease(r []*github.RepositoryRelease) []string {
var versions []string
for _, release := range r {
version := release.GetTagName()
if !release.GetPrerelease() {
versions = append(versions, version)
// [
// "v1.28.14+rke2r1",
// "v1.30.1+rke2r3",
// "v1.29.4+rke2r1",
// "v1.30.1+rke2r2",
// "v1.29.5+rke2r2",
// "v1.30.1+rke2r1",
// "v1.27.20+rke2r1",
// "v1.30.0+rke2r1",
// "v1.29.5+rke2r1",
// "v1.28.17+rke2r1",
// ]
}
}
return versions
}
func sortVersions(v *[]string) {
slices.SortFunc(*v, func(a, b string) int {
return cmp.Compare(b, a)
//[
// v1.4.1+rke2r3,
// v1.30.1+rke2r3,
// v1.30.1+rke2r2,
// v1.30.1+rke2r1,
// v1.30.0+rke2r1,
// v1.29.5+rke2r2,
// v1.29.5+rke2r1,
// v1.29.4+rke2r1,
// v1.28.17+rke2r1,
// v1.28.14+rke2r1,
// v1.27.20+rke2r1,
//]
})
}
func filterDuplicateMinors(v *[]string) { // assumes versions are sorted already
var fv []string
versions := *v
fv = append(fv, versions[0])
for i := 1; i < len(versions); i++ {
c := versions[i]
p := versions[i-1]
cp := strings.Split(c[1:], "+") //["1.30.1","rke2r3"]
pp := strings.Split(p[1:], "+") //["1.30.1","rke2r2"]
if cp[0] != pp[0] {
cpp := strings.Split(cp[0], ".") //["1","30","1]
ppp := strings.Split(pp[0], ".") //["1","30","1]
if cpp[1] != ppp[1] {
fv = append(fv, c)
//[
// v1.30.1+rke2r3,
// v1.29.5+rke2r2,
// v1.28.17+rke2r1,
// v1.27.20+rke2r1,
//]
}
}
}
*v = fv
}
func zeroPadVersionNumbers(v *[]string) {
var zv []string
versions := *v
for i := 0; i < len(versions); i++ {
vp := strings.Split(versions[i], "+") //["v1.3.1","rke2r3"] OR ["v2.5.4"] if no "+"
vpp := strings.Split(vp[0], ".") //["v1","3","1]
major := vpp[0] // assumes single digit major
minor := ""
trivial := ""
if len(vpp[1]) < 2 {
minor = fmt.Sprintf("0%s", vpp[1]) // assumes double digit versions
} else {
minor = vpp[1]
}
if len(vpp[2]) < 2 {
trivial = fmt.Sprintf("0%s", vpp[2]) // assumes double digit versions
} else {
trivial = vpp[2]
}
if len(vp) > 1 {
version := fmt.Sprintf("%s.%s.%s+%s", major, minor, trivial, vp[1]) //"v1.03.01+rke2r3"
zv = append(zv, version)
} else {
version := fmt.Sprintf("%s.%s.%s", major, minor, trivial) //"v1.03.01"
zv = append(zv, version)
}
}
*v = zv
}
func removeZeroPadding (v *[]string) {
var zv []string
versions := *v
for i := 0; i < len(versions); i++ {
vp := strings.Split(versions[i], "+") //["v1.03.01","rke2r3"] OR ["v2.05.04"] if no "+"
vpp := strings.Split(vp[0], ".") //["v1","03","01]
major := vpp[0] // assumes single digit major
minor := vpp[1]
trivial := vpp[2]
if minor[0] == '0' {
minor = minor[1:]
}
if trivial[0] == '0' {
trivial = trivial[1:]
}
if len(vp) > 1 {
version := fmt.Sprintf("%s.%s.%s+%s", major, minor, trivial, vp[1]) //"v1.3.1+rke2r3"
zv = append(zv, version)
} else {
version := fmt.Sprintf("%s.%s.%s", major, minor, trivial) //"v1.3.1"
zv = append(zv, version)
}
}
*v = zv
}
func CreateKeypair(t *testing.T, region string, owner string, id string) (*aws.Ec2Keypair, error) {
t.Log("Creating keypair...")
// Create an EC2 KeyPair that we can use for SSH access
keyPairName := fmt.Sprintf("terraform-ci-%s", id)
keyPair := aws.CreateAndImportEC2KeyPair(t, region, keyPairName)
// tag the key pair so we can find in the access module
client, err := aws.NewEc2ClientE(t, region)
if err != nil {
return nil, err
}
k := "key-name"
keyNameFilter := ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input := &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err := client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
err = aws.AddTagsToResourceE(t, region, *result.KeyPairs[0].KeyPairId, map[string]string{"Name": keyPairName, "Owner": owner})
if err != nil {
return nil, err
}
// Verify that the name and owner tags were placed properly
k = "tag:Name"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&keyPairName},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
k = "tag:Owner"
keyNameFilter = ec2.Filter{
Name: &k,
Values: []*string{&owner},
}
input = &ec2.DescribeKeyPairsInput{
Filters: []*ec2.Filter{&keyNameFilter},
}
result, err = client.DescribeKeyPairs(input)
if err != nil {
return nil, err
}
return keyPair, nil
}
func GetRetryableTerraformErrors() map[string]string {
retryableTerraformErrors := map[string]string{
// The reason is unknown, but eventually these succeed after a few retries.
".*unable to verify signature.*": "Failed due to transient network error.",
".*unable to verify checksum.*": "Failed due to transient network error.",
".*no provider exists with the given name.*": "Failed due to transient network error.",
".*registry service is unreachable.*": "Failed due to transient network error.",
".*connection reset by peer.*": "Failed due to transient network error.",
".*TLS handshake timeout.*": "Failed due to transient network error.",
".*Error: disassociating EC2 EIP.*does not exist.*": "Failed to delete EIP because interface is already gone",
".*context deadline exceeded.*": "Failed due to kubernetes timeout, retrying.",
}
return retryableTerraformErrors
}
func SetAcmeServer() string {
acmeserver := os.Getenv("ACME_SERVER_URL")
if acmeserver == "" {
os.Setenv("ACME_SERVER_URL", "https://acme-staging-v02.api.letsencrypt.org/directory")
}
return acmeserver
}
func GetRegion() string {
region := os.Getenv("AWS_REGION")
if region == "" {
region = os.Getenv("AWS_DEFAULT_REGION")
}
if region == "" {
region = "us-west-2"
}
return region
}
func GetId() string {
id := os.Getenv("IDENTIFIER")
if id == "" {
id = random.UniqueId()
}
id += "-" + random.UniqueId()
return id
}
func CreateTestDirectories(t *testing.T, id string) error {
gwd := g.GetRepoRoot(t)
fwd, err := filepath.Abs(gwd)
if err != nil {
return err
}
tdd := fwd + "/test/tests/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
tdd = fwd + "/test/tests/data/" + id + "/data"
err = os.Mkdir(tdd, 0755)
if err != nil && !os.IsExist(err) {
return err
}
return nil
}
func Teardown(t *testing.T, directory string, options *terraform.Options, keyPair *aws.Ec2Keypair) {
directoryExists := true
_, err := os.Stat(directory)
if err != nil {
if os.IsNotExist(err) {
directoryExists = false
}
}
if directoryExists {
terraform.Destroy(t, options)
err := os.RemoveAll(directory)
if err != nil {
t.Logf("Failed to delete test data directory: %v", err)
}
}
aws.DeleteEC2KeyPair(t, keyPair)
}