fix: properly apply metadata when multiple resources share the same id (#5443)

This commit is contained in:
Colin Adler 2022-12-21 13:48:49 -05:00 committed by GitHub
parent 308a0602b6
commit ac27cf8c07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 3751 additions and 94 deletions

View File

@ -1069,12 +1069,12 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
// templateVersionResources returns the workspace agent resources associated
// with a template version. A template can specify more than one resource to be
// provisioned, each resource can have an agent that dials back to coderd.
// The agents returned are informative of the template version, and do not
// return agents associated with any particular workspace.
// provisioned, each resource can have an agent that dials back to coderd. The
// agents returned are informative of the template version, and do not return
// agents associated with any particular workspace.
func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var (
ctx = r.Context()
templateVersion = httpmw.TemplateVersionParam(r)
template = httpmw.TemplateParam(r)
)
@ -1100,8 +1100,8 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
// and not any build logs for a workspace.
// Eg: Logs returned from 'terraform plan' when uploading a new terraform file.
func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var (
ctx = r.Context()
templateVersion = httpmw.TemplateVersionParam(r)
template = httpmw.TemplateParam(r)
)

View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@ -270,7 +269,7 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
return "", ctx.Err()
}
var out bytes.Buffer
var out strings.Builder
cmd := exec.CommandContext(killCtx, e.binaryPath, "graph") // #nosec
cmd.Stdout = &out
cmd.Dir = e.workdir
@ -289,14 +288,13 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
return out.String(), nil
}
// revive:disable-next-line:flag-parameter
func (e *executor) apply(
ctx, killCtx context.Context, plan []byte, env []string, logr logSink,
) (*proto.Provision_Response, error) {
e.mut.Lock()
defer e.mut.Unlock()
planFile, err := ioutil.TempFile("", "coder-terrafrom-plan")
planFile, err := os.CreateTemp("", "coder-terrafrom-plan")
if err != nil {
return nil, xerrors.Errorf("create plan file: %w", err)
}

View File

@ -68,8 +68,8 @@ type metadataItem struct {
IsNull bool `mapstructure:"is_null"`
}
// ConvertResources consumes Terraform state and a GraphViz representation produced by
// `terraform graph` to produce resources consumable by Coder.
// ConvertResources consumes Terraform state and a GraphViz representation
// produced by `terraform graph` to produce resources consumable by Coder.
// nolint:gocyclo
func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Resource, error) {
parsedGraph, err := gographviz.ParseString(rawGraph)
@ -84,13 +84,9 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
resources := make([]*proto.Resource, 0)
resourceAgents := map[string][]*proto.Agent{}
// Indexes Terraform resources by their label and ID.
// The label is what "terraform graph" uses to reference nodes, and the ID
// is used by "coder_metadata" resources to refer to their targets. (The ID
// field is only available when reading a state file, and not when reading a
// plan file.)
// Indexes Terraform resources by their label.
// The label is what "terraform graph" uses to reference nodes.
tfResourceByLabel := map[string]*tfjson.StateResource{}
resourceLabelByID := map[string]string{}
var findTerraformResources func(mod *tfjson.StateModule)
findTerraformResources = func(mod *tfjson.StateModule) {
for _, module := range mod.ChildModules {
@ -100,14 +96,6 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
label := convertAddressToLabel(resource.Address)
// index by label
tfResourceByLabel[label] = resource
// index by ID, if it exists
id, ok := resource.AttributeValues["id"]
if ok {
idString, ok := id.(string)
if ok {
resourceLabelByID[idString] = label
}
}
}
}
findTerraformResources(module)
@ -319,58 +307,48 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
if resource.Type != "coder_metadata" {
continue
}
var attrs metadataAttributes
err = mapstructure.Decode(resource.AttributeValues, &attrs)
if err != nil {
return nil, xerrors.Errorf("decode metadata attributes: %w", err)
}
var targetLabel string
// This occurs in a plan, because there is no resource ID.
// We attempt to find the closest node, just so we can hide it from the UI.
if attrs.ResourceID == "" {
resourceLabel := convertAddressToLabel(resource.Address)
resourceLabel := convertAddressToLabel(resource.Address)
var attachedNode *gographviz.Node
for _, node := range graph.Nodes.Lookup {
// The node attributes surround the label with quotes.
if strings.Trim(node.Attrs["label"], `"`) != resourceLabel {
continue
}
attachedNode = node
break
}
if attachedNode == nil {
var attachedNode *gographviz.Node
for _, node := range graph.Nodes.Lookup {
// The node attributes surround the label with quotes.
if strings.Trim(node.Attrs["label"], `"`) != resourceLabel {
continue
}
var attachedResource *graphResource
for _, resource := range findResourcesInGraph(graph, tfResourceByLabel, attachedNode.Name, 0, false) {
if attachedResource == nil {
// Default to the first resource because we have nothing to compare!
attachedResource = resource
continue
}
if resource.Depth < attachedResource.Depth {
// There's a closer resource!
attachedResource = resource
continue
}
if resource.Depth == attachedResource.Depth && resource.Label < attachedResource.Label {
attachedResource = resource
continue
}
}
if attachedResource == nil {
continue
}
targetLabel = attachedResource.Label
attachedNode = node
break
}
if targetLabel == "" {
targetLabel = resourceLabelByID[attrs.ResourceID]
}
if targetLabel == "" {
if attachedNode == nil {
continue
}
var attachedResource *graphResource
for _, resource := range findResourcesInGraph(graph, tfResourceByLabel, attachedNode.Name, 0, false) {
if attachedResource == nil {
// Default to the first resource because we have nothing to compare!
attachedResource = resource
continue
}
if resource.Depth < attachedResource.Depth {
// There's a closer resource!
attachedResource = resource
continue
}
if resource.Depth == attachedResource.Depth && resource.Label < attachedResource.Label {
attachedResource = resource
continue
}
}
if attachedResource == nil {
continue
}
targetLabel := attachedResource.Label
resourceHidden[targetLabel] = attrs.Hide
resourceIcon[targetLabel] = attrs.Icon
@ -416,9 +394,11 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
}
// convertAddressToLabel returns the Terraform address without the count
// specifier. eg. "module.ec2_dev.ec2_instance.dev[0]" becomes "module.ec2_dev.ec2_instance.dev"
// specifier.
// eg. "module.ec2_dev.ec2_instance.dev[0]" becomes "module.ec2_dev.ec2_instance.dev"
func convertAddressToLabel(address string) string {
return strings.Split(address, "[")[0]
cut, _, _ := strings.Cut(address, "[")
return cut
}
type graphResource struct {

View File

@ -8,14 +8,14 @@ import (
"sort"
"testing"
protobuf "github.com/golang/protobuf/proto"
tfjson "github.com/hashicorp/terraform-json"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
"github.com/coder/coder/cryptorand"
"github.com/coder/coder/provisioner/terraform"
"github.com/coder/coder/provisionersdk/proto"
protobuf "github.com/golang/protobuf/proto"
)
func TestConvertResources(t *testing.T) {
@ -165,6 +165,53 @@ func TestConvertResources(t *testing.T) {
Sensitive: true,
}},
}},
// Tests that resources with the same id correctly get metadata applied
// to them.
"kubernetes-metadata": {{
Name: "coder_workspace",
Type: "kubernetes_service_account",
}, {
Name: "coder_workspace",
Type: "kubernetes_config_map",
}, {
Name: "coder_workspace",
Type: "kubernetes_role",
}, {
Name: "coder_workspace",
Type: "kubernetes_role_binding",
}, {
Name: "coder_workspace",
Type: "kubernetes_secret",
}, {
Name: "main",
Type: "kubernetes_pod",
Metadata: []*proto.Resource_Metadata{{
Key: "cpu",
Value: "1",
}, {
Key: "memory",
Value: "1Gi",
}, {
Key: "gpu",
Value: "1",
}},
Agents: []*proto.Agent{{
Name: "main",
OperatingSystem: "linux",
Architecture: "amd64",
StartupScript: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n",
Apps: []*proto.App{
{
Icon: "/icon/code.svg",
Slug: "code-server",
DisplayName: "code-server",
Url: "http://localhost:13337?folder=/home/coder",
},
},
Auth: &proto.Agent_Token{},
ConnectionTimeoutSeconds: 120,
}},
}},
} {
folderName := folderName
expected := expected
@ -210,6 +257,17 @@ func TestConvertResources(t *testing.T) {
err = json.Unmarshal(data, &resourcesMap)
require.NoError(t, err)
slices.SortFunc(expectedNoMetadataMap, func(a, b map[string]interface{}) bool {
//nolint:forcetypeassert
return a["name"].(string)+a["type"].(string) <
b["name"].(string)+b["type"].(string)
})
slices.SortFunc(resourcesMap, func(a, b map[string]interface{}) bool {
//nolint:forcetypeassert
return a["name"].(string)+a["type"].(string) <
b["name"].(string)+b["type"].(string)
})
require.Equal(t, expectedNoMetadataMap, resourcesMap)
})
@ -251,6 +309,17 @@ func TestConvertResources(t *testing.T) {
err = json.Unmarshal(data, &resourcesMap)
require.NoError(t, err)
slices.SortFunc(expectedMap, func(a, b map[string]interface{}) bool {
//nolint:forcetypeassert
return a["name"].(string)+a["type"].(string) <
b["name"].(string)+b["type"].(string)
})
slices.SortFunc(resourcesMap, func(a, b map[string]interface{}) bool {
//nolint:forcetypeassert
return a["name"].(string)+a["type"].(string) <
b["name"].(string)+b["type"].(string)
})
require.Equal(t, expectedMap, resourcesMap)
})
})

