diff --git a/cmd/root.go b/cmd/root.go index 19d1ce8..25d7a38 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,11 +14,17 @@ var ( tfstate bool file []byte path string + debug bool rootCmd = &cobra.Command{ Use: "inframap", Short: "Reads the TFState or HCL to generate a Graphical view", Long: "Reads the TFState or HCL to generate a Graphical view with Nodes and Edges.", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if debug { + os.Setenv("TF_LOG", "DEBUG") + } + }, } ) @@ -101,4 +107,5 @@ func init() { rootCmd.PersistentFlags().BoolVar(&hcl, "hcl", false, "Forces to use HCL parser") rootCmd.PersistentFlags().BoolVar(&tfstate, "tfstate", false, "Forces to use TFState parser") + rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Activate the debug mode wich includes TF logs via TF_LOG=TRACE|DEBUG|INFO|WARN|ERROR configuration https://www.terraform.io/docs/internals/debugging.html") } diff --git a/generate/hcl.go b/generate/hcl.go index bc7f6c8..566ed7d 100644 --- a/generate/hcl.go +++ b/generate/hcl.go @@ -3,23 +3,39 @@ package generate import ( "errors" "fmt" + "os" + "path" + "path/filepath" "strings" + "github.com/adrg/xdg" "github.com/cycloidio/inframap/errcode" "github.com/cycloidio/inframap/graph" "github.com/cycloidio/inframap/provider" + "github.com/hashicorp/go-getter" + "github.com/hashicorp/go-version" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/registry" + "github.com/hashicorp/terraform/registry/regsrc" uuid "github.com/satori/go.uuid" "github.com/spf13/afero" ) +var ( + localSourcePrefixes = []string{ + "./", + "../", + } + cachePath = path.Join(xdg.CacheHome, "inframap", "modules") +) + // FromHCL generates a new graph from the HCL on the path, // it can be a file or a Module/Dir -func FromHCL(fs afero.Fs, path string, opt Options) (*graph.Graph, error) { +func FromHCL(fs afero.Fs, p string, opt Options) (*graph.Graph, error) { parser := configs.NewParser(fs) g := graph.New() @@ -30,10 +46,10 @@ func FromHCL(fs afero.Fs, path string, opt Options) (*graph.Graph, error) { err error ) - if parser.IsConfigDir(path) { - mod, diags = parser.LoadConfigDir(path) + if parser.IsConfigDir(p) { + mod, diags = parser.LoadConfigDir(p) } else { - f, dgs := parser.LoadConfigFile(path) + f, dgs := parser.LoadConfigFile(p) if dgs.HasErrors() { return nil, errors.New(dgs.Error()) } @@ -44,6 +60,21 @@ func FromHCL(fs afero.Fs, path string, opt Options) (*graph.Graph, error) { return nil, errors.New(diags.Error()) } + managedResources := make(map[string]*configs.Resource) + for rk, rv := range mod.ManagedResources { + managedResources[rk] = rv + } + + installedModules := make(map[string]struct{}) + calls := make([]*configs.ModuleCall, 0) + for _, call := range mod.ModuleCalls { + calls = append(calls, call) + } + p, _ = filepath.Abs(p) + if err := moduleInstall(calls, &managedResources, p, installedModules); err != nil { + return nil, fmt.Errorf("unable to fetch all modules: %w", err) + } + // nodeCanID holds as key the `aws_alb.front` (graph.Node.Canonical) // and as value the UUID (graph.Node.ID) we give to it nodeCanID := make(map[string]string) @@ -64,7 +95,7 @@ func FromHCL(fs afero.Fs, path string, opt Options) (*graph.Graph, error) { } } - for rk, rv := range mod.ManagedResources { + for rk, rv := range managedResources { pv, rs, err := getProviderAndResource(rk, opt) if err != nil { if errors.Is(err, errcode.ErrProviderNotFound) { @@ -277,3 +308,139 @@ func checkHCLProviders(mod *configs.Module, opt Options) (Options, error) { return opt, nil } + +// moduleInstall will recursively walk through the module calls required by the Terraform config, it will store the downloaded module +// in $XDG_CACHE directory and stop once all the required modules have been downloaded. +func moduleInstall(calls []*configs.ModuleCall, mRes *map[string]*configs.Resource, pwd string, installedModules map[string]struct{}) error { + // stop condition, if there is no module to + // fetch we stop + if len(calls) == 0 { + return nil + } + + call := calls[0] + name := call.Name + + // we check if the module is already installed + // or not + if _, ok := installedModules[name]; ok { + return nil + } + + var ( + src string = call.SourceAddr + vers string + ) + + // we check if the module is a Terraform registry module + // in order to get its source address from Terraform registry + if regMod, err := regsrc.ParseModuleSource(src); err == nil { + client := registry.NewClient(nil, nil) + // we get the list of available module versions + resp, err := client.ModuleVersions(regMod) + if err != nil { + return fmt.Errorf("unable to get module versions: %w", err) + } + + if len(resp.Modules) < 1 { + return fmt.Errorf("unable to find suitable versions") + } + meta := resp.Modules[0] + + var ( + latest *version.Version + // match holds the version matching the + // source constraints set in the module call + match *version.Version + ) + for _, vers := range meta.Versions { + v, err := version.NewVersion(vers.Version) + if err != nil { + return fmt.Errorf("unable to create version from string: %w", err) + } + + if latest == nil || v.GreaterThan(latest) { + latest = v + } + + if call.Version.Required.Check(v) { + if match == nil || v.GreaterThan(match) { + match = v + } + } + } + + vers = match.String() + // we finally get the module location, it will return + // a string `go-getter` compliant + src, err = client.ModuleLocation(regMod, vers) + if err != nil { + return fmt.Errorf("unable to fetch module location: %w", err) + } + } + + // since go-getter does not support yet in-memory fs, + // we need to initialize the parser using actual fs + // https://github.com/hashicorp/go-getter/issues/83 + pars := configs.NewParser(nil) + + // we check if the module is a local one by checking + // its prefix "./", "../", etc. + var isLocal bool + for _, prefix := range localSourcePrefixes { + if strings.HasPrefix(src, prefix) { + isLocal = true + } + } + + var ( + m *configs.Module + diags hcl.Diagnostics + ) + + // the module is not a local one or a Terraform registry one + // it should be handle by `go-getter` + if !isLocal { + dst := path.Join(cachePath, fmt.Sprintf("%s-%s", name, vers)) + // TODO: we should add a logic to invalidate + // the cache + if _, err := os.Stat(dst); os.IsNotExist(err) { + client := &getter.Client{ + Src: src, + Dst: dst, + Pwd: pwd, + Mode: getter.ClientModeDir, + } + if err := client.Get(); err != nil { + return fmt.Errorf("unable to get remote module: %w", err) + } + } + m, diags = pars.LoadConfigDir(dst) + } else { + m, diags = pars.LoadConfigDir(path.Join(pwd, src)) + } + if diags.HasErrors() { + return fmt.Errorf("unable to load config directory: %s", diags.Error()) + } + + // fill the final map of managed resources + // using the config freshly loaded + for rk, rv := range m.ManagedResources { + (*mRes)[rk] = rv + } + + // keep a trace of the imported / loaded module + // to avoid infinite recursion + installedModules[name] = struct{}{} + + // create the next slice of module calls to + // check before merging it with the current we + // still have + next := make([]*configs.ModuleCall, 0) + for _, call := range m.ModuleCalls { + next = append(next, call) + } + calls = append(calls[1:len(calls)], next...) + + return moduleInstall(calls, mRes, pwd, installedModules) +} diff --git a/generate/testdata/stack-gke/gke.tf b/generate/testdata/stack-gke/gke.tf new file mode 100644 index 0000000..e85b5a0 --- /dev/null +++ b/generate/testdata/stack-gke/gke.tf @@ -0,0 +1,195 @@ +# Put here a custom name for the GKE Cluster +# Otherwise `${var.project}-${var.env}` will be used +locals { + cluster_name = "" +} + +# https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters +# You cannot use a cluster master, node, Pod, or Service IP range that overlaps with 172.17.0.0/16. +# The size of the RFC 1918 block for the cluster master must be /28. + +module "vpc" { + ##################################### + # Do not modify the following lines # + source = "./module-vpc" + + project = var.project + env = var.env + customer = var.customer + + ##################################### + + ### + # General + ### + + #. gcp_project (required): + #+ The Google Cloud Platform project to use. + gcp_project = var.gcp_project + + #. gcp_region (optional): eu-central1 + #+ The Google Cloud Platform region to use. + gcp_region = var.gcp_region + + #. extra_labels (optional): {} + #+ Dict of extra labels to add on aws resources. format { "foo" = "bar" }. + + ### + # Networking + ### + + #. subnet_cidr (optional): 10.8.0.0/16 + #+ The CIDR of the VPC subnet. + subnet_cidr = "10.8.0.0/16" + + #. pods_cidr (optional): 10.9.0.0/16 + #+ The CIDR of the pods secondary range. + pods_cidr = "10.9.0.0/16" + + #. services_cidr (optional): 10.10.0.0/16 + #+ The CIDR of the services secondary range. + services_cidr = "10.10.0.0/16" + + #. network_routing_mode (optional): GLOBAL + #+ The network routing mode. + + ### + # Required (should probably not be touched) + ### + + cluster_name = local.gke_cluster_name +} + +module "gke" { + ##################################### + # Do not modify the following lines # + source = "./module-gke" + + project = var.project + env = var.env + customer = var.customer + + ##################################### + + ### + # General + ### + + #. gcp_project (required): + #+ The Google Cloud Platform project to use. + gcp_project = var.gcp_project + + #. gcp_region (optional): eu-central1 + #+ The Google Cloud Platform region to use. + gcp_region = var.gcp_region + + #. gcp_zones (optional): [] + #+ To use specific Google Cloud Platform zones if not regional, otherwise it will be chosen randomly. + + #. extra_labels (optional): {} + #+ Dict of extra labels to add on GCP resources. format { "foo" = "bar" }. + + ### + # Control plane + ### + + #. cluster_version (optional): latest + #+ GKE Cluster version to use. + + #. cluster_release_channel (optional): UNSPECIFIED + #+ GKE Cluster release channel to use. Accepted values are UNSPECIFIED, RAPID, REGULAR and STABLE. + + #. cluster_regional (optional): false + #+ If the GKE Cluster must be regional or zonal. Be careful, this setting is destructive. + + #. enable_only_private_endpoint (optional): false + #+ If true, only enable the private endpoint which disable the Public endpoint entirely. If false, private endpoint will be enabled, and the public endpoint will be only accessible by master authorized networks. + + #. master_authorized_networks (optional): [] + #+ List of master authorized networks. + # master_authorized_networks = [ + # { + # name: "my-ip", + # cidr: "x.x.x.x/32" + # } + # ] + + #. enable_network_policy (optional): true + #+ Enable GKE Cluster network policies addon. + + #. enable_horizontal_pod_autoscaling (optional): true + #+ Enable GKE Cluster horizontal pod autoscaling addon. + + #. enable_vertical_pod_autoscaling (optional): false + #+ Enable GKE Cluster vertical pod autoscaling addon. Vertical Pod Autoscaling automatically adjusts the resources of pods controlled by it. + + #. enable_http_load_balancing (optional): false + #+ Enable GKE Cluster HTTP load balancing addon. + + #. enable_binary_authorization (optional): false + #+ Enable GKE Cluster BinAuthZ Admission controller. + + #. enable_cloudrun (optional): false + #+ Enable GKE Cluster Cloud Run for Anthos addon. + + #. enable_istio (optional): false + #+ Enable GKE Cluster Istio addon. + + ### + # Node pools + ### + + #. node_pools (optional): [] + #+ GKE Cluster node pools to create. + node_pools = [ + { + name = "my-node-pool" + machine_type = "n1-standard-1" + image_type = "COS" + + auto_repair = true + auto_upgrade = false + preemptible = false + + autoscaling = true + initial_node_count = 1 + min_count = 1 + max_count = 1 + + # autoscaling = false + # node_count = 1 + + local_ssd_count = 0 + disk_size_gb = 100 + disk_type = "pd-ssd" + + # service_account = "" + # accelerator_count = 0 + # accelerator_type = "" + + # oauth_scopes = [] + # metadata = {} + # labels = {} + # taints = [] + # tags = [] + }, + ] + + #. enable_shielded_nodes (optional): true + #+ Enable GKE Cluster Shielded Nodes features on all nodes. + + #. enable_sandbox (optional): false + #+ Enable GKE Sandbox (Do not forget to set image_type = COS_CONTAINERD and node_version = 1.12.7-gke.17 or later to use it). + + #. default_max_pods_per_node (optional): 110 + #+ The maximum number of pods to schedule per node. + + ### + # Required (should probably not be touched) + ### + + cluster_name = local.gke_cluster_name + subnet_name = module.vpc.subnet_name + pods_ip_range = module.vpc.pods_ip_range + services_ip_range = module.vpc.services_ip_range +} diff --git a/generate/testdata/stack-gke/module-gke/control_plane.tf b/generate/testdata/stack-gke/module-gke/control_plane.tf new file mode 100644 index 0000000..968c952 --- /dev/null +++ b/generate/testdata/stack-gke/module-gke/control_plane.tf @@ -0,0 +1,150 @@ + +data "google_compute_subnetwork" "subnetwork" { + name = var.subnet_name + project = var.gcp_project + region = var.gcp_region +} + +module "gcp-gke" { + source = "terraform-google-modules/kubernetes-engine/google//modules/beta-private-cluster-update-variant" + version = "~> 6.1" + + project_id = var.gcp_project + region = var.gcp_region + + name = var.cluster_name + description = "${var.cluster_name} GKE Cluster deployed via the cycloid.io GKE stack. Customer: ${var.customer}, Project: ${var.project}, Env: ${var.env}." + regional = var.cluster_regional + zones = local.gcp_available_zones + kubernetes_version = var.cluster_version + release_channel = var.cluster_release_channel + + // This craziness gets a plain network name from the reference link which is the + // only way to force cluster creation to wait on network creation without a + // depends_on link. Tests use terraform 0.12.6, which does not have regex or regexall + network = reverse(split("/", data.google_compute_subnetwork.subnetwork.network))[0] + + subnetwork = data.google_compute_subnetwork.subnetwork.name + ip_range_pods = var.pods_ip_range + ip_range_services = var.services_ip_range + + # security + create_service_account = true + enable_private_endpoint = var.enable_only_private_endpoint + grant_registry_access = var.grant_registry_access + disable_legacy_metadata_endpoints = var.disable_legacy_metadata_endpoints + enable_intranode_visibility = var.enable_intranode_visibility + enable_shielded_nodes = var.enable_shielded_nodes + node_metadata = "SECURE" + sandbox_enabled = var.enable_sandbox + + # { state = "ENCRYPTED", key_name = "" } + # database_encryption + + # addons + network_policy = var.enable_network_policy + network_policy_provider = var.network_policy_provider + horizontal_pod_autoscaling = var.enable_horizontal_pod_autoscaling + enable_vertical_pod_autoscaling = var.enable_vertical_pod_autoscaling + http_load_balancing = var.enable_http_load_balancing + enable_binary_authorization = var.enable_binary_authorization + cloudrun = var.enable_cloudrun + istio = var.enable_istio + + # settings + default_max_pods_per_node = var.default_max_pods_per_node + maintenance_start_time = var.maintenance_start_time + logging_service = "logging.googleapis.com/kubernetes" + monitoring_service = "monitoring.googleapis.com/kubernetes" + + master_ipv4_cidr_block = var.master_cidr + master_authorized_networks = concat( + [ + { + cidr_block = data.google_compute_subnetwork.subnetwork.ip_cidr_range + display_name = "VPC" + }, + ], + [ + for allowed_ip in var.master_authorized_networks: { + cidr_block = allowed_ip["cidr"] + display_name = allowed_ip["name"] + } + ] + ) + + enable_private_nodes = true + remove_default_node_pool = true + node_pools = var.node_pools + + node_pools_oauth_scopes = merge( + { + all = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + "https://www.googleapis.com/auth/service.management.readonly", + "https://www.googleapis.com/auth/servicecontrol", + "https://www.googleapis.com/auth/trace.append", + ] + }, + zipmap( + [for node_pool in var.node_pools : node_pool["name"]], + [for node_pool in var.node_pools : lookup(node_pool, "oauth_scopes", [])] + ), + ) + + node_pools_labels = merge( + { + all = {} + }, + zipmap( + [for node_pool in var.node_pools : node_pool["name"]], + [for node_pool in var.node_pools : lookup(node_pool, "labels", {})] + ), + ) + + node_pools_metadata = merge( + { + all = { + shutdown-script = file("${path.module}/data/shutdown-script.sh") + } + }, + zipmap( + [for node_pool in var.node_pools : node_pool["name"]], + [for node_pool in var.node_pools : lookup(node_pool, "metadata", {})] + ), + ) + + node_pools_taints = merge( + { + all = [] + }, + zipmap( + [for node_pool in var.node_pools : node_pool["name"]], + [for node_pool in var.node_pools : lookup(node_pool, "taints", [])] + ), + ) + + node_pools_tags = merge( + { + all = [] + }, + zipmap( + [for node_pool in var.node_pools : node_pool["name"]], + [for node_pool in var.node_pools : lookup(node_pool, "tags", [])] + ), + ) + + cluster_resource_labels = merge(local.merged_labels, { + name = "${var.project}-${var.env}-gke-cluster" + }) + + # gcloud and jq commands not available in the concourse terraform-resource. + # By doing that, `stub_domains` and `upstream_nameservers` variables can't be use. + skip_provisioners = true +} + +data "google_client_config" "default" { +} diff --git a/generate/testdata/stack-gke/module-gke/data/shutdown-script.sh b/generate/testdata/stack-gke/module-gke/data/shutdown-script.sh new file mode 100644 index 0000000..20566a4 --- /dev/null +++ b/generate/testdata/stack-gke/module-gke/data/shutdown-script.sh @@ -0,0 +1,17 @@ + #!/bin/bash -e + +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kubectl --kubeconfig=/var/lib/kubelet/kubeconfig drain --force=true --ignore-daemonsets=true --delete-local-data "$HOSTNAME" \ No newline at end of file diff --git a/generate/testdata/stack-gke/module-gke/outputs.tf b/generate/testdata/stack-gke/module-gke/outputs.tf new file mode 100644 index 0000000..6cf72d2 --- /dev/null +++ b/generate/testdata/stack-gke/module-gke/outputs.tf @@ -0,0 +1,100 @@ +output "cluster_name" { + description = "GKE Cluster name." + value = module.gcp-gke.name +} + +output "cluster_type" { + description = "GKE Cluster type." + value = module.gcp-gke.type +} + +output "cluster_location" { + description = "GKE Cluster location (region if regional cluster, zone if zonal cluster)." + value = module.gcp-gke.location +} + +output "cluster_region" { + description = "GKE Cluster region." + value = module.gcp-gke.region +} + +output "cluster_zones" { + description = "GKE Cluster zones." + value = module.gcp-gke.zones +} + +output "cluster_master_version" { + description = "GKE Cluster master version." + value = module.gcp-gke.master_version +} + +output "cluster_release_channel" { + description = "GKE Cluster release channel." + value = module.gcp-gke.release_channel +} + +output "cluster_endpoint" { + description = "GKE Cluster endpoint." + value = "https://${module.gcp-gke.endpoint}" +} + +output "cluster_ca" { + description = "GKE Cluster certificate authority." + value = module.gcp-gke.ca_certificate +} + +output "cluster_master_authorized_networks_config" { + description = "GKE Cluster networks from which access to master is permitted." + value = module.gcp-gke.master_authorized_networks_config +} + +output "node_pools_names" { + description = "GKE Cluster node pools names." + value = module.gcp-gke.node_pools_names +} + +output "node_pools_versions" { + description = "GKE Cluster node pools versions." + value = module.gcp-gke.node_pools_versions +} + +output "node_pools_service_account" { + description = "GKE Cluster nodes default service account if not overriden in `node_pools`." + value = module.gcp-gke.service_account +} + +locals { + kubeconfig = < 0 ? var.gcp_zones : data.google_compute_zones.available.names +} + +variable "project" { + description = "Cycloid project name." +} + +variable "env" { + description = "Cycloid environment name." +} + +variable "customer" { + description = "Cycloid customer name." +} + +variable "extra_labels" { + description = "Extra labels to add to all resources." + default = {} +} + +locals { + standard_labels = { + cycloidio = "true" + env = var.env + project = var.project + client = var.customer + } + merged_labels = merge(local.standard_labels, var.extra_labels) +} + +# +# Networking +# + +variable "subnet_name" { + description = "GKE Cluster subnet name to use." +} + +variable "pods_ip_range" { + description = "GKE Cluster pods IP range to use." +} + +variable "services_ip_range" { + description = "GKE Cluster services IP range to use." +} + +variable "master_cidr" { + description = "GKE Cluster masters IP CIDR to use." + default = "172.16.0.0/28" +} + +# +# Control plane +# + +variable "cluster_name" { + description = "GKE Cluster given name." +} + +variable "cluster_version" { + description = "GKE Cluster version to use." + default = "latest" +} + +variable "cluster_release_channel" { + description = "GKE Cluster release channel to use. Accepted values are UNSPECIFIED, RAPID, REGULAR and STABLE." + default = "UNSPECIFIED" +} + +variable "cluster_regional" { + description = "If the GKE Cluster must be regional or zonal. Be careful, this setting is destructive." + default = false +} + +variable "enable_only_private_endpoint" { + description = "If true, only enable the private endpoint which disable the Public endpoint entirely. If false, private endpoint will be enabled, and the public endpoint will be only accessible by master authorized networks." + default = false +} + +variable "grant_registry_access" { + description = "Grants created cluster-specific service account storage.objectViewer role." + default = true +} + +variable "master_authorized_networks" { + description = "List of master authorized networks." + default = [] +} + +variable "enable_network_policy" { + description = "Enable GKE Cluster network policies addon." + default = true +} + +variable "network_policy_provider" { + description = "The GKE Cluster network policies addon provider." + default = "CALICO" +} + +variable "enable_horizontal_pod_autoscaling" { + description = "Enable GKE Cluster horizontal pod autoscaling addon." + default = true +} + +variable "enable_vertical_pod_autoscaling" { + description = "Enable GKE Cluster vertical pod autoscaling addon. Vertical Pod Autoscaling automatically adjusts the resources of pods controlled by it." + default = false +} + +variable "enable_http_load_balancing" { + description = "Enable GKE Cluster HTTP load balancing addon." + default = false +} + +variable "disable_legacy_metadata_endpoints" { + description = "Disable GKE Cluster legacy metadata endpoints." + default = true +} + +variable "enable_binary_authorization" { + description = "Enable GKE Cluster BinAuthZ Admission controller." + default = false +} + +variable "enable_cloudrun" { + description = "Enable GKE Cluster Cloud Run for Anthos addon." + default = false +} + +variable "enable_istio" { + description = "Enable GKE Cluster Istio addon." + default = false +} + +variable "maintenance_start_time" { + description = "Time window specified for daily maintenance operations in RFC3339 format." + default = "05:00" +} + +# +# Node pools +# + +variable "node_pools" { + description = "GKE Cluster node pools to create." + default = [] +} + +variable "enable_shielded_nodes" { + description = "Enable GKE Cluster Shielded Nodes features on all nodes." + default = true +} + +variable "enable_sandbox" { + description = "Enable GKE Sandbox (Do not forget to set image_type = COS_CONTAINERD and node_version = 1.12.7-gke.17 or later to use it)." + default = false +} + +variable "default_max_pods_per_node" { + description = "The maximum number of pods to schedule per node." + default = "110" +} + +variable "enable_intranode_visibility" { + description = "Whether Intra-node visibility is enabled for this cluster. This makes same node pod to pod traffic visible for VPC network." + default = false +} diff --git a/generate/testdata/stack-gke/module-vpc/outputs.tf b/generate/testdata/stack-gke/module-vpc/outputs.tf new file mode 100644 index 0000000..070b6e4 --- /dev/null +++ b/generate/testdata/stack-gke/module-vpc/outputs.tf @@ -0,0 +1,34 @@ +output "network_name" { + description = "GKE Cluster dedicated network name." + value = module.gcp-network.network_name +} + +output "network_self_link" { + description = "GKE Cluster dedicated network URI." + value = module.gcp-network.network_self_link +} + +output "subnet_name" { + description = "GKE Cluster dedicated subnet name." + value = module.gcp-network.subnets_names[0] +} + +output "subnet_self_link" { + description = "GKE Cluster dedicated subnet URI." + value = module.gcp-network.subnets_self_links[0] +} + +output "subnet_region" { + description = "GKE Cluster dedicated subnet region." + value = module.gcp-network.subnets_regions[0] +} + +output "pods_ip_range" { + description = "GKE Cluster dedicated pods IP range." + value = module.gcp-network.subnets_secondary_ranges[0].*.range_name[0] +} + +output "services_ip_range" { + description = "GKE Cluster dedicated services IP range." + value = module.gcp-network.subnets_secondary_ranges[0].*.range_name[1] +} diff --git a/generate/testdata/stack-gke/module-vpc/variables.tf b/generate/testdata/stack-gke/module-vpc/variables.tf new file mode 100644 index 0000000..426e5a1 --- /dev/null +++ b/generate/testdata/stack-gke/module-vpc/variables.tf @@ -0,0 +1,71 @@ +# +# General +# + +variable "gcp_project" { + description = "The Google Cloud Platform project to use." +} + +variable "gcp_region" { + description = "The Google Cloud Platform region to use." + default = "eu-central1" +} + +variable "project" { + description = "Cycloid project name." +} + +variable "env" { + description = "Cycloid environment name." +} + +variable "customer" { + description = "Cycloid customer name." +} + +variable "extra_labels" { + description = "Extra labels to add to all resources." + default = {} +} + +locals { + standard_labels = { + cycloidio = "true" + env = var.env + project = var.project + client = var.customer + } + merged_labels = merge(local.standard_labels, var.extra_labels) +} + +# +# Networking +# + +variable "network_routing_mode" { + description = "The network routing mode." + default = "GLOBAL" +} + +variable "subnet_cidr" { + description = "The CIDR of the VPC subnet." + default = "10.8.0.0/16" +} + +variable "pods_cidr" { + description = "The CIDR of the pods secondary range." + default = "10.9.0.0/16" +} + +variable "services_cidr" { + description = "The CIDR of the services secondary range." + default = "10.10.0.0/16" +} + +# +# Control plane +# + +variable "cluster_name" { + description = "EKS Cluster given name." +} \ No newline at end of file diff --git a/generate/testdata/stack-gke/module-vpc/vpc.tf b/generate/testdata/stack-gke/module-vpc/vpc.tf new file mode 100644 index 0000000..fdf431e --- /dev/null +++ b/generate/testdata/stack-gke/module-vpc/vpc.tf @@ -0,0 +1,59 @@ +# +# Dedicated VPC +# + +module "gcp-network" { + source = "github.com/terraform-google-modules/terraform-google-network" + version = "~> 1.5" + + project_id = var.gcp_project + network_name = "${var.project}-gke-${var.env}" + routing_mode = var.network_routing_mode + + subnets = [ + { + subnet_name = "${var.project}-gke-${var.env}-${var.gcp_region}" + subnet_ip = var.subnet_cidr + subnet_region = var.gcp_region + subnet_private_access = "true" + }, + ] + + secondary_ranges = { + "${var.project}-gke-${var.env}-${var.gcp_region}" = [ + { + range_name = "${var.project}-gke-${var.env}-${var.gcp_region}-pods" + ip_cidr_range = var.pods_cidr + }, + { + range_name = "${var.project}-gke-${var.env}-${var.gcp_region}-services" + ip_cidr_range = var.services_cidr + }, + ] + } + + # routes = [ + # { + # name = "${var.project}-gke-${var.env}-${var.gcp_region}-egress-inet" + # description = "route through IGW to access internet" + # destination_range = "0.0.0.0/0" + # tags = "egress-inet" + # next_hop_internet = "true" + # }, + # ] +} + +# +# Cloud NAT +# + +module "cloud-nat" { + source = "github.com/terraform-google-modules/terraform-google-cloud-nat" + version = "~> 1.2" + + project_id = var.gcp_project + region = var.gcp_region + create_router = "true" + router = "${var.project}-gke-${var.env}-${var.gcp_region}-cloud-nat" + network = module.gcp-network.network_name +} diff --git a/generate/testdata/stack-gke/outputs.tf b/generate/testdata/stack-gke/outputs.tf new file mode 100644 index 0000000..1a7f34e --- /dev/null +++ b/generate/testdata/stack-gke/outputs.tf @@ -0,0 +1,107 @@ +# VPC +output "network_name" { + description = "GKE Cluster dedicated network name." + value = module.vpc.network_name +} + +output "network_self_link" { + description = "GKE Cluster dedicated network URI." + value = module.vpc.network_self_link +} + +output "subnet_name" { + description = "GKE Cluster dedicated subnet name." + value = module.vpc.subnet_name +} + +output "subnet_self_link" { + description = "GKE Cluster dedicated subnet URI." + value = module.vpc.subnet_self_link +} + +output "subnet_region" { + description = "GKE Cluster dedicated subnet region." + value = module.vpc.subnet_region +} + +output "pods_ip_range" { + description = "GKE Cluster dedicated pods IP range." + value = module.vpc.pods_ip_range +} + +output "services_ip_range" { + description = "GKE Cluster dedicated services IP range." + value = module.vpc.services_ip_range +} + + +# EKS Cluster +output "cluster_name" { + description = "GKE Cluster name." + value = module.gke.cluster_name +} + +output "cluster_type" { + description = "GKE Cluster type." + value = module.gke.cluster_type +} + +output "cluster_location" { + description = "GKE Cluster location (region if regional cluster, zone if zonal cluster)." + value = module.gke.cluster_location +} + +output "cluster_region" { + description = "GKE Cluster region." + value = module.gke.cluster_region +} + +output "cluster_zones" { + description = "GKE Cluster zones." + value = module.gke.cluster_zones +} + +output "cluster_master_version" { + description = "GKE Cluster master version." + value = module.gke.cluster_master_version +} + +output "cluster_release_channel" { + description = "GKE Cluster release channel." + value = module.gke.cluster_release_channel +} + +output "cluster_master_authorized_networks_config" { + description = "GKE Cluster networks from which access to master is permitted." + value = module.gke.cluster_master_authorized_networks_config +} + +output "cluster_endpoint" { + description = "GKE Cluster endpoint." + value = module.gke.cluster_endpoint +} + +output "cluster_ca" { + description = "GKE Cluster certificate authority." + value = module.gke.cluster_ca +} + +output "node_pools_names" { + description = "GKE Cluster node pools names." + value = module.gke.node_pools_names +} + +output "node_pools_versions" { + description = "GKE Cluster node pools versions." + value = module.gke.node_pools_versions +} + +output "node_pools_service_account" { + description = "GKE Cluster nodes default service account if not overriden in `node_pools`." + value = module.gke.node_pools_service_account +} + +output "kubeconfig" { + description = "Kubernetes config to connect to the GKE cluster." + value = module.gke.kubeconfig +} diff --git a/generate/testdata/stack-gke/provider.tf b/generate/testdata/stack-gke/provider.tf new file mode 100644 index 0000000..88d92d3 --- /dev/null +++ b/generate/testdata/stack-gke/provider.tf @@ -0,0 +1,19 @@ +# GCP +provider "google" { + version = "~> 2.18.0" +} + +provider "google-beta" { + version = "~> 2.18.0" +} + +# Kubernetes +data "google_client_config" "default" { +} + +provider "kubernetes" { + host = module.gke.cluster_endpoint + cluster_ca_certificate = base64decode(module.gke.cluster_ca) + token = data.google_client_config.default.access_token + load_config_file = false +} diff --git a/generate/testdata/stack-gke/variables.tf b/generate/testdata/stack-gke/variables.tf new file mode 100644 index 0000000..5bac189 --- /dev/null +++ b/generate/testdata/stack-gke/variables.tf @@ -0,0 +1,28 @@ +# Cycloid requirements +variable "project" { + description = "Cycloid project name." +} + +variable "env" { + description = "Cycloid environment name." +} + +variable "customer" { + description = "Cycloid customer name." +} + +# GCP +variable "gcp_project" { + description = "GCP project to launch services." + default = "kubernetes-gke" +} + +variable "gcp_region" { + description = "GCP region to launch services." + default = "europe-west1" +} + +# EKS +locals { + gke_cluster_name = length(local.cluster_name) > 0 ? local.cluster_name : "${var.project}-${var.env}" +} \ No newline at end of file diff --git a/generate/testdata/stack-gke/versions.tf b/generate/testdata/stack-gke/versions.tf new file mode 100644 index 0000000..ac97c6a --- /dev/null +++ b/generate/testdata/stack-gke/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/generate/testdata/vpc-bis.tf b/generate/testdata/vpc-bis.tf new file mode 100644 index 0000000..3e3d5bc --- /dev/null +++ b/generate/testdata/vpc-bis.tf @@ -0,0 +1,196 @@ +# Put here a custom name for the GKE Cluster +# Otherwise `${var.project}-${var.env}` will be used +locals { + cluster_name = "" +} + +# https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters +# You cannot use a cluster master, node, Pod, or Service IP range that overlaps with 172.17.0.0/16. +# The size of the RFC 1918 block for the cluster master must be /28. + +module "vpc" { + ##################################### + # Do not modify the following lines # + source = "./module-vpc" + + project = var.project + env = var.env + customer = var.customer + + ##################################### + + ### + # General + ### + + #. gcp_project (required): + #+ The Google Cloud Platform project to use. + gcp_project = var.gcp_project + + #. gcp_region (optional): eu-central1 + #+ The Google Cloud Platform region to use. + gcp_region = var.gcp_region + + #. extra_labels (optional): {} + #+ Dict of extra labels to add on aws resources. format { "foo" = "bar" }. + + ### + # Networking + ### + + #. subnet_cidr (optional): 10.8.0.0/16 + #+ The CIDR of the VPC subnet. + subnet_cidr = "10.8.0.0/16" + + #. pods_cidr (optional): 10.9.0.0/16 + #+ The CIDR of the pods secondary range. + pods_cidr = "10.9.0.0/16" + + #. services_cidr (optional): 10.10.0.0/16 + #+ The CIDR of the services secondary range. + services_cidr = "10.10.0.0/16" + + #. network_routing_mode (optional): GLOBAL + #+ The network routing mode. + + ### + # Required (should probably not be touched) + ### + + cluster_name = local.gke_cluster_name +} + +module "gke" { + ##################################### + # Do not modify the following lines # + source = "./module-gke" + + project = var.project + env = var.env + customer = var.customer + + ##################################### + + ### + # General + ### + + #. gcp_project (required): + #+ The Google Cloud Platform project to use. + gcp_project = var.gcp_project + + #. gcp_region (optional): eu-central1 + #+ The Google Cloud Platform region to use. + gcp_region = var.gcp_region + + #. gcp_zones (optional): [] + #+ To use specific Google Cloud Platform zones if not regional, otherwise it will be chosen randomly. + + #. extra_labels (optional): {} + #+ Dict of extra labels to add on GCP resources. format { "foo" = "bar" }. + + ### + # Control plane + ### + + #. cluster_version (optional): latest + #+ GKE Cluster version to use. + + #. cluster_release_channel (optional): UNSPECIFIED + #+ GKE Cluster release channel to use. Accepted values are UNSPECIFIED, RAPID, REGULAR and STABLE. + + #. cluster_regional (optional): false + #+ If the GKE Cluster must be regional or zonal. Be careful, this setting is destructive. + + #. enable_only_private_endpoint (optional): false + #+ If true, only enable the private endpoint which disable the Public endpoint entirely. If false, private endpoint will be enabled, and the public endpoint will be only accessible by master authorized networks. + + #. master_authorized_networks (optional): [] + #+ List of master authorized networks. + # master_authorized_networks = [ + # { + # name: "my-ip", + # cidr: "x.x.x.x/32" + # } + # ] + + #. enable_network_policy (optional): true + #+ Enable GKE Cluster network policies addon. + + #. enable_horizontal_pod_autoscaling (optional): true + #+ Enable GKE Cluster horizontal pod autoscaling addon. + + #. enable_vertical_pod_autoscaling (optional): false + #+ Enable GKE Cluster vertical pod autoscaling addon. Vertical Pod Autoscaling automatically adjusts the resources of pods controlled by it. + + #. enable_http_load_balancing (optional): false + #+ Enable GKE Cluster HTTP load balancing addon. + + #. enable_binary_authorization (optional): false + #+ Enable GKE Cluster BinAuthZ Admission controller. + + #. enable_cloudrun (optional): false + #+ Enable GKE Cluster Cloud Run for Anthos addon. + + #. enable_istio (optional): false + #+ Enable GKE Cluster Istio addon. + + ### + # Node pools + ### + + #. node_pools (optional): [] + #+ GKE Cluster node pools to create. + node_pools = [ + { + name = "my-node-pool" + machine_type = "n1-standard-1" + image_type = "COS" + + auto_repair = true + auto_upgrade = false + preemptible = false + + autoscaling = true + initial_node_count = 1 + min_count = 1 + max_count = 1 + + # autoscaling = false + # node_count = 1 + + local_ssd_count = 0 + disk_size_gb = 100 + disk_type = "pd-ssd" + + # service_account = "" + # accelerator_count = 0 + # accelerator_type = "" + + # oauth_scopes = [] + # metadata = {} + # labels = {} + # taints = [] + # tags = [] + }, + ] + + #. enable_shielded_nodes (optional): true + #+ Enable GKE Cluster Shielded Nodes features on all nodes. + + #. enable_sandbox (optional): false + #+ Enable GKE Sandbox (Do not forget to set image_type = COS_CONTAINERD and node_version = 1.12.7-gke.17 or later to use it). + + #. default_max_pods_per_node (optional): 110 + #+ The maximum number of pods to schedule per node. + + ### + # Required (should probably not be touched) + ### + + cluster_name = local.gke_cluster_name + subnet_name = module.vpc.subnet_name + pods_ip_range = module.vpc.pods_ip_range + services_ip_range = module.vpc.services_ip_range +} + diff --git a/generate/testdata/vpc.tf b/generate/testdata/vpc.tf new file mode 100644 index 0000000..9378fd0 --- /dev/null +++ b/generate/testdata/vpc.tf @@ -0,0 +1,57 @@ +# +# Dedicated VPC +# + +module "gcp-network" { + source = "github.com/terraform-google-modules/terraform-google-network" + + project_id = var.gcp_project + network_name = "${var.project}-gke-${var.env}" + routing_mode = var.network_routing_mode + + subnets = [ + { + subnet_name = "${var.project}-gke-${var.env}-${var.gcp_region}" + subnet_ip = var.subnet_cidr + subnet_region = var.gcp_region + subnet_private_access = "true" + }, + ] + + secondary_ranges = { + "${var.project}-gke-${var.env}-${var.gcp_region}" = [ + { + range_name = "${var.project}-gke-${var.env}-${var.gcp_region}-pods" + ip_cidr_range = var.pods_cidr + }, + { + range_name = "${var.project}-gke-${var.env}-${var.gcp_region}-services" + ip_cidr_range = var.services_cidr + }, + ] + } + + # routes = [ + # { + # name = "${var.project}-gke-${var.env}-${var.gcp_region}-egress-inet" + # description = "route through IGW to access internet" + # destination_range = "0.0.0.0/0" + # tags = "egress-inet" + # next_hop_internet = "true" + # }, + # ] +} + +# +# Cloud NAT +# + +module "cloud-nat" { + source = "github.com/terraform-google-modules/terraform-google-cloud-nat" + + project_id = var.gcp_project + region = var.gcp_region + create_router = "true" + router = "${var.project}-gke-${var.env}-${var.gcp_region}-cloud-nat" + network = module.gcp-network.network_name +} diff --git a/go.mod b/go.mod index 72357b8..abf2912 100644 --- a/go.mod +++ b/go.mod @@ -14,15 +14,17 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.5.1 github.com/hashicorp/go-hclog v0.14.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.8 // indirect - github.com/hashicorp/go-version v1.3.0 // indirect + github.com/hashicorp/go-version v1.3.0 github.com/hashicorp/hcl/v2 v2.9.1 github.com/hashicorp/terraform v0.14.10 github.com/markbates/pkger v0.17.1 github.com/mattn/go-colorable v0.1.7 // indirect github.com/mitchellh/copystructure v1.1.2 // indirect + github.com/mitchellh/go-testing-interface v1.0.4 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/pascaldekloe/name v1.0.1 // indirect github.com/satori/go.uuid v1.2.0 diff --git a/go.sum b/go.sum index 11cc129..98ad009 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,7 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -30,6 +31,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -93,10 +95,12 @@ github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2 github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.37.0 h1:GzFnhOIsrGyQ69s7VgqtrG2BG8v7X7vwB3Xpbd/DBBk= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -188,6 +192,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -230,7 +235,9 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 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= @@ -243,6 +250,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM= @@ -268,6 +276,7 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 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.5.1 h1:lM9sM02nvEApQGFgkXxWbhfqtyN+AyhQmi+MaMdBDOI= github.com/hashicorp/go-getter v1.5.1/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -285,6 +294,7 @@ github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs= github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +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-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -330,7 +340,9 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= @@ -339,6 +351,7 @@ github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBv github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -402,10 +415,13 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.4 h1:ZU1VNC02qyufSZsjjs7+khruk2fKvbQ3TwRV/IBCeFA= +github.com/mitchellh/go-testing-interface v1.0.4/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -519,6 +535,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tombuildsstuff/giovanni v0.12.0/go.mod h1:qJ5dpiYWkRsuOSXO8wHbee7+wElkLNfWVolcf59N84E= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 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= @@ -544,6 +561,7 @@ 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= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -795,6 +813,7 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A= google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 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= @@ -834,6 +853,7 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -848,6 +868,7 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 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= @@ -883,6 +904,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=