// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package role import ( "bytes" "encoding/json" "fmt" "strings" "github.com/hashicorp/consul/api" ) const ( PrettyFormat string = "pretty" JSONFormat string = "json" ) // Formatter defines methods provided by role command output formatter type Formatter interface { FormatRole(role *api.ACLRole) (string, error) FormatRoleList(roles []*api.ACLRole) (string, error) } // GetSupportedFormats returns supported formats func GetSupportedFormats() []string { return []string{PrettyFormat, JSONFormat} } // NewFormatter returns Formatter implementation func NewFormatter(format string, showMeta bool) (formatter Formatter, err error) { switch format { case PrettyFormat: formatter = newPrettyFormatter(showMeta) case JSONFormat: formatter = newJSONFormatter(showMeta) default: err = fmt.Errorf("Unknown format: %s", format) } return formatter, err } func newPrettyFormatter(showMeta bool) Formatter { return &prettyFormatter{showMeta} } type prettyFormatter struct { showMeta bool } func (f *prettyFormatter) FormatRole(role *api.ACLRole) (string, error) { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("ID: %s\n", role.ID)) buffer.WriteString(fmt.Sprintf("Name: %s\n", role.Name)) if role.Partition != "" { buffer.WriteString(fmt.Sprintf("Partition: %s\n", role.Partition)) } if role.Namespace != "" { buffer.WriteString(fmt.Sprintf("Namespace: %s\n", role.Namespace)) } buffer.WriteString(fmt.Sprintf("Description: %s\n", role.Description)) if f.showMeta { buffer.WriteString(fmt.Sprintf("Hash: %x\n", role.Hash)) buffer.WriteString(fmt.Sprintf("Create Index: %d\n", role.CreateIndex)) buffer.WriteString(fmt.Sprintf("Modify Index: %d\n", role.ModifyIndex)) } if len(role.Policies) > 0 { buffer.WriteString(fmt.Sprintln("Policies:")) for _, policy := range role.Policies { buffer.WriteString(fmt.Sprintf(" %s - %s\n", policy.ID, policy.Name)) } } if len(role.ServiceIdentities) > 0 { buffer.WriteString(fmt.Sprintln("Service Identities:")) for _, svcid := range role.ServiceIdentities { if len(svcid.Datacenters) > 0 { buffer.WriteString(fmt.Sprintf(" %s (Datacenters: %s)\n", svcid.ServiceName, strings.Join(svcid.Datacenters, ", "))) } else { buffer.WriteString(fmt.Sprintf(" %s (Datacenters: all)\n", svcid.ServiceName)) } } } if len(role.NodeIdentities) > 0 { buffer.WriteString(fmt.Sprintln("Node Identities:")) for _, nodeid := range role.NodeIdentities { buffer.WriteString(fmt.Sprintf(" %s (Datacenter: %s)\n", nodeid.NodeName, nodeid.Datacenter)) } } if len(role.TemplatedPolicies) > 0 { buffer.WriteString(fmt.Sprintln("Templated Policies:")) for _, templatedPolicy := range role.TemplatedPolicies { buffer.WriteString(fmt.Sprintf(" %s\n", templatedPolicy.TemplateName)) if templatedPolicy.TemplateVariables != nil && templatedPolicy.TemplateVariables.Name != "" { buffer.WriteString(fmt.Sprintf(" Name: %s\n", templatedPolicy.TemplateVariables.Name)) } if len(templatedPolicy.Datacenters) > 0 { buffer.WriteString(fmt.Sprintf(" Datacenters: %s\n", strings.Join(templatedPolicy.Datacenters, ", "))) } else { buffer.WriteString(" Datacenters: all\n") } } } return buffer.String(), nil } func (f *prettyFormatter) FormatRoleList(roles []*api.ACLRole) (string, error) { var buffer bytes.Buffer for _, role := range roles { buffer.WriteString(f.formatRoleListEntry(role)) } return buffer.String(), nil } func (f *prettyFormatter) formatRoleListEntry(role *api.ACLRole) string { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("%s:\n", role.Name)) buffer.WriteString(fmt.Sprintf(" ID: %s\n", role.ID)) if role.Partition != "" { buffer.WriteString(fmt.Sprintf(" Partition: %s\n", role.Partition)) } if role.Namespace != "" { buffer.WriteString(fmt.Sprintf(" Namespace: %s\n", role.Namespace)) } buffer.WriteString(fmt.Sprintf(" Description: %s\n", role.Description)) if f.showMeta { buffer.WriteString(fmt.Sprintf(" Hash: %x\n", role.Hash)) buffer.WriteString(fmt.Sprintf(" Create Index: %d\n", role.CreateIndex)) buffer.WriteString(fmt.Sprintf(" Modify Index: %d\n", role.ModifyIndex)) } if len(role.Policies) > 0 { buffer.WriteString(fmt.Sprintln(" Policies:")) for _, policy := range role.Policies { buffer.WriteString(fmt.Sprintf(" %s - %s\n", policy.ID, policy.Name)) } } if len(role.ServiceIdentities) > 0 { buffer.WriteString(fmt.Sprintln(" Service Identities:")) for _, svcid := range role.ServiceIdentities { if len(svcid.Datacenters) > 0 { buffer.WriteString(fmt.Sprintf(" %s (Datacenters: %s)\n", svcid.ServiceName, strings.Join(svcid.Datacenters, ", "))) } else { buffer.WriteString(fmt.Sprintf(" %s (Datacenters: all)\n", svcid.ServiceName)) } } } if len(role.NodeIdentities) > 0 { buffer.WriteString(fmt.Sprintln(" Node Identities:")) for _, nodeid := range role.NodeIdentities { buffer.WriteString(fmt.Sprintf(" %s (Datacenter: %s)\n", nodeid.NodeName, nodeid.Datacenter)) } } if len(role.TemplatedPolicies) > 0 { buffer.WriteString(fmt.Sprintln(" Templated Policies:")) for _, templatedPolicy := range role.TemplatedPolicies { buffer.WriteString(fmt.Sprintf(" %s\n", templatedPolicy.TemplateName)) if templatedPolicy.TemplateVariables != nil && templatedPolicy.TemplateVariables.Name != "" { buffer.WriteString(fmt.Sprintf(" Name: %s\n", templatedPolicy.TemplateVariables.Name)) } if len(templatedPolicy.Datacenters) > 0 { buffer.WriteString(fmt.Sprintf(" Datacenters: %s\n", strings.Join(templatedPolicy.Datacenters, ", "))) } else { buffer.WriteString(" Datacenters: all\n") } } } return buffer.String() } func newJSONFormatter(showMeta bool) Formatter { return &jsonFormatter{showMeta} } type jsonFormatter struct { showMeta bool } func (f *jsonFormatter) FormatRole(role *api.ACLRole) (string, error) { b, err := json.MarshalIndent(role, "", " ") if err != nil { return "", fmt.Errorf("Failed to marshal role: %v", err) } return string(b), nil } func (f *jsonFormatter) FormatRoleList(roles []*api.ACLRole) (string, error) { b, err := json.MarshalIndent(roles, "", " ") if err != nil { return "", fmt.Errorf("Failed to marshal roles: %v", err) } return string(b), nil }