View File

@ -1,6 +1,6 @@
{
"format_version": "1.1",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"planned_values": {
"root_module": {
"resources": [

View File

@ -1,6 +1,6 @@
{
"format_version": "1.0",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"values": {
"root_module": {
"resources": [
@ -17,11 +17,11 @@
"connection_timeout": 120,
"dir": null,
"env": null,
"id": "5c92d003-112d-4eb1-8e5f-d3009aa52fcb",
"id": "411bdd93-0ea4-4376-a032-52b1fbf44ca5",
"init_script": "",
"os": "linux",
"startup_script": null,
"token": "fedbf404-c42d-4360-815b-5ffc34198df3",
"token": "eeac85aa-19f9-4a50-8002-dfd11556081b",
"troubleshooting_url": null
},
"sensitive_values": {}
@ -46,7 +46,7 @@
"outputs": {
"script": ""
},
"random": "5577006791947779410"
"random": "5816533441722838433"
},
"sensitive_values": {
"inputs": {},
@ -61,7 +61,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "8674665223082153551",
"id": "5594550025354402054",
"triggers": null
},
"sensitive_values": {},

View File

@ -1,6 +1,6 @@
{
"format_version": "1.1",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"planned_values": {
"root_module": {
"resources": [

View File

@ -1,6 +1,6 @@
{
"format_version": "1.0",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"values": {
"root_module": {
"resources": [
@ -17,11 +17,11 @@
"connection_timeout": 120,
"dir": null,
"env": null,
"id": "6cc2be0d-fe90-4256-944f-482787433587",
"id": "4dc52ff5-b270-47a2-8b6a-695b4872f07b",
"init_script": "",
"os": "linux",
"startup_script": null,
"token": "1927809c-5fcf-4fdd-94d7-9a619fb86d13",
"token": "c5c8378e-66df-4f3f-94a2-84bff1dc6fc9",
"troubleshooting_url": null
},
"sensitive_values": {}
@ -34,7 +34,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "8674665223082153551",
"id": "7372487656283423086",
"triggers": null
},
"sensitive_values": {},
@ -51,7 +51,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "5577006791947779410",
"id": "2553224683756509362",
"triggers": null
},
"sensitive_values": {},

View File

@ -1,6 +1,6 @@
{
"format_version": "1.1",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"planned_values": {
"root_module": {
"resources": [

View File

@ -1,6 +1,6 @@
{
"format_version": "1.0",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"values": {
"root_module": {
"resources": [
@ -17,11 +17,11 @@
"connection_timeout": 120,
"dir": null,
"env": null,
"id": "bcaf2577-5dfd-4083-a446-789092a7babe",
"id": "3cd9cbba-31f7-482c-a8a0-bf39dfe42dc2",
"init_script": "",
"os": "linux",
"startup_script": null,
"token": "862867af-cf08-4aea-a2af-70d0014f848b",
"token": "8b063f22-9e66-4dbf-9f13-7b09ac2a470f",
"troubleshooting_url": null
},
"sensitive_values": {}
@ -34,7 +34,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "8674665223082153551",
"id": "3370347998754925285",
"triggers": null
},
"sensitive_values": {},
@ -50,7 +50,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "5577006791947779410",
"id": "4707694957868093590",
"triggers": null
},
"sensitive_values": {},

View File

@ -6,6 +6,12 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
for d in */; do
pushd "$d"
name=$(basename "$(pwd)")
# This needs care to update correctly.
if [[ $name == "kubernetes-metadata" ]]; then
continue
fi
terraform init -upgrade
terraform plan -out terraform.tfplan
terraform show -json ./terraform.tfplan | jq >"$name".tfplan.json

View File

@ -1,6 +1,6 @@
{
"format_version": "1.1",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"planned_values": {
"root_module": {
"resources": [

View File

@ -1,6 +1,6 @@
{
"format_version": "1.0",
"terraform_version": "1.3.3",
"terraform_version": "1.3.6",
"values": {
"root_module": {
"resources": [
@ -17,11 +17,11 @@
"connection_timeout": 120,
"dir": null,
"env": null,
"id": "30431432-7afb-4d73-8eeb-ee464a28e157",
"id": "36189f12-6eed-4094-9179-6584a8659219",
"init_script": "",
"os": "linux",
"startup_script": null,
"token": "3ce9bbd8-0f31-4460-842b-8e9c1de9a567",
"token": "907fa482-fd3b-44be-8cfb-4515e3122e78",
"troubleshooting_url": null
},
"sensitive_values": {}
@ -34,8 +34,8 @@
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"agent_id": "30431432-7afb-4d73-8eeb-ee464a28e157",
"id": "679f9bf2-8887-4201-a5cd-e53913e8d361",
"agent_id": "36189f12-6eed-4094-9179-6584a8659219",
"id": "c9bd849e-ac37-440b-9c5b-a288344be41c",
"instance_id": "example"
},
"sensitive_values": {},
@ -51,7 +51,7 @@
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "5577006791947779410",
"id": "4399071137990404376",
"triggers": null
},
"sensitive_values": {},

View File

@ -0,0 +1,183 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "0.6.5"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.13.1"
}
google = {
source = "hashicorp/google"
version = "4.46.0"
}
}
}
data "google_client_config" "provider" {}
data "google_container_cluster" "dev-4-2" {
project = "coder-dev-1"
name = "dev-4-2"
location = "us-central1-a"
}
locals {
namespace = "colin-coder"
workspace_name = lower("coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}")
cpu = 1
memory = "1Gi"
gpu = 1
}
provider "kubernetes" {
host = "https://${data.google_container_cluster.dev-4-2.endpoint}"
token = data.google_client_config.provider.access_token
cluster_ca_certificate = base64decode(
data.google_container_cluster.dev-4-2.master_auth[0].cluster_ca_certificate,
)
}
data "coder_workspace" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
startup_script = <<EOT
#!/bin/bash
# home folder can be empty, so copying default bash settings
if [ ! -f ~/.profile ]; then
cp /etc/skel/.profile $HOME
fi
if [ ! -f ~/.bashrc ]; then
cp /etc/skel/.bashrc $HOME
fi
# install and start code-server
curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log
code-server --auth none --port 13337 | tee code-server-install.log &
EOT
}
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "code-server"
icon = "/icon/code.svg"
url = "http://localhost:13337?folder=/home/coder"
subdomain = false
}
resource "kubernetes_config_map" "coder_workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = local.workspace_name
namespace = local.namespace
}
}
resource "kubernetes_service_account" "coder_workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = local.workspace_name
namespace = local.namespace
}
}
resource "kubernetes_secret" "coder_workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = local.workspace_name
namespace = local.namespace
annotations = {
"kubernetes.io/service-account.name" = local.workspace_name
"kubernetes.io/service-account.namespace" = local.namespace
}
}
type = "kubernetes.io/service-account-token"
}
resource "kubernetes_role" "coder_workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = local.workspace_name
namespace = local.namespace
}
rule {
api_groups = ["*"]
resources = ["configmaps"]
resource_names = [local.workspace_name]
verbs = ["*"]
}
}
resource "kubernetes_role_binding" "coder_workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = local.workspace_name
namespace = local.namespace
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "Role"
name = local.workspace_name
}
subject {
kind = "ServiceAccount"
name = local.workspace_name
namespace = local.namespace
}
}
resource "kubernetes_pod" "main" {
count = data.coder_workspace.me.start_count
depends_on = [
kubernetes_role.coder_workspace,
kubernetes_role_binding.coder_workspace,
kubernetes_service_account.coder_workspace,
kubernetes_secret.coder_workspace,
kubernetes_config_map.coder_workspace
]
metadata {
name = local.workspace_name
namespace = local.namespace
}
spec {
service_account_name = local.workspace_name
container {
name = "dev"
image = "codercom/enterprise-base:ubuntu"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
resources {
limits = {
cpu = local.cpu
memory = local.memory
}
}
}
}
}
resource "coder_metadata" "kubernetes_pod_main" {
count = data.coder_workspace.me.start_count
resource_id = kubernetes_pod.main[0].id
item {
key = "cpu"
value = local.cpu
}
item {
key = "memory"
value = local.memory
}
item {
key = "gpu"
value = local.gpu
}
}

View File

@ -0,0 +1,63 @@
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] coder_agent.main (expand)" [label = "coder_agent.main", shape = "box"]
"[root] coder_app.code-server (expand)" [label = "coder_app.code-server", shape = "box"]
"[root] coder_metadata.kubernetes_pod_main (expand)" [label = "coder_metadata.kubernetes_pod_main", shape = "box"]
"[root] data.coder_workspace.me (expand)" [label = "data.coder_workspace.me", shape = "box"]
"[root] data.google_client_config.provider (expand)" [label = "data.google_client_config.provider", shape = "box"]
"[root] data.google_container_cluster.dev-4-2 (expand)" [label = "data.google_container_cluster.dev-4-2", shape = "box"]
"[root] kubernetes_config_map.coder_workspace (expand)" [label = "kubernetes_config_map.coder_workspace", shape = "box"]
"[root] kubernetes_pod.main (expand)" [label = "kubernetes_pod.main", shape = "box"]
"[root] kubernetes_role.coder_workspace (expand)" [label = "kubernetes_role.coder_workspace", shape = "box"]
"[root] kubernetes_role_binding.coder_workspace (expand)" [label = "kubernetes_role_binding.coder_workspace", shape = "box"]
"[root] kubernetes_secret.coder_workspace (expand)" [label = "kubernetes_secret.coder_workspace", shape = "box"]
"[root] kubernetes_service_account.coder_workspace (expand)" [label = "kubernetes_service_account.coder_workspace", shape = "box"]
"[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/google\"]" [label = "provider[\"registry.terraform.io/hashicorp/google\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" [label = "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", shape = "diamond"]
"[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] coder_app.code-server (expand)" -> "[root] coder_agent.main (expand)"
"[root] coder_metadata.kubernetes_pod_main (expand)" -> "[root] kubernetes_pod.main (expand)"
"[root] coder_metadata.kubernetes_pod_main (expand)" -> "[root] local.gpu (expand)"
"[root] data.coder_workspace.me (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.google_client_config.provider (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"]"
"[root] data.google_container_cluster.dev-4-2 (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"]"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_pod.main (expand)" -> "[root] coder_agent.main (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_config_map.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_role.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_role_binding.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_secret.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_service_account.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] local.cpu (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] local.memory (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] local.workspace_name (expand)" -> "[root] data.coder_workspace.me (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.code-server (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_metadata.kubernetes_pod_main (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)" -> "[root] data.google_client_config.provider (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)" -> "[root] data.google_container_cluster.dev-4-2 (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"] (close)" -> "[root] kubernetes_pod.main (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" -> "[root] data.google_client_config.provider (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" -> "[root] data.google_container_cluster.dev-4-2 (expand)"
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"] (close)"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] coder_agent.main (expand)" [label = "coder_agent.main", shape = "box"]
"[root] coder_app.code-server (expand)" [label = "coder_app.code-server", shape = "box"]
"[root] coder_metadata.kubernetes_pod_main (expand)" [label = "coder_metadata.kubernetes_pod_main", shape = "box"]
"[root] data.coder_workspace.me (expand)" [label = "data.coder_workspace.me", shape = "box"]
"[root] data.google_client_config.provider (expand)" [label = "data.google_client_config.provider", shape = "box"]
"[root] data.google_container_cluster.dev-4-2 (expand)" [label = "data.google_container_cluster.dev-4-2", shape = "box"]
"[root] kubernetes_config_map.coder_workspace (expand)" [label = "kubernetes_config_map.coder_workspace", shape = "box"]
"[root] kubernetes_pod.main (expand)" [label = "kubernetes_pod.main", shape = "box"]
"[root] kubernetes_role.coder_workspace (expand)" [label = "kubernetes_role.coder_workspace", shape = "box"]
"[root] kubernetes_role_binding.coder_workspace (expand)" [label = "kubernetes_role_binding.coder_workspace", shape = "box"]
"[root] kubernetes_secret.coder_workspace (expand)" [label = "kubernetes_secret.coder_workspace", shape = "box"]
"[root] kubernetes_service_account.coder_workspace (expand)" [label = "kubernetes_service_account.coder_workspace", shape = "box"]
"[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/google\"]" [label = "provider[\"registry.terraform.io/hashicorp/google\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" [label = "provider[\"registry.terraform.io/hashicorp/kubernetes\"]", shape = "diamond"]
"[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] coder_app.code-server (expand)" -> "[root] coder_agent.main (expand)"
"[root] coder_metadata.kubernetes_pod_main (expand)" -> "[root] kubernetes_pod.main (expand)"
"[root] coder_metadata.kubernetes_pod_main (expand)" -> "[root] local.gpu (expand)"
"[root] data.coder_workspace.me (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.google_client_config.provider (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"]"
"[root] data.google_container_cluster.dev-4-2 (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"]"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_config_map.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_pod.main (expand)" -> "[root] coder_agent.main (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_config_map.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_role.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_role_binding.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_secret.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] kubernetes_service_account.coder_workspace (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] local.cpu (expand)"
"[root] kubernetes_pod.main (expand)" -> "[root] local.memory (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_role.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_role_binding.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_secret.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] local.namespace (expand)"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] local.workspace_name (expand)"
"[root] kubernetes_service_account.coder_workspace (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]"
"[root] local.workspace_name (expand)" -> "[root] data.coder_workspace.me (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.code-server (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_metadata.kubernetes_pod_main (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)" -> "[root] data.google_client_config.provider (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)" -> "[root] data.google_container_cluster.dev-4-2 (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"] (close)" -> "[root] kubernetes_pod.main (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" -> "[root] data.google_client_config.provider (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"]" -> "[root] data.google_container_cluster.dev-4-2 (expand)"
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/google\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/kubernetes\"] (close)"
}
}

File diff suppressed because it is too large Load Diff