diff --git a/.github/actions/go_init/action.yml b/.github/actions/go_init/action.yml index 74819a84..0b815d31 100644 --- a/.github/actions/go_init/action.yml +++ b/.github/actions/go_init/action.yml @@ -12,10 +12,17 @@ runs: with: repository: overmindtech/aws-source path: ./aws-source + - name: Checkout + uses: actions/checkout@v3 + with: + repository: overmindtech/k8s-source + path: ./k8s-source - name: Move sources in place shell: bash - run: mv -v aws-source .. + run: | + mv -v aws-source .. + mv -v k8s-source .. - name: Go Generate shell: bash diff --git a/README.md b/README.md index 2e7cf7a4..9476ee07 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,26 @@ terraform show -json ./tfplan > ./tfplan.json ovm-cli submit-plan --title "example change" --plan-json ./tfplan.json ``` +## Terraform ➡ Overmind Mapping + +In order to calculate the blast radius from a Terraform plan, we use mappings provided by the sources to map a Terraform resource change to an Overmind item. In many cases this is simple, however in some instances, the plan doesn't have enough information for us to determine which resource the change is referring to. A good example is a Terraform environment that manages 2x Kubernetes deployments in 2x clusters which both have the same name. + +By default we'll add both deployments to the blast radius since we can't tell them apart. However to improve the results, you can add the `overmind_mappings` output to your plan: + +```hcl +output "overmind_mappings" { + value = { + # The key here should be the name of the provider. Resources that use this + # provider will be mapped to a cluster with the below name. If you had + # another provider with an alias such as "prod" the name would be + # "kubernetes.prod" + kubernetes = { + cluster_name = var.terraform_env_name + } + } +} +``` + +Valid mapping values are: + +* `cluster_name`: The name of the cluster that was provided to the kubernetes source using the `source.clusterName` option \ No newline at end of file diff --git a/cmd/datamaps/k8ssource.go b/cmd/datamaps/k8ssource.go new file mode 100644 index 00000000..b8b1e450 --- /dev/null +++ b/cmd/datamaps/k8ssource.go @@ -0,0 +1,408 @@ +// Code generated by "extractmaps k8s-source"; DO NOT EDIT + +package datamaps + +import "github.com/overmindtech/sdp-go" + +var K8ssourceData = map[string][]TfMapData{ + "kubernetes_cluster_role_binding": { + { + Type: "ClusterRoleBinding", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}", + }, + }, + "kubernetes_cluster_role_binding_v1": { + { + Type: "ClusterRoleBinding", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}", + }, + }, + "kubernetes_cluster_role_v1": { + { + Type: "ClusterRole", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}", + }, + }, + "kubernetes_config_map": { + { + Type: "ConfigMap", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_config_map_v1": { + { + Type: "ConfigMap", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_cron_job": { + { + Type: "CronJob", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_cron_job_v1": { + { + Type: "CronJob", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_daemon_set_v1": { + { + Type: "DaemonSet", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_daemonset": { + { + Type: "DaemonSet", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_deployment": { + { + Type: "Deployment", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_deployment_v1": { + { + Type: "Deployment", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_endpoints": { + { + Type: "Endpoints", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_endpoints_slice_v1": { + { + Type: "EndpointSlice", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_endpoints_v1": { + { + Type: "Endpoints", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_horizontal_pod_autoscaler_v2": { + { + Type: "HorizontalPodAutoscaler", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_ingress_v1": { + { + Type: "Ingress", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_job": { + { + Type: "Job", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_job_v1": { + { + Type: "Job", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_limit_range": { + { + Type: "LimitRange", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_limit_range_v1": { + { + Type: "LimitRange", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_network_policy": { + { + Type: "NetworkPolicy", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_network_policy_v1": { + { + Type: "NetworkPolicy", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_node_taint": { + { + Type: "Node", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_persistent_volume": { + { + Type: "PersistentVolume", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_persistent_volume_claim": { + { + Type: "PersistentVolumeClaim", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_persistent_volume_claim_v1": { + { + Type: "PersistentVolumeClaim", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_persistent_volume_v1": { + { + Type: "PersistentVolume", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_pod": { + { + Type: "Pod", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_pod_disruption_budget_v1": { + { + Type: "PodDisruptionBudget", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_pod_v1": { + { + Type: "Pod", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_priority_class": { + { + Type: "PriorityClass", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_priority_class_v1": { + { + Type: "PriorityClass", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_replication_controller": { + { + Type: "ReplicationController", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_replication_controller_v1": { + { + Type: "ReplicationController", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_resource_quota": { + { + Type: "ResourceQuota", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_resource_quota_v1": { + { + Type: "ResourceQuota", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_role": { + { + Type: "Role", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_role_binding": { + { + Type: "RoleBinding", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_role_binding_v1": { + { + Type: "RoleBinding", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_role_v1": { + { + Type: "Role", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_secret": { + { + Type: "Secret", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_secret_v1": { + { + Type: "Secret", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_service": { + { + Type: "Service", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_service_account": { + { + Type: "ServiceAccount", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_service_account_v1": { + { + Type: "ServiceAccount", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_service_v1": { + { + Type: "Service", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_stateful_set": { + { + Type: "StatefulSet", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_stateful_set_v1": { + { + Type: "StatefulSet", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_storage_class": { + { + Type: "StorageClass", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, + "kubernetes_storage_class_v1": { + { + Type: "StorageClass", + Method: sdp.QueryMethod_GET, + QueryField: "metadata[0].name", + Scope: "${provider_mapping.cluster_name}.${values.metadata[0].namespace}", + }, + }, +} diff --git a/cmd/datamaps/types.go b/cmd/datamaps/types.go index 675fcbf8..cc024aec 100644 --- a/cmd/datamaps/types.go +++ b/cmd/datamaps/types.go @@ -1,12 +1,24 @@ package datamaps -import "github.com/overmindtech/sdp-go" +import ( + "github.com/overmindtech/sdp-go" +) //go:generate go run ../../extractmaps.go aws-source +//go:generate go run ../../extractmaps.go k8s-source type TfMapData struct { - Type string - Method sdp.QueryMethod + // The overmind type name + Type string + + // The method that the query should use + Method sdp.QueryMethod + + // The field within the resource that should be queried for QueryField string - Scope string + + // The scope for the query. This can be either `*`, `global` or a string + // that includes interpolations in Terraform format i.e. + // ${outputs.overmind_kubernetes_cluster_name}.${values.metadata.namespace} + Scope string } diff --git a/cmd/plan.go b/cmd/plan.go new file mode 100644 index 00000000..d1a23372 --- /dev/null +++ b/cmd/plan.go @@ -0,0 +1,611 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/xiam/dig" +) + +// NOTE: These definitions are copied from the +// https://pkg.go.dev/github.com/hashicorp/terraform/internal/command/jsonplan +// package, which is internal so should be imported directly. Hence why we have +// copied them here + +// Plan is the top-level representation of the json format of a plan. It includes +// the complete config and current state. +type Plan struct { + FormatVersion string `json:"format_version,omitempty"` + TerraformVersion string `json:"terraform_version,omitempty"` + Variables Variables `json:"variables,omitempty"` + PlannedValues StateValues `json:"planned_values,omitempty"` + // ResourceDrift and ResourceChanges are sorted in a user-friendly order + // that is undefined at this time, but consistent. + ResourceDrift []ResourceChange `json:"resource_drift,omitempty"` + ResourceChanges []ResourceChange `json:"resource_changes,omitempty"` + OutputChanges map[string]Change `json:"output_changes,omitempty"` + PriorState json.RawMessage `json:"prior_state,omitempty"` + Config config `json:"configuration,omitempty"` + RelevantAttributes []ResourceAttr `json:"relevant_attributes,omitempty"` + Checks json.RawMessage `json:"checks,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + Errored bool `json:"errored"` +} + +// Config represents the complete configuration source +type config struct { + ProviderConfigs map[string]ProviderConfig `json:"provider_config,omitempty"` + RootModule ConfigModule `json:"root_module,omitempty"` +} + +// ProviderConfig describes all of the provider configurations throughout the +// configuration tree, flattened into a single map for convenience since +// provider configurations are the one concept in Terraform that can span across +// module boundaries. +type ProviderConfig struct { + Name string `json:"name,omitempty"` + FullName string `json:"full_name,omitempty"` + Alias string `json:"alias,omitempty"` + VersionConstraint string `json:"version_constraint,omitempty"` + ModuleAddress string `json:"module_address,omitempty"` + Expressions map[string]interface{} `json:"expressions,omitempty"` +} + +type ConfigModule struct { + Outputs map[string]output `json:"outputs,omitempty"` + // Resources are sorted in a user-friendly order that is undefined at this + // time, but consistent. + Resources []ConfigResource `json:"resources,omitempty"` + ModuleCalls map[string]moduleCall `json:"module_calls,omitempty"` + Variables variables `json:"variables,omitempty"` +} + +// Digs through a map using the same logic that terraform does i.e. foo.bar[0] +func TerraformDig(srcMapPtr interface{}, path string) interface{} { + // Split the path on each period + parts := strings.Split(path, ".") + + if len(parts) == 0 { + return nil + } + + // Check for an index in this section + indexMatches := indexBrackets.FindStringSubmatch(parts[0]) + + var value interface{} + + if len(indexMatches) == 0 { + // No index, just get the value + value = dig.Interface(srcMapPtr, parts[0]) + } else { + // strip the brackets + keyName := indexBrackets.ReplaceAllString(parts[0], "") + + // Get the index + index, err := strconv.Atoi(indexMatches[1]) + + if err != nil { + return nil + } + + // Get the value + arr, ok := dig.Interface(srcMapPtr, keyName).([]interface{}) + + if !ok { + return nil + } + + // Check if the index is in range + if index < 0 || index >= len(arr) { + return nil + } + + value = arr[index] + } + + if len(parts) == 1 { + return value + } else { + // Force it to another map[string]interface{} + valueMap := make(map[string]interface{}) + + if mapString, ok := value.(map[string]string); ok { + for k, v := range mapString { + valueMap[k] = v + } + } else if mapInterface, ok := value.(map[string]interface{}); ok { + valueMap = mapInterface + } else if mapAttributeValues, ok := value.(AttributeValues); ok { + valueMap = mapAttributeValues + } else { + return nil + } + + return TerraformDig(&valueMap, strings.Join(parts[1:], ".")) + } +} + +var escapeRegex = regexp.MustCompile(`\${([\w\.\[\]]*)}`) + +// InterpolateScope Will interpolate variables in the scope string. These +// variables can come from the following places: +// +// * `outputs` - These are the outputs from the plan +// * `values` - These are the values from the resource in question +// +// Interpolation is done using the Terraform interpolation syntax: +// https://www.terraform.io/docs/configuration/interpolation.html +func InterpolateScope(scope string, data map[string]any) (string, error) { + // Find all instances of ${} in the Scope + matches := escapeRegex.FindAllStringSubmatch(scope, -1) + + interpolated := scope + + for _, match := range matches { + // The first match is the entire string, the second match is the + // variable name + variableName := match[1] + + value := TerraformDig(&data, variableName) + + if value == nil { + return "", fmt.Errorf("variable '%v' not found", variableName) + } + + // Convert the value to a string + valueString, ok := value.(string) + + if !ok { + return "", fmt.Errorf("variable '%v' is not a string", variableName) + } + + interpolated = strings.Replace(interpolated, match[0], valueString, 1) + } + + return interpolated, nil +} + +// Digs for a config resource in this module or its children +func (m ConfigModule) DigResource(address string) *ConfigResource { + addressSections := strings.Split(address, ".") + + if len(addressSections) == 0 { + return nil + } + + if addressSections[0] == "module" { + // If it's addressed to a module, then we need to dig into that module + if len(addressSections) < 2 { + return nil + } + + moduleName := addressSections[1] + + if module, ok := m.ModuleCalls[moduleName]; ok { + // Dig through the correct module + return module.Module.DigResource(strings.Join(addressSections[2:], ".")) + } + } else { + // If the address has brackets, than we need to extract the index and + // return the resource at that index + indexMatches := indexBrackets.FindStringSubmatch(address) + var desiredIndex int + var err error + + if len(indexMatches) == 0 { + // Return the first result + desiredIndex = 0 + } else { + desiredIndex, err = strconv.Atoi(indexMatches[1]) + + if err != nil { + return nil + } + } + + // Remove the [] from the address if it exists + address = indexBrackets.ReplaceAllString(address, "") + + // Look through the current module + currentIndex := 0 + for _, r := range m.Resources { + if r.Address == address { + if currentIndex == desiredIndex { + return &r + } + + currentIndex++ + } + } + } + + return nil +} + +type moduleCall struct { + Source string `json:"source,omitempty"` + Expressions map[string]interface{} `json:"expressions,omitempty"` + CountExpression *expression `json:"count_expression,omitempty"` + ForEachExpression *expression `json:"for_each_expression,omitempty"` + Module ConfigModule `json:"module,omitempty"` + VersionConstraint string `json:"version_constraint,omitempty"` + DependsOn []string `json:"depends_on,omitempty"` +} + +// variables is the JSON representation of the variables provided to the current +// plan. +type variables map[string]*variable + +type variable struct { + Default json.RawMessage `json:"default,omitempty"` + Description string `json:"description,omitempty"` + Sensitive bool `json:"sensitive,omitempty"` +} + +// Resource is the representation of a resource in the config +type ConfigResource struct { + // Address is the absolute resource address + Address string `json:"address,omitempty"` + + // Mode can be "managed" or "data" + Mode string `json:"mode,omitempty"` + + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + + // ProviderConfigKey is the key into "provider_configs" (shown above) for + // the provider configuration that this resource is associated with. + // + // NOTE: If a given resource is in a ModuleCall, and the provider was + // configured outside of the module (in a higher level configuration file), + // the ProviderConfigKey will not match a key in the ProviderConfigs map. + ProviderConfigKey string `json:"provider_config_key,omitempty"` + + // Provisioners is an optional field which describes any provisioners. + // Connection info will not be included here. + Provisioners []provisioner `json:"provisioners,omitempty"` + + // Expressions" describes the resource-type-specific content of the + // configuration block. + Expressions map[string]interface{} `json:"expressions,omitempty"` + + // SchemaVersion indicates which version of the resource type schema the + // "values" property conforms to. + SchemaVersion uint64 `json:"schema_version"` + + // CountExpression and ForEachExpression describe the expressions given for + // the corresponding meta-arguments in the resource configuration block. + // These are omitted if the corresponding argument isn't set. + CountExpression *expression `json:"count_expression,omitempty"` + ForEachExpression *expression `json:"for_each_expression,omitempty"` + + DependsOn []string `json:"depends_on,omitempty"` +} + +type output struct { + Sensitive bool `json:"sensitive,omitempty"` + Expression expression `json:"expression,omitempty"` + DependsOn []string `json:"depends_on,omitempty"` + Description string `json:"description,omitempty"` +} + +type provisioner struct { + Type string `json:"type,omitempty"` + Expressions map[string]interface{} `json:"expressions,omitempty"` +} + +// expression represents any unparsed expression +type expression struct { + // "constant_value" is set only if the expression contains no references to + // other objects, in which case it gives the resulting constant value. This + // is mapped as for the individual values in the common value + // representation. + ConstantValue json.RawMessage `json:"constant_value,omitempty"` + + // Alternatively, "references" will be set to a list of references in the + // expression. Multi-step references will be unwrapped and duplicated for + // each significant traversal step, allowing callers to more easily + // recognize the objects they care about without attempting to parse the + // expressions. Callers should only use string equality checks here, since + // the syntax may be extended in future releases. + References []string `json:"references,omitempty"` +} + +// Variables is the JSON representation of the variables provided to the current +// plan. +type Variables map[string]*Variable + +type Variable struct { + Value json.RawMessage `json:"value,omitempty"` +} + +// StateValues is the common representation of resolved values for both the +// prior state (which is always complete) and the planned new state. +type StateValues struct { + Outputs map[string]Output `json:"outputs,omitempty"` + RootModule Module `json:"root_module,omitempty"` +} + +// Get a specific resource from this module or its children +func (m Module) DigResource(address string) *Resource { + // Look through the current module + for _, r := range m.Resources { + if r.Address == address { + return &r + } + } + + // Look through children + for _, child := range m.ChildModules { + resource := child.DigResource(address) + + if resource != nil { + return resource + } + } + + return nil +} + +// Module is the representation of a module in state. This can be the root +// module or a child module. +type Module struct { + // Resources are sorted in a user-friendly order that is undefined at this + // time, but consistent. + Resources []Resource `json:"resources,omitempty"` + + // Address is the absolute module address, omitted for the root module + Address string `json:"address,omitempty"` + + // Each module object can optionally have its own nested "child_modules", + // recursively describing the full module tree. + ChildModules []Module `json:"child_modules,omitempty"` +} + +// Resource is the representation of a resource in the json plan +type Resource struct { + // Address is the absolute resource address + Address string `json:"address,omitempty"` + + // Mode can be "managed" or "data" + Mode string `json:"mode,omitempty"` + + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + + // ProviderName allows the property "type" to be interpreted unambiguously + // in the unusual situation where a provider offers a resource type whose + // name does not start with its own name, such as the "googlebeta" provider + // offering "google_compute_instance". + ProviderName string `json:"provider_name,omitempty"` + + // SchemaVersion indicates which version of the resource type schema the + // "values" property conforms to. + SchemaVersion uint64 `json:"schema_version"` + + // AttributeValues is the JSON representation of the attribute values of the + // resource, whose structure depends on the resource type schema. Any + // unknown values are omitted or set to null, making them indistinguishable + // from absent values. + AttributeValues AttributeValues `json:"values,omitempty"` + + // SensitiveValues is similar to AttributeValues, but with all sensitive + // values replaced with true, and all non-sensitive leaf values omitted. + SensitiveValues json.RawMessage `json:"sensitive_values,omitempty"` +} + +// AttributeValues is the JSON representation of the attribute values of the +// resource, whose structure depends on the resource type schema. +type AttributeValues map[string]interface{} + +var indexBrackets = regexp.MustCompile(`\[(\d+)\]`) + +// Digs through the attribute values to find the value at the given key. This +// supports nested keys i.e. "foo.bar" and arrays i.e. "foo[0]" +func (av AttributeValues) Dig(key string) (interface{}, bool) { + sections := strings.Split(key, ".") + + if len(sections) == 0 { + return nil, false + } + + // Get the first section + section := sections[0] + + // Check for an index + indexMatches := indexBrackets.FindStringSubmatch(section) + + var value interface{} + var ok bool + + if len(indexMatches) == 0 { + // No index, just get the value + value, ok = av[section] + + if !ok { + return nil, false + } + } else { + // Get the index + index, err := strconv.Atoi(indexMatches[1]) + + if err != nil { + return nil, false + } + + // Get the value + keyName := indexBrackets.ReplaceAllString(section, "") + arr, ok := av[keyName] + + if !ok { + return nil, false + } + + // Check if the value is an array + array, ok := arr.([]interface{}) + + if !ok { + return nil, false + } + + // Check if the index is in range + if index < 0 || index >= len(array) { + return nil, false + } + + value = array[index] + } + + // If there are no more sections, then we're done + if len(sections) == 1 { + return value, true + } + + // If there are more sections, then we need to dig deeper + childMap, ok := value.(map[string]interface{}) + + if !ok { + return nil, false + } + + childAttributeValues := AttributeValues(childMap) + + return childAttributeValues.Dig(strings.Join(sections[1:], ".")) +} + +type Output struct { + Sensitive bool `json:"sensitive"` + Type json.RawMessage `json:"type,omitempty"` + Value json.RawMessage `json:"value,omitempty"` +} + +// ResourceChange is a description of an individual change action that Terraform +// plans to use to move from the prior state to a new state matching the +// configuration. +type ResourceChange struct { + // Address is the absolute resource address + Address string `json:"address,omitempty"` + + // PreviousAddress is the absolute address that this resource instance had + // at the conclusion of a previous run. + // + // This will typically be omitted, but will be present if the previous + // resource instance was subject to a "moved" block that we handled in the + // process of creating this plan. + // + // Note that this behavior diverges from the internal plan data structure, + // where the previous address is set equal to the current address in the + // common case, rather than being omitted. + PreviousAddress string `json:"previous_address,omitempty"` + + // ModuleAddress is the module portion of the above address. Omitted if the + // instance is in the root module. + ModuleAddress string `json:"module_address,omitempty"` + + // "managed" or "data" + Mode string `json:"mode,omitempty"` + + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Index json.RawMessage `json:"index,omitempty"` + ProviderName string `json:"provider_name,omitempty"` + + // "deposed", if set, indicates that this action applies to a "deposed" + // object of the given instance rather than to its "current" object. Omitted + // for changes to the current object. + Deposed string `json:"deposed,omitempty"` + + // Change describes the change that will be made to this object + Change Change `json:"change,omitempty"` + + // ActionReason is a keyword representing some optional extra context + // for why the actions in Change.Actions were chosen. + // + // This extra detail is only for display purposes, to help a UI layer + // present some additional explanation to a human user. The possible + // values here might grow and change over time, so any consumer of this + // information should be resilient to encountering unrecognized values + // and treat them as an unspecified reason. + ActionReason string `json:"action_reason,omitempty"` +} + +// Change is the representation of a proposed change for an object. +type Change struct { + // Actions are the actions that will be taken on the object selected by the + // properties below. Valid actions values are: + // ["no-op"] + // ["create"] + // ["read"] + // ["update"] + // ["delete", "create"] + // ["create", "delete"] + // ["delete"] + // The two "replace" actions are represented in this way to allow callers to + // e.g. just scan the list for "delete" to recognize all three situations + // where the object will be deleted, allowing for any new deletion + // combinations that might be added in future. + Actions []string `json:"actions,omitempty"` + + // Before and After are representations of the object value both before and + // after the action. For ["create"] and ["delete"] actions, either "before" + // or "after" is unset (respectively). For ["no-op"], the before and after + // values are identical. The "after" value will be incomplete if there are + // values within it that won't be known until after apply. + Before json.RawMessage `json:"before,omitempty"` + After json.RawMessage `json:"after,omitempty"` + + // AfterUnknown is an object value with similar structure to After, but + // with all unknown leaf values replaced with true, and all known leaf + // values omitted. This can be combined with After to reconstruct a full + // value after the action, including values which will only be known after + // apply. + AfterUnknown json.RawMessage `json:"after_unknown,omitempty"` + + // BeforeSensitive and AfterSensitive are object values with similar + // structure to Before and After, but with all sensitive leaf values + // replaced with true, and all non-sensitive leaf values omitted. These + // objects should be combined with Before and After to prevent accidental + // display of sensitive values in user interfaces. + BeforeSensitive json.RawMessage `json:"before_sensitive,omitempty"` + AfterSensitive json.RawMessage `json:"after_sensitive,omitempty"` + + // ReplacePaths is an array of arrays representing a set of paths into the + // object value which resulted in the action being "replace". This will be + // omitted if the action is not replace, or if no paths caused the + // replacement (for example, if the resource was tainted). Each path + // consists of one or more steps, each of which will be a number or a + // string. + ReplacePaths json.RawMessage `json:"replace_paths,omitempty"` + + // Importing contains the import metadata about this operation. If importing + // is present (ie. not null) then the change is an import operation in + // addition to anything mentioned in the actions field. The actual contents + // of the Importing struct is subject to change, so downstream consumers + // should treat any values in here as strictly optional. + Importing *Importing `json:"importing,omitempty"` + + // GeneratedConfig contains any HCL config generated for this resource + // during planning as a string. + // + // If this is populated, then Importing should also be populated but this + // might change in the future. However, nNot all Importing changes will + // contain generated config. + GeneratedConfig string `json:"generated_config,omitempty"` +} + +// Importing is a nested object for the resource import metadata. +type Importing struct { + // The original ID of this resource used to target it as part of planned + // import operation. + ID string `json:"id,omitempty"` +} + +// ResourceAttr contains the address and attribute of an external for the +// RelevantAttributes in the plan. +type ResourceAttr struct { + Resource string `json:"resource"` + Attr json.RawMessage `json:"attribute"` +} diff --git a/cmd/plan_test.go b/cmd/plan_test.go new file mode 100644 index 00000000..433113a4 --- /dev/null +++ b/cmd/plan_test.go @@ -0,0 +1,70 @@ +package cmd + +import "testing" + +func TestInterpolateScope(t *testing.T) { + t.Run("with no interpolation", func(t *testing.T) { + t.Parallel() + + result, err := InterpolateScope("foo", map[string]any{}) + + if err != nil { + t.Error(err) + } + + if result != "foo" { + t.Errorf("Expected result to be foo, got %s", result) + } + }) + + t.Run("with a single variable", func(t *testing.T) { + t.Parallel() + + result, err := InterpolateScope("${outputs.overmind_kubernetes_cluster_name}", map[string]any{ + "outputs": map[string]any{ + "overmind_kubernetes_cluster_name": "foo", + }, + }) + + if err != nil { + t.Error(err) + } + + if result != "foo" { + t.Errorf("Expected result to be foo, got %s", result) + } + }) + + t.Run("with multiple variables", func(t *testing.T) { + t.Parallel() + + result, err := InterpolateScope("${outputs.overmind_kubernetes_cluster_name}.${values.metadata.namespace}", map[string]any{ + "outputs": map[string]any{ + "overmind_kubernetes_cluster_name": "foo", + }, + "values": map[string]any{ + "metadata": map[string]any{ + "namespace": "bar", + }, + }, + }) + + if err != nil { + t.Error(err) + } + + if result != "foo.bar" { + t.Errorf("Expected result to be foo.bar, got %s", result) + } + }) + + t.Run("with a variable that doesn't exist", func(t *testing.T) { + t.Parallel() + + _, err := InterpolateScope("${outputs.overmind_kubernetes_cluster_name}", map[string]any{}) + + if err == nil { + t.Error("Expected error, got nil") + } + }) +} diff --git a/cmd/submitplan.go b/cmd/submitplan.go index 0ef7ea5c..d748d208 100644 --- a/cmd/submitplan.go +++ b/cmd/submitplan.go @@ -55,66 +55,116 @@ type TfData struct { } func changingItemQueriesFromPlan(ctx context.Context, planJSON []byte, lf log.Fields) ([]*sdp.Query, error) { - changing_items_tf := map[string]TfData{} - - var parsed map[string]any - err := json.Unmarshal(planJSON, &parsed) + var plan Plan + err := json.Unmarshal(planJSON, &plan) if err != nil { return nil, fmt.Errorf("failed to parse %v: %w", viper.GetString("plan-json"), err) } - root_module := parsed["planned_values"].(map[string]any)["root_module"].(map[string]any) - resourceValues := map[string]map[string]any{} - resourceValuesFromModule(root_module, &resourceValues) - - resource_changes := parsed["resource_changes"].([]any) - for _, changed_item_data := range resource_changes { - changed_item_map := changed_item_data.(map[string]any) - change := changed_item_map["change"].(map[string]any) - actions := change["actions"].([]any) - if len(actions) == 0 || actions[0] == "no-op" { + var changing_items []*sdp.Query + // for all managed resources: + for _, resourceChange := range plan.ResourceChanges { + if len(resourceChange.Change.Actions) == 0 || resourceChange.Change.Actions[0] == "no-op" { // skip resources with no changes continue } - log.WithContext(ctx).WithFields(lf).WithFields(log.Fields{ - "actions": actions, - "address": changed_item_map["address"], - }).Debugf("mapping item") - - changed_item := TfData{ - Address: changed_item_map["address"].(string), - Type: changed_item_map["type"].(string), - Values: resourceValues[changed_item_map["address"].(string)], - } - changing_items_tf[changed_item.Address] = changed_item - } + awsMappings := datamaps.AwssourceData[resourceChange.Type] + k8sMappings := datamaps.K8ssourceData[resourceChange.Type] - var changing_items []*sdp.Query - // for all managed resources: - for _, r := range changing_items_tf { - mappings, ok := datamaps.AwssourceData[r.Type] - if !ok { - log.WithContext(ctx).WithFields(lf).WithField("terraform-address", r.Address).Warn("skipping unmapped resource") + mappings := append(awsMappings, k8sMappings...) + + if len(mappings) == 0 { + log.WithContext(ctx).WithFields(lf).WithField("terraform-address", resourceChange.Address).Warn("skipping unmapped resource") continue } + var currentResource *Resource for _, mapData := range mappings { - queryStr, ok := r.Values[mapData.QueryField] + currentResource = plan.PlannedValues.RootModule.DigResource(resourceChange.Address) + if currentResource == nil { + log.WithContext(ctx). + WithFields(lf). + WithField("terraform-address", resourceChange.Address). + WithField("terraform-query-field", mapData.QueryField).Warn("skipping resource without values") + continue + } + + query, ok := currentResource.AttributeValues.Dig(mapData.QueryField) if !ok { log.WithContext(ctx). WithFields(lf). - WithField("terraform-address", r.Address). + WithField("terraform-address", resourceChange.Address). WithField("terraform-query-field", mapData.QueryField).Warn("skipping resource without query field") continue } + // Create the map that variables will pull data from + dataMap := make(map[string]interface{}) + + // Populate resource values + dataMap["values"] = currentResource.AttributeValues + + if overmindMappings, ok := plan.PlannedValues.Outputs["overmind_mappings"]; ok { + // TODO: Check for provider mappings + // + // This will need to follow the logic form the readme. We now have + // the entire plan parsed in a typesafe manner so it shouldn't be + // terribly hard. We just need to map from the changing resource to + // the provider, which probably should be its own function. Once we + // have that we can check the outputs for mappings + + configResource := plan.Config.RootModule.DigResource(resourceChange.Address) + + if configResource == nil { + log.WithContext(ctx). + WithFields(lf). + WithField("terraform-address", resourceChange.Address). + Warn("skipping resource without config") + } else { + // Look up the provider config key in the mappings + mappings := make(map[string]map[string]string) + + err = json.Unmarshal(overmindMappings.Value, &mappings) + + if err != nil { + log.WithContext(ctx). + WithFields(lf). + WithField("terraform-address", resourceChange.Address). + WithError(err). + Error("failed to parse overmind_mappings output") + } else { + currentProviderMappings, ok := mappings[configResource.ProviderConfigKey] + + if ok { + log.WithContext(ctx). + WithFields(lf). + WithField("terraform-address", resourceChange.Address). + WithField("provider-config-key", configResource.ProviderConfigKey). + Debug("found provider mappings") + + // We have mappings for this provider, so set them + // in the `provider_mapping` value + dataMap["provider_mapping"] = currentProviderMappings + } + } + } + } + + // Interpolate variables in the scope + scope, err := InterpolateScope(mapData.Scope, dataMap) + + if err != nil { + log.WithContext(ctx).WithError(err).Infof("could not find scope mapping variables %v, adding them will result in better results. Error: ", mapData.Scope) + scope = "*" + } + u := uuid.New() changing_items = append(changing_items, &sdp.Query{ Type: mapData.Type, Method: mapData.Method, - Query: queryStr.(string), - Scope: mapData.Scope, + Query: query.(string), + Scope: scope, RecursionBehaviour: &sdp.Query_RecursionBehaviour{}, UUID: u[:], }) @@ -124,24 +174,6 @@ func changingItemQueriesFromPlan(ctx context.Context, planJSON []byte, lf log.Fi return changing_items, nil } -func resourceValuesFromModule(module map[string]any, result *map[string]map[string]any) { - resource_data, ok := module["resources"] - if ok { - for _, r := range resource_data.([]any) { - resource := r.(map[string]any) - (*result)[resource["address"].(string)] = resource["values"].(map[string]any) - } - } - - child_modules_data, ok := module["child_modules"] - if ok { - for _, cm := range child_modules_data.([]any) { - child_module := cm.(map[string]any) - resourceValuesFromModule(child_module, result) - } - } -} - func SubmitPlan(signals chan os.Signal, ready chan bool) int { timeout, err := time.ParseDuration(viper.GetString("timeout")) if err != nil { diff --git a/cmd/submitplan_test.go b/cmd/submitplan_test.go index a1d51139..54a07f0f 100644 --- a/cmd/submitplan_test.go +++ b/cmd/submitplan_test.go @@ -22,19 +22,31 @@ func TestChangingItemQueriesFromPlan(t *testing.T) { t.Error(err) } - if len(queries) != 1 { + if len(queries) != 2 { t.Errorf("Expected 1 queries, got %v", len(queries)) } - if queries[0].Type != "iam-policy" { - t.Errorf("Expected query type to be iam-policy, got %v", queries[0].Type) + if queries[0].Type != "Deployment" { + t.Errorf("Expected query type to be Deployment, got %v", queries[0].Type) } - if queries[0].Query != "arn:aws:iam::123456789012:policy/test-alb-ingress" { - t.Errorf("Expected query to be arn:aws:iam::123456789012:policy/test-alb-ingress, got %v", queries[0].Query) + if queries[0].Query != "api-server" { + t.Errorf("Expected query to be api-server, got %v", queries[0].Query) } - if queries[0].Scope != "*" { - t.Errorf("Expected query scope to be *, got %v", queries[0].Scope) + if queries[0].Scope != "dogfood.default" { + t.Errorf("Expected query scope to be dogfood.default, got %v", queries[0].Scope) + } + + if queries[1].Type != "iam-policy" { + t.Errorf("Expected query type to be iam-policy, got %v", queries[1].Type) + } + + if queries[1].Query != "arn:aws:iam::123456789012:policy/test-alb-ingress" { + t.Errorf("Expected query to be arn:aws:iam::123456789012:policy/test-alb-ingress, got %v", queries[1].Query) + } + + if queries[1].Scope != "*" { + t.Errorf("Expected query scope to be *, got %v", queries[1].Scope) } } diff --git a/cmd/testdata/plan.json b/cmd/testdata/plan.json index ee86ab67..911621dc 100644 --- a/cmd/testdata/plan.json +++ b/cmd/testdata/plan.json @@ -2,6 +2,544 @@ "planned_values": { "root_module": { "resources": [ + { + "address": "kubernetes_deployment.api_server", + "mode": "managed", + "type": "kubernetes_deployment", + "name": "api_server", + "provider_name": "registry.terraform.io/hashicorp/kubernetes", + "schema_version": 1, + "values": { + "id": "default/api-server", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 18, + "labels": {}, + "name": "api-server", + "namespace": "default", + "resource_version": "16505436", + "uid": "cd11a255-2964-434a-b366-063ea673bbd2" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "paused": false, + "progress_deadline_seconds": 600, + "replicas": "1", + "revision_history_limit": 10, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "api-server" + } + } + ], + "strategy": [ + { + "rolling_update": [ + { + "max_surge": "25%", + "max_unavailable": "25%" + } + ], + "type": "RollingUpdate" + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "api-server" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": [], + "command": [], + "env": [ + ], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:d10d15d0bce640a7e4e505b57652d7a7e46f8092a3dbd64408de4368cda40270", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "api-server", + "port": [ + { + "container_port": 8080, + "host_ip": "", + "host_port": 0, + "name": "", + "protocol": "TCP" + } + ], + "readiness_probe": [], + "resources": [ + { + "limits": { + "memory": "2Gi" + }, + "requests": { + "cpu": "250m", + "memory": "200Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [ + { + "name": "srcman-registry-credentials" + } + ], + "init_container": [ + { + "args": [], + "command": [ + "/bin/mkdir", + "-p", + "/nats-nkeys/nsc" + ], + "env": [], + "env_from": [], + "image": "alpine:latest", + "image_pull_policy": "Always", + "lifecycle": [], + "liveness_probe": [], + "name": "create-folder", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + }, + { + "args": [ + "init", + "--nsc-location", + "/nats-nkeys/nsc", + "--nsc-operator", + "dogfood", + "--revlink-account", + "revlink" + ], + "command": [], + "env": [], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:d10d15d0bce640a7e4e505b57652d7a7e46f8092a3dbd64408de4368cda40270", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "generate-nkeys", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "node_name": "", + "node_selector": {}, + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "default-scheduler", + "security_context": [], + "service_account_name": "api-server-service-account", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "name": "nats-nkeys", + "nfs": [], + "persistent_volume_claim": [ + { + "claim_name": "nats-nkeys", + "read_only": false + } + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": { + "create": "2m", + "delete": "2m", + "update": "2m" + }, + "wait_for_rollout": true + }, + "sensitive_values": { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "selector": [ + { + "match_expressions": [], + "match_labels": {} + } + ], + "strategy": [ + { + "rolling_update": [ + {} + ] + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "affinity": [], + "container": [ + { + "args": [], + "command": [], + "env": [ + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [] + } + ], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [ + {} + ], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "dns_config": [], + "host_aliases": [], + "image_pull_secrets": [ + {} + ], + "init_container": [ + { + "args": [], + "command": [ + false, + false, + false + ], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + }, + { + "args": [ + false, + false, + false, + false, + false, + false, + false + ], + "command": [], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "node_selector": {}, + "readiness_gate": [], + "security_context": [], + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "nfs": [], + "persistent_volume_claim": [ + {} + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": {} + } + }, { "address": "module.eks_elb_controller.aws_iam_policy.lb_controller[0]", "mode": "managed", @@ -65,8 +603,8 @@ "name": "test-cluster-ClusterEncryption2023061613390591120000000e", "name_prefix": "test-cluster-ClusterEncryption", "path": "/", - "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:944651592624:key/e10b7c35-f5ec-48ce-8699-81497e5c2f57\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYA4TZO6GN3", + "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:12345678901:key/1234567\"}],\"Version\":\"2012-10-17\"}", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -796,7 +1334,7 @@ "name_prefix": "AmazonEKS_EFS_CSI_Policy-", "path": "/", "policy": "{\"Statement\":[{\"Action\":[\"elasticfilesystem:DescribeMountTargets\",\"elasticfilesystem:DescribeFileSystems\",\"elasticfilesystem:DescribeAccessPoints\",\"ec2:DescribeAvailabilityZones\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:CreateAccessPoint\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:TagResource\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:DeleteAccessPoint\",\"Condition\":{\"StringEquals\":{\"aws:ResourceTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYJS4FPQGBX", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -815,7 +1353,7 @@ "schema_version": 0, "values": { "arn": "arn:aws:iam::123456789012:role/efs-csi", - "assume_role_policy": "{\"Statement\":[{\"Action\":\"sts:AssumeRoleWithWebIdentity\",\"Condition\":{\"StringEquals\":{\"oidc.eks.eu-west-2.amazonaws.com/id/F41E26F5371FABBCFA5F3129C57D4AD8:aud\":\"sts.amazonaws.com\",\"oidc.eks.eu-west-2.amazonaws.com/id/F41E26F5371FABBCFA5F3129C57D4AD8:sub\":[\"system:serviceaccount:kube-system:efs-csi-controller-sa\",\"system:serviceaccount:kube-system:efs-csi-node-sa\"]}},\"Effect\":\"Allow\",\"Principal\":{\"Federated\":\"arn:aws:iam::123456789012:oidc-provider/oidc.eks.eu-west-2.amazonaws.com/id/F41E26F5371FABBCFA5F3129C57D4AD8\"}}],\"Version\":\"2012-10-17\"}", + "assume_role_policy": "{\"Statement\":[{\"Action\":\"sts:AssumeRoleWithWebIdentity\",\"Condition\":{\"StringEquals\":{\"foobar:aud\":\"sts.amazonaws.com\",\"foobar:sub\":[\"system:serviceaccount:kube-system:efs-csi-controller-sa\",\"system:serviceaccount:kube-system:efs-csi-node-sa\"]}},\"Effect\":\"Allow\",\"Principal\":{\"Federated\":\"arn:aws:iam::123456789012:oidc-provider/foobar\"}}],\"Version\":\"2012-10-17\"}", "create_date": "2023-03-17T13:43:01Z", "description": "", "force_detach_policies": true, @@ -1635,7 +2173,7 @@ "values": [ "system:serviceaccount:kube-system:aws-alb-ingress-controller" ], - "variable": "oidc.eks.eu-west-2.amazonaws.com/id/F41E26F5371FABBCFA5F3129C57D4AD8:sub" + "variable": "foobar:sub" } ], "effect": "Allow", @@ -1645,7 +2183,7 @@ "principals": [ { "identifiers": [ - "arn:aws:iam::123456789012:oidc-provider/oidc.eks.eu-west-2.amazonaws.com/id/F41E26F5371FABBCFA5F3129C57D4AD8" + "arn:aws:iam::123456789012:oidc-provider/foobar" ], "type": "Federated" } @@ -1795,9 +2333,1103 @@ "address": "module.eks_elb_controller" } ] + }, + "outputs": { + "overmind_mappings": { + "sensitive": false, + "type": [ + "object", + { + "kubernetes": [ + "object", + { + "cluster_name": "string" + } + ] + } + ], + "value": { + "kubernetes": { + "cluster_name": "dogfood" + } + } + } } }, "resource_changes": [ + { + "address": "kubernetes_deployment.api_server", + "mode": "managed", + "type": "kubernetes_deployment", + "name": "api_server", + "provider_name": "registry.terraform.io/hashicorp/kubernetes", + "change": { + "actions": [ + "update" + ], + "before": { + "id": "default/api-server", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 18, + "labels": {}, + "name": "api-server", + "namespace": "default", + "resource_version": "16505436", + "uid": "cd11a255-2964-434a-b366-063ea673bbd2" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "paused": false, + "progress_deadline_seconds": 600, + "replicas": "1", + "revision_history_limit": 10, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "api-server" + } + } + ], + "strategy": [ + { + "rolling_update": [ + { + "max_surge": "25%", + "max_unavailable": "25%" + } + ], + "type": "RollingUpdate" + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "api-server" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": [], + "command": [], + "env": [ + ], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:41be6bab8dc65bf19fe3771fa9cf54e51621d93161056db8091ca2ff905be24a", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "api-server", + "port": [ + { + "container_port": 8080, + "host_ip": "", + "host_port": 0, + "name": "", + "protocol": "TCP" + } + ], + "readiness_probe": [], + "resources": [ + { + "limits": { + "memory": "2Gi" + }, + "requests": { + "cpu": "250m", + "memory": "200Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [ + { + "name": "srcman-registry-credentials" + } + ], + "init_container": [ + { + "args": [], + "command": [ + "/bin/mkdir", + "-p", + "/nats-nkeys/nsc" + ], + "env": [], + "env_from": [], + "image": "alpine:latest", + "image_pull_policy": "Always", + "lifecycle": [], + "liveness_probe": [], + "name": "create-folder", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + }, + { + "args": [ + "init", + "--nsc-location", + "/nats-nkeys/nsc", + "--nsc-operator", + "dogfood", + "--revlink-account", + "revlink" + ], + "command": [], + "env": [], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:41be6bab8dc65bf19fe3771fa9cf54e51621d93161056db8091ca2ff905be24a", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "generate-nkeys", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "node_name": "", + "node_selector": {}, + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "default-scheduler", + "security_context": [], + "service_account_name": "api-server-service-account", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "name": "nats-nkeys", + "nfs": [], + "persistent_volume_claim": [ + { + "claim_name": "nats-nkeys", + "read_only": false + } + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": { + "create": "2m", + "delete": "2m", + "update": "2m" + }, + "wait_for_rollout": true + }, + "after": { + "id": "default/api-server", + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 18, + "labels": {}, + "name": "api-server", + "namespace": "default", + "resource_version": "16505436", + "uid": "cd11a255-2964-434a-b366-063ea673bbd2" + } + ], + "spec": [ + { + "min_ready_seconds": 0, + "paused": false, + "progress_deadline_seconds": 600, + "replicas": "1", + "revision_history_limit": 10, + "selector": [ + { + "match_expressions": [], + "match_labels": { + "app": "api-server" + } + } + ], + "strategy": [ + { + "rolling_update": [ + { + "max_surge": "25%", + "max_unavailable": "25%" + } + ], + "type": "RollingUpdate" + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "generate_name": "", + "generation": 0, + "labels": { + "app": "api-server" + }, + "name": "", + "namespace": "", + "resource_version": "", + "uid": "" + } + ], + "spec": [ + { + "active_deadline_seconds": 0, + "affinity": [], + "automount_service_account_token": true, + "container": [ + { + "args": [], + "command": [], + "env": [ + ], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:d10d15d0bce640a7e4e505b57652d7a7e46f8092a3dbd64408de4368cda40270", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "api-server", + "port": [ + { + "container_port": 8080, + "host_ip": "", + "host_port": 0, + "name": "", + "protocol": "TCP" + } + ], + "readiness_probe": [], + "resources": [ + { + "limits": { + "memory": "2Gi" + }, + "requests": { + "cpu": "250m", + "memory": "200Mi" + } + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "dns_config": [], + "dns_policy": "ClusterFirst", + "enable_service_links": true, + "host_aliases": [], + "host_ipc": false, + "host_network": false, + "host_pid": false, + "hostname": "", + "image_pull_secrets": [ + { + "name": "srcman-registry-credentials" + } + ], + "init_container": [ + { + "args": [], + "command": [ + "/bin/mkdir", + "-p", + "/nats-nkeys/nsc" + ], + "env": [], + "env_from": [], + "image": "alpine:latest", + "image_pull_policy": "Always", + "lifecycle": [], + "liveness_probe": [], + "name": "create-folder", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + }, + { + "args": [ + "init", + "--nsc-location", + "/nats-nkeys/nsc", + "--nsc-operator", + "dogfood", + "--revlink-account", + "revlink" + ], + "command": [], + "env": [], + "env_from": [], + "image": "ghcr.io/overmindtech/api-server@sha256:d10d15d0bce640a7e4e505b57652d7a7e46f8092a3dbd64408de4368cda40270", + "image_pull_policy": "IfNotPresent", + "lifecycle": [], + "liveness_probe": [], + "name": "generate-nkeys", + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "stdin": false, + "stdin_once": false, + "termination_message_path": "/dev/termination-log", + "termination_message_policy": "File", + "tty": false, + "volume_mount": [ + { + "mount_path": "/nats-nkeys", + "mount_propagation": "None", + "name": "nats-nkeys", + "read_only": false, + "sub_path": "" + } + ], + "working_dir": "" + } + ], + "node_name": "", + "node_selector": {}, + "priority_class_name": "", + "readiness_gate": [], + "restart_policy": "Always", + "runtime_class_name": "", + "scheduler_name": "default-scheduler", + "security_context": [], + "service_account_name": "api-server-service-account", + "share_process_namespace": false, + "subdomain": "", + "termination_grace_period_seconds": 30, + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "name": "nats-nkeys", + "nfs": [], + "persistent_volume_claim": [ + { + "claim_name": "nats-nkeys", + "read_only": false + } + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": { + "create": "2m", + "delete": "2m", + "update": "2m" + }, + "wait_for_rollout": true + }, + "after_unknown": {}, + "before_sensitive": { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "selector": [ + { + "match_expressions": [], + "match_labels": {} + } + ], + "strategy": [ + { + "rolling_update": [ + {} + ] + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "affinity": [], + "container": [ + { + "args": [], + "command": [], + "env": [ + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [] + } + ], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [ + {} + ], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "dns_config": [], + "host_aliases": [], + "image_pull_secrets": [ + {} + ], + "init_container": [ + { + "args": [], + "command": [ + false, + false, + false + ], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + }, + { + "args": [ + false, + false, + false, + false, + false, + false, + false + ], + "command": [], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "node_selector": {}, + "readiness_gate": [], + "security_context": [], + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "nfs": [], + "persistent_volume_claim": [ + {} + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": {} + }, + "after_sensitive": { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "selector": [ + { + "match_expressions": [], + "match_labels": {} + } + ], + "strategy": [ + { + "rolling_update": [ + {} + ] + } + ], + "template": [ + { + "metadata": [ + { + "annotations": {}, + "labels": {} + } + ], + "spec": [ + { + "affinity": [], + "container": [ + { + "args": [], + "command": [], + "env": [ + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value": true, + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [ + { + "config_map_key_ref": [], + "field_ref": [], + "resource_field_ref": [], + "secret_key_ref": [ + {} + ] + } + ] + }, + { + "value_from": [] + } + ], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [ + {} + ], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "dns_config": [], + "host_aliases": [], + "image_pull_secrets": [ + {} + ], + "init_container": [ + { + "args": [], + "command": [ + false, + false, + false + ], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + }, + { + "args": [ + false, + false, + false, + false, + false, + false, + false + ], + "command": [], + "env": [], + "env_from": [], + "lifecycle": [], + "liveness_probe": [], + "port": [], + "readiness_probe": [], + "resources": [ + { + "limits": {}, + "requests": {} + } + ], + "security_context": [], + "startup_probe": [], + "volume_mount": [ + {} + ] + } + ], + "node_selector": {}, + "readiness_gate": [], + "security_context": [], + "toleration": [], + "topology_spread_constraint": [], + "volume": [ + { + "aws_elastic_block_store": [], + "azure_disk": [], + "azure_file": [], + "ceph_fs": [], + "cinder": [], + "config_map": [], + "csi": [], + "downward_api": [], + "empty_dir": [], + "fc": [], + "flex_volume": [], + "flocker": [], + "gce_persistent_disk": [], + "git_repo": [], + "glusterfs": [], + "host_path": [], + "iscsi": [], + "local": [], + "nfs": [], + "persistent_volume_claim": [ + {} + ], + "photon_persistent_disk": [], + "projected": [], + "quobyte": [], + "rbd": [], + "secret": [], + "vsphere_volume": [] + } + ] + } + ] + } + ] + } + ], + "timeouts": {} + } + } + }, { "address": "aws_iam_policy.auth0_ses_send_emails", "mode": "managed", @@ -1863,7 +3495,7 @@ "name_prefix": "AmazonEKS_EFS_CSI_Policy-", "path": "/", "policy": "{\"Statement\":[{\"Action\":[\"elasticfilesystem:DescribeMountTargets\",\"elasticfilesystem:DescribeFileSystems\",\"elasticfilesystem:DescribeAccessPoints\",\"ec2:DescribeAvailabilityZones\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:CreateAccessPoint\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:TagResource\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:DeleteAccessPoint\",\"Condition\":{\"StringEquals\":{\"aws:ResourceTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYJS4FPQGBX", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -1875,7 +3507,7 @@ "name_prefix": "AmazonEKS_EFS_CSI_Policy-", "path": "/", "policy": "{\"Statement\":[{\"Action\":[\"elasticfilesystem:DescribeMountTargets\",\"elasticfilesystem:DescribeFileSystems\",\"elasticfilesystem:DescribeAccessPoints\",\"ec2:DescribeAvailabilityZones\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:CreateAccessPoint\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:TagResource\",\"Condition\":{\"StringLike\":{\"aws:RequestTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"},{\"Action\":\"elasticfilesystem:DeleteAccessPoint\",\"Condition\":{\"StringEquals\":{\"aws:ResourceTag/efs.csi.aws.com/cluster\":\"true\"}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYJS4FPQGBX", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -1909,8 +3541,8 @@ "name": "test-cluster-ClusterEncryption2023061613390591120000000e", "name_prefix": "test-cluster-ClusterEncryption", "path": "/", - "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:944651592624:key/e10b7c35-f5ec-48ce-8699-81497e5c2f57\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYA4TZO6GN3", + "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:12345678901:key/1234567\"}],\"Version\":\"2012-10-17\"}", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -1921,8 +3553,8 @@ "name": "test-cluster-ClusterEncryption2023061613390591120000000e", "name_prefix": "test-cluster-ClusterEncryption", "path": "/", - "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:944651592624:key/e10b7c35-f5ec-48ce-8699-81497e5c2f57\"}],\"Version\":\"2012-10-17\"}", - "policy_id": "ANPA5X4M7MOYA4TZO6GN3", + "policy": "{\"Statement\":[{\"Action\":[\"kms:Encrypt\",\"kms:Decrypt\",\"kms:ListGrants\",\"kms:DescribeKey\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:kms:eu-west-2:12345678901:key/1234567\"}],\"Version\":\"2012-10-17\"}", + "policy_id": "foobar", "tags": {}, "tags_all": {} }, @@ -3011,5 +4643,1329 @@ } } ], + "configuration": { + "root_module": { + "resources": [ + { + "address": "kubernetes_deployment.api_server", + "mode": "managed", + "type": "kubernetes_deployment", + "name": "api_server", + "provider_config_key": "kubernetes", + "expressions": { + "metadata": [ + { + "name": { + "constant_value": "api-server" + } + } + ], + "spec": [ + { + "replicas": { + "constant_value": 1 + }, + "selector": [ + { + "match_labels": { + "constant_value": { + "app": "api-server" + } + } + } + ], + "template": [ + { + "metadata": [ + { + "labels": { + "constant_value": { + "app": "api-server" + } + } + } + ], + "spec": [ + { + "container": [ + { + "env": [ + { + "name": { + "constant_value": "LOG" + }, + "value": { + "references": [ + "local.default_log_level" + ] + } + }, + { + "name": { + "constant_value": "NSC_LOCATION" + }, + "value": { + "constant_value": "/nats-nkeys/nsc" + } + }, + { + "name": { + "constant_value": "NSC_OPERATOR" + }, + "value": { + "references": [ + "var.terraform_env_name" + ] + } + }, + { + "name": { + "constant_value": "NATS_URL" + }, + "value": { + "references": [ + "kubernetes_service.nats.metadata[0].name", + "kubernetes_service.nats.metadata[0]", + "kubernetes_service.nats.metadata", + "kubernetes_service.nats" + ] + } + }, + { + "name": { + "constant_value": "AUTH0_AUDIENCE" + }, + "value": { + "references": [ + "auth0_resource_server.api_server.identifier", + "auth0_resource_server.api_server" + ] + } + }, + { + "name": { + "constant_value": "AUTH0_DOMAIN" + }, + "value": { + "references": [ + "var.auth0_domain" + ] + } + }, + { + "name": { + "constant_value": "AUTH_COOKIE_NAME" + }, + "value": { + "references": [ + "local.session_name" + ] + } + }, + { + "name": { + "constant_value": "CORS_ALLOW_ORIGINS" + }, + "value": { + "references": [ + "local.cors_origin" + ] + } + }, + { + "name": { + "constant_value": "REVLINK_ACCOUNT" + }, + "value": { + "constant_value": "revlink" + } + }, + { + "name": { + "constant_value": "API_KEY_CLIENT_ID" + }, + "value": { + "references": [ + "auth0_client.api_keys.client_id", + "auth0_client.api_keys" + ] + } + }, + { + "name": { + "constant_value": "API_KEY_CLIENT_SECRET" + }, + "value": { + "references": [ + "auth0_client.api_keys.client_secret", + "auth0_client.api_keys" + ] + } + }, + { + "name": { + "constant_value": "GATEWAY_URL" + }, + "value": { + "constant_value": "http://gateway:8080/api/gateway" + } + }, + { + "name": { + "constant_value": "BOOKMARKS_BASE_URL" + }, + "value": { + "constant_value": "http://gateway:8080/" + } + }, + { + "name": { + "constant_value": "SNAPSHOTS_BASE_URL" + }, + "value": { + "constant_value": "http://gateway:8080/" + } + }, + { + "name": { + "constant_value": "SOURCE_MANAGER" + }, + "value": { + "references": [ + "var.terraform_env_name" + ] + } + }, + { + "name": { + "constant_value": "HONEYCOMB_API_KEY" + }, + "value": { + "references": [ + "var.honeycomb_api_key" + ] + } + }, + { + "name": { + "constant_value": "SENTRY_DSN" + }, + "value": { + "references": [ + "var.backend_sentry_dsn" + ] + } + }, + { + "name": { + "constant_value": "RUN_MODE" + }, + "value": { + "references": [ + "var.run_mode" + ] + } + }, + { + "name": { + "constant_value": "PGHOST" + }, + "value": { + "references": [ + "local.overmind_db_endpoint" + ] + } + }, + { + "name": { + "constant_value": "PGPORT" + }, + "value": { + "references": [ + "local.overmind_db_port" + ] + } + }, + { + "name": { + "constant_value": "PGUSER" + }, + "value_from": [ + { + "secret_key_ref": [ + { + "key": { + "constant_value": "username" + }, + "name": { + "constant_value": "apiserverdb-root-creds" + } + } + ] + } + ] + }, + { + "name": { + "constant_value": "PGPASSWORD" + }, + "value_from": [ + { + "secret_key_ref": [ + { + "key": { + "constant_value": "password" + }, + "name": { + "constant_value": "apiserverdb-root-creds" + } + } + ] + } + ] + }, + { + "name": { + "constant_value": "PGDBNAME" + }, + "value": { + "references": [ + "local.apiserverdb_name" + ] + } + } + ], + "image": { + "references": [ + "local.api_server_imageref" + ] + }, + "image_pull_policy": { + "references": [ + "local.api_server_image_pull_policy" + ] + }, + "name": { + "constant_value": "api-server" + }, + "port": [ + { + "container_port": { + "constant_value": 8080 + } + } + ], + "resources": [ + { + "limits": { + "constant_value": { + "memory": "2Gi" + } + }, + "requests": { + "constant_value": { + "cpu": "250m", + "memory": "200Mi" + } + } + } + ], + "volume_mount": [ + { + "mount_path": { + "constant_value": "/nats-nkeys" + }, + "name": { + "constant_value": "nats-nkeys" + } + } + ] + } + ], + "image_pull_secrets": [ + { + "name": { + "constant_value": "srcman-registry-credentials" + } + } + ], + "init_container": [ + { + "command": { + "constant_value": [ + "/bin/mkdir", + "-p", + "/nats-nkeys/nsc" + ] + }, + "image": { + "constant_value": "alpine:latest" + }, + "name": { + "constant_value": "create-folder" + }, + "volume_mount": [ + { + "mount_path": { + "constant_value": "/nats-nkeys" + }, + "name": { + "constant_value": "nats-nkeys" + } + } + ] + }, + { + "args": { + "references": [ + "var.terraform_env_name" + ] + }, + "image": { + "references": [ + "local.api_server_imageref" + ] + }, + "image_pull_policy": { + "references": [ + "local.api_server_image_pull_policy" + ] + }, + "name": { + "constant_value": "generate-nkeys" + }, + "volume_mount": [ + { + "mount_path": { + "constant_value": "/nats-nkeys" + }, + "name": { + "constant_value": "nats-nkeys" + } + } + ] + } + ], + "service_account_name": { + "constant_value": "api-server-service-account" + }, + "volume": [ + { + "name": { + "constant_value": "nats-nkeys" + }, + "persistent_volume_claim": [ + { + "claim_name": { + "constant_value": "nats-nkeys" + } + } + ] + } + ] + } + ] + } + ] + } + ], + "timeouts": { + "create": { + "constant_value": "2m" + }, + "delete": { + "constant_value": "2m" + }, + "update": { + "constant_value": "2m" + } + } + }, + "schema_version": 1, + "depends_on": [ + "module.eks", + "module.api_server_efs", + "postgresql_database.apiserverdb", + "postgresql_role.apiserverdb_role" + ] + } + ], + "module_calls": { + "eks_elb_controller": { + "source": "DNXLabs/eks-lb-controller/aws", + "expressions": { + "cluster_identity_oidc_issuer": { + "references": [ + "module.eks.cluster_oidc_issuer_url", + "module.eks" + ] + }, + "cluster_identity_oidc_issuer_arn": { + "references": [ + "module.eks.oidc_provider_arn", + "module.eks" + ] + }, + "cluster_name": { + "references": [ + "module.eks.cluster_name", + "module.eks" + ] + } + }, + "module": { + "resources": [ + { + "address": "aws_iam_policy.lb_controller", + "mode": "managed", + "type": "aws_iam_policy", + "name": "lb_controller", + "provider_config_key": "aws", + "expressions": { + "description": { + "constant_value": "Policy for alb-ingress service" + }, + "name": { + "references": [ + "var.cluster_name" + ] + }, + "path": { + "constant_value": "/" + }, + "policy": { + "references": [ + "data.aws_iam_policy_document.lb_controller[0].json", + "data.aws_iam_policy_document.lb_controller[0]", + "data.aws_iam_policy_document.lb_controller" + ] + } + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled" + ] + }, + "depends_on": [ + "var.mod_dependency" + ] + }, + { + "address": "aws_iam_role.lb_controller", + "mode": "managed", + "type": "aws_iam_role", + "name": "lb_controller", + "provider_config_key": "aws", + "expressions": { + "assume_role_policy": { + "references": [ + "data.aws_iam_policy_document.lb_controller_assume[0].json", + "data.aws_iam_policy_document.lb_controller_assume[0]", + "data.aws_iam_policy_document.lb_controller_assume" + ] + }, + "name": { + "references": [ + "var.cluster_name" + ] + } + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled" + ] + } + }, + { + "address": "aws_iam_role_policy_attachment.lb_controller", + "mode": "managed", + "type": "aws_iam_role_policy_attachment", + "name": "lb_controller", + "provider_config_key": "aws", + "expressions": { + "policy_arn": { + "references": [ + "aws_iam_policy.lb_controller[0].arn", + "aws_iam_policy.lb_controller[0]", + "aws_iam_policy.lb_controller" + ] + }, + "role": { + "references": [ + "aws_iam_role.lb_controller[0].name", + "aws_iam_role.lb_controller[0]", + "aws_iam_role.lb_controller" + ] + } + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled" + ] + } + }, + { + "address": "helm_release.lb_controller", + "mode": "managed", + "type": "helm_release", + "name": "lb_controller", + "provider_config_key": "helm", + "expressions": { + "chart": { + "references": [ + "var.helm_chart_release_name" + ] + }, + "name": { + "references": [ + "var.helm_chart_name" + ] + }, + "namespace": { + "references": [ + "var.namespace" + ] + }, + "repository": { + "references": [ + "var.helm_chart_repo" + ] + }, + "set": [ + { + "name": { + "constant_value": "clusterName" + }, + "value": { + "references": [ + "var.cluster_name" + ] + } + }, + { + "name": { + "constant_value": "rbac.create" + }, + "value": { + "constant_value": "true" + } + }, + { + "name": { + "constant_value": "serviceAccount.create" + }, + "value": { + "constant_value": "true" + } + }, + { + "name": { + "constant_value": "serviceAccount.name" + }, + "value": { + "references": [ + "var.service_account_name" + ] + } + }, + { + "name": { + "constant_value": "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn" + }, + "value": { + "references": [ + "aws_iam_role.lb_controller[0].arn", + "aws_iam_role.lb_controller[0]", + "aws_iam_role.lb_controller" + ] + } + } + ], + "values": { + "references": [ + "var.settings" + ] + }, + "version": { + "references": [ + "var.helm_chart_version" + ] + } + }, + "schema_version": 1, + "count_expression": { + "references": [ + "var.enabled" + ] + }, + "depends_on": [ + "var.mod_dependency", + "kubernetes_namespace.lb_controller" + ] + }, + { + "address": "kubectl_manifest.cluster_role", + "mode": "managed", + "type": "kubectl_manifest", + "name": "cluster_role", + "provider_config_key": "kubectl", + "expressions": { + "yaml_body": { + "references": [ + "path.module", + "each.value.name", + "each.value", + "each.value.namespace", + "each.value", + "each.value.secrets", + "each.value" + ] + } + }, + "schema_version": 1, + "for_each_expression": { + "references": [ + "var.roles" + ] + } + }, + { + "address": "kubectl_manifest.cluster_role_binding", + "mode": "managed", + "type": "kubectl_manifest", + "name": "cluster_role_binding", + "provider_config_key": "kubectl", + "expressions": { + "yaml_body": { + "references": [ + "path.module", + "each.value.name", + "each.value", + "each.value.namespace", + "each.value", + "each.value.secrets", + "each.value" + ] + } + }, + "schema_version": 1, + "for_each_expression": { + "references": [ + "var.roles" + ] + }, + "depends_on": [ + "kubectl_manifest.cluster_role" + ] + }, + { + "address": "kubernetes_namespace.lb_controller", + "mode": "managed", + "type": "kubernetes_namespace", + "name": "lb_controller", + "provider_config_key": "kubernetes", + "expressions": { + "metadata": [ + { + "name": { + "references": [ + "var.namespace" + ] + } + } + ] + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled", + "var.create_namespace", + "var.namespace" + ] + }, + "depends_on": [ + "var.mod_dependency" + ] + }, + { + "address": "data.aws_iam_policy_document.lb_controller", + "mode": "data", + "type": "aws_iam_policy_document", + "name": "lb_controller", + "provider_config_key": "aws", + "expressions": { + "statement": [ + { + "actions": { + "constant_value": [ + "iam:CreateServiceLinkedRole" + ] + }, + "condition": [ + { + "test": { + "constant_value": "StringEquals" + }, + "values": { + "constant_value": [ + "elasticloadbalancing.amazonaws.com" + ] + }, + "variable": { + "constant_value": "iam:AWSServiceName" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:CreateSecurityGroup" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:CreateTags" + ] + }, + "condition": [ + { + "test": { + "constant_value": "StringEquals" + }, + "values": { + "constant_value": [ + "CreateSecurityGroup" + ] + }, + "variable": { + "constant_value": "ec2:CreateAction" + } + }, + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:RequestTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "references": [ + "var.arn_format" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:CreateTags", + "ec2:DeleteTags" + ] + }, + "condition": [ + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "true" + ] + }, + "variable": { + "constant_value": "aws:RequestTag/elbv2.k8s.aws/cluster" + } + }, + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:ResourceTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "references": [ + "var.arn_format" + ] + } + }, + { + "actions": { + "constant_value": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup" + ] + }, + "condition": [ + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:ResourceTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup" + ] + }, + "condition": [ + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:RequestTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:DeleteRule" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ] + }, + "condition": [ + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "true" + ] + }, + "variable": { + "constant_value": "aws:RequestTag/elbv2.k8s.aws/cluster" + } + }, + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:ResourceTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "references": [ + "var.arn_format", + "var.arn_format", + "var.arn_format" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "references": [ + "var.arn_format", + "var.arn_format", + "var.arn_format", + "var.arn_format" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup" + ] + }, + "condition": [ + { + "test": { + "constant_value": "Null" + }, + "values": { + "constant_value": [ + "false" + ] + }, + "variable": { + "constant_value": "aws:ResourceTag/elbv2.k8s.aws/cluster" + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "references": [ + "var.arn_format" + ] + } + }, + { + "actions": { + "constant_value": [ + "elasticloadbalancing:SetWebAcl", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule" + ] + }, + "effect": { + "constant_value": "Allow" + }, + "resources": { + "constant_value": [ + "*" + ] + } + } + ] + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled" + ] + } + }, + { + "address": "data.aws_iam_policy_document.lb_controller_assume", + "mode": "data", + "type": "aws_iam_policy_document", + "name": "lb_controller_assume", + "provider_config_key": "aws", + "expressions": { + "statement": [ + { + "actions": { + "constant_value": [ + "sts:AssumeRoleWithWebIdentity" + ] + }, + "condition": [ + { + "test": { + "constant_value": "StringEquals" + }, + "values": { + "references": [ + "var.namespace", + "var.service_account_name" + ] + }, + "variable": { + "references": [ + "var.cluster_identity_oidc_issuer" + ] + } + } + ], + "effect": { + "constant_value": "Allow" + }, + "principals": [ + { + "identifiers": { + "references": [ + "var.cluster_identity_oidc_issuer_arn" + ] + }, + "type": { + "constant_value": "Federated" + } + } + ] + } + ] + }, + "schema_version": 0, + "count_expression": { + "references": [ + "var.enabled" + ] + } + } + ], + "variables": { + "arn_format": { + "default": "aws", + "description": "ARNs identifier, usefull for GovCloud begin with `aws-us-gov-`." + }, + "cluster_identity_oidc_issuer": { + "description": "The OIDC Identity issuer for the cluster." + }, + "cluster_identity_oidc_issuer_arn": { + "description": "The OIDC Identity issuer ARN for the cluster that can be used to associate IAM roles with a service account." + }, + "cluster_name": { + "description": "The name of the cluster." + }, + "create_namespace": { + "default": true, + "description": "Whether to create Kubernetes namespace with name defined by `namespace`." + }, + "enabled": { + "default": true, + "description": "Variable indicating whether deployment is enabled." + }, + "helm_chart_name": { + "default": "aws-load-balancer-controller", + "description": "AWS Load Balancer Controller Helm chart name." + }, + "helm_chart_release_name": { + "default": "aws-load-balancer-controller", + "description": "AWS Load Balancer Controller Helm chart release name." + }, + "helm_chart_repo": { + "default": "https://aws.github.io/eks-charts", + "description": "AWS Load Balancer Controller Helm repository name." + }, + "helm_chart_version": { + "default": "1.4.4", + "description": "AWS Load Balancer Controller Helm chart version." + }, + "mod_dependency": { + "default": null, + "description": "Dependence variable binds all AWS resources allocated by this module, dependent modules reference this variable." + }, + "namespace": { + "default": "kube-system", + "description": "AWS Load Balancer Controller Helm chart namespace which the service will be created." + }, + "roles": { + "default": [], + "description": "RBAC roles that give secret access in other namespaces to the lb controller" + }, + "service_account_name": { + "default": "aws-alb-ingress-controller", + "description": "The kubernetes service account name." + }, + "settings": { + "default": {}, + "description": "Additional settings which will be passed to the Helm chart values, see https://github.com/aws/eks-charts/tree/master/stable/aws-load-balancer-controller#configuration." + } + } + }, + "version_constraint": "0.7.0", + "depends_on": [ + "module.eks" + ] + } + } + } + }, "timestamp": "2023-07-17T15:48:38Z" } \ No newline at end of file diff --git a/go.mod b/go.mod index e539dbba..4bbb9e36 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,8 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2 // indirect + github.com/xiam/dig v0.0.0-20191116195832-893b5fb5093b // indirect + github.com/xiam/to v0.0.0-20191116183551-8328998fc0ed // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect diff --git a/go.sum b/go.sum index d90bbe2e..3c138002 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,6 @@ github.com/auth0/go-jwt-middleware/v2 v2.1.0 h1:VU4LsC3aFPoqXVyEp8EixU6FNM+ZNIjE github.com/auth0/go-jwt-middleware/v2 v2.1.0/go.mod h1:CpzcJoleayAACpv+vt0AP8/aYn5TDngsqzLapV1nM4c= github.com/aws/aws-sdk-go v1.44.266 h1:MWd775dcYf7NrwgcHLtlsIbWoWkX8p4vomfNHr88zH0= github.com/aws/aws-sdk-go v1.44.266/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/bufbuild/connect-go v1.8.0 h1:srluNkFkZBfSfg9Qb6DrO+5nMaxix//h2ctrHZhMGKc= -github.com/bufbuild/connect-go v1.8.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk= github.com/bufbuild/connect-go v1.9.0 h1:JIgAeNuFpo+SUPfU19Yt5TcWlznsN5Bv10/gI/6Pjoc= github.com/bufbuild/connect-go v1.9.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -234,12 +232,6 @@ github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/overmindtech/sdp-go v0.34.2 h1:JJ0Riu5ReQ1z155nd5t/0Zrox3W3zWMC01fB7pixGE4= -github.com/overmindtech/sdp-go v0.34.2/go.mod h1:LnTjr8eX6nLxbF5lW5zSC3mnoJBxdqTplz0Rc6FisrA= -github.com/overmindtech/sdp-go v0.36.0 h1:hbfs39GuVZnlUKyvqQzE3pilhZPoK0wVzOD5V30CmHI= -github.com/overmindtech/sdp-go v0.36.0/go.mod h1:LIUppm58V+JpNJsPCXiBOwlySgQ9uacsC+Hfl5D/zQo= -github.com/overmindtech/sdp-go v0.36.1 h1:c2pbYCJUwpdFbl6GPs+m1TH4TjaQgKGrggl9/cuCED8= -github.com/overmindtech/sdp-go v0.36.1/go.mod h1:LIUppm58V+JpNJsPCXiBOwlySgQ9uacsC+Hfl5D/zQo= github.com/overmindtech/sdp-go v0.36.2 h1:pIBMzuADDR1A10lZk0zQcroo2VzeNwmiYXpA/0h5Q3Q= github.com/overmindtech/sdp-go v0.36.2/go.mod h1:LIUppm58V+JpNJsPCXiBOwlySgQ9uacsC+Hfl5D/zQo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= @@ -293,6 +285,10 @@ github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.2 h1:s3quVevwQlgEMCkvs github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.2/go.mod h1:IJLBT7ItgUACTEoq6BEw/9HwHWL4BiPirYgKhZS83Lg= github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2 h1:CNznWHkrbA6o1q2H/BsH4tIHf4zbKNtndeoV+AH8z0U= github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.2/go.mod h1:7YSrHCmYPHIXjTWnKSU7EGT0TFEcm3WwSeQquwCGg38= +github.com/xiam/dig v0.0.0-20191116195832-893b5fb5093b h1:ajy6PPLDeQaf7xf4P/4Ie/wsUTEqjy3Irl+xFelmjk0= +github.com/xiam/dig v0.0.0-20191116195832-893b5fb5093b/go.mod h1:TkoiLoIgvAxmagjbnKWq18F2VlqnIcqAx/HzmFAqXNU= +github.com/xiam/to v0.0.0-20191116183551-8328998fc0ed h1:Gjnw8buhv4V8qXaHtAWPnKXNpCNx62heQpjO8lOY0/M= +github.com/xiam/to v0.0.0-20191116183551-8328998fc0ed/go.mod h1:cqbG7phSzrbdg3aj+Kn63bpVruzwDZi58CpxlZkjwzw= 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= @@ -335,8 +331,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -408,8 +402,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -422,8 +414,6 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= -golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -481,8 +471,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -498,8 +486,6 @@ 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.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/sources/k8s-source b/sources/k8s-source new file mode 120000 index 00000000..0eb6d8a8 --- /dev/null +++ b/sources/k8s-source @@ -0,0 +1 @@ +../../k8s-source/ \ No newline at end of file