mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: Allow hide resources (#3977)
This commit is contained in:
@ -36,6 +36,7 @@ type agentAppAttributes struct {
|
||||
// A mapping of attributes on the "coder_metadata" resource.
|
||||
type metadataAttributes struct {
|
||||
ResourceID string `mapstructure:"resource_id"`
|
||||
Hide bool `mapstructure:"hide"`
|
||||
Items []metadataItem `mapstructure:"item"`
|
||||
}
|
||||
|
||||
@ -48,6 +49,7 @@ type metadataItem struct {
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
@ -137,7 +139,7 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
|
||||
}
|
||||
|
||||
var agentResource *graphResource
|
||||
for _, resource := range findResourcesUpGraph(graph, tfResourceByLabel, agentNode.Name, 0) {
|
||||
for _, resource := range findResourcesInGraph(graph, tfResourceByLabel, agentNode.Name, 0, true) {
|
||||
if agentResource == nil {
|
||||
// Default to the first resource because we have nothing to compare!
|
||||
agentResource = resource
|
||||
@ -234,7 +236,8 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
|
||||
|
||||
// Associate metadata blocks with resources.
|
||||
resourceMetadata := map[string][]*proto.Resource_Metadata{}
|
||||
for label, resource := range tfResourceByLabel {
|
||||
resourceHidden := map[string]bool{}
|
||||
for _, resource := range tfResourceByLabel {
|
||||
if resource.Type != "coder_metadata" {
|
||||
continue
|
||||
}
|
||||
@ -244,17 +247,54 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
|
||||
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 == "" {
|
||||
// TODO: detect this as an error
|
||||
// At plan time, change.after_unknown.resource_id should be "true".
|
||||
// At provision time, values.resource_id should be set.
|
||||
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 {
|
||||
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
|
||||
}
|
||||
if targetLabel == "" {
|
||||
targetLabel = resourceLabelByID[attrs.ResourceID]
|
||||
}
|
||||
if targetLabel == "" {
|
||||
continue
|
||||
}
|
||||
targetLabel, ok := resourceLabelByID[attrs.ResourceID]
|
||||
if !ok {
|
||||
return nil, xerrors.Errorf("attribute %s.resource_id = %q does not refer to a valid resource", label, attrs.ResourceID)
|
||||
}
|
||||
|
||||
resourceHidden[targetLabel] = attrs.Hide
|
||||
for _, item := range attrs.Items {
|
||||
resourceMetadata[targetLabel] = append(resourceMetadata[targetLabel],
|
||||
&proto.Resource_Metadata{
|
||||
@ -284,6 +324,7 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
|
||||
Name: resource.Name,
|
||||
Type: resource.Type,
|
||||
Agents: agents,
|
||||
Hide: resourceHidden[label],
|
||||
Metadata: resourceMetadata[label],
|
||||
})
|
||||
}
|
||||
@ -344,14 +385,19 @@ func applyAutomaticInstanceID(resource *tfjson.StateResource, agents []*proto.Ag
|
||||
}
|
||||
}
|
||||
|
||||
// findResourcesUpGraph traverses upwards in a graph until a resource is found,
|
||||
// findResourcesInGraph traverses directionally in a graph until a resource is found,
|
||||
// then it stores the depth it was found at, and continues working up the tree.
|
||||
func findResourcesUpGraph(graph *gographviz.Graph, tfResourceByLabel map[string]*tfjson.StateResource, nodeName string, currentDepth uint) []*graphResource {
|
||||
// nolint:revive
|
||||
func findResourcesInGraph(graph *gographviz.Graph, tfResourceByLabel map[string]*tfjson.StateResource, nodeName string, currentDepth uint, up bool) []*graphResource {
|
||||
graphResources := make([]*graphResource, 0)
|
||||
for destination := range graph.Edges.DstToSrcs[nodeName] {
|
||||
mapping := graph.Edges.DstToSrcs
|
||||
if !up {
|
||||
mapping = graph.Edges.SrcToDsts
|
||||
}
|
||||
for destination := range mapping[nodeName] {
|
||||
destinationNode := graph.Nodes.Lookup[destination]
|
||||
// Work our way up the tree!
|
||||
graphResources = append(graphResources, findResourcesUpGraph(graph, tfResourceByLabel, destinationNode.Name, currentDepth+1)...)
|
||||
graphResources = append(graphResources, findResourcesInGraph(graph, tfResourceByLabel, destinationNode.Name, currentDepth+1, up)...)
|
||||
|
||||
destinationLabel, exists := destinationNode.Attrs["label"]
|
||||
if !exists {
|
||||
|
Reference in New Issue
Block a user