2023-09-14 20:14:55 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
|
|
|
package templatedpolicy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/api"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
PrettyFormat string = "pretty"
|
|
|
|
JSONFormat string = "json"
|
|
|
|
WhitespaceIndent = "\t"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Formatter defines methods provided by templated-policy command output formatter
|
|
|
|
type Formatter interface {
|
|
|
|
FormatTemplatedPolicy(policy api.ACLTemplatedPolicyResponse) (string, error)
|
|
|
|
FormatTemplatedPolicyList(policies map[string]api.ACLTemplatedPolicyResponse) (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: %q", format)
|
|
|
|
}
|
|
|
|
|
|
|
|
return formatter, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func newPrettyFormatter(showMeta bool) Formatter {
|
|
|
|
return &prettyFormatter{showMeta}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newJSONFormatter(showMeta bool) Formatter {
|
|
|
|
return &jsonFormatter{showMeta}
|
|
|
|
}
|
|
|
|
|
|
|
|
type prettyFormatter struct {
|
|
|
|
showMeta bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// FormatTemplatedPolicy displays template name, input variables and example usages. When
|
|
|
|
// showMeta is true, we display raw template code and schema.
|
|
|
|
// This implementation is a conscious choice as we know builtin variables we know every required/optional input variables
|
|
|
|
// so we can just hardcode this.
|
|
|
|
// In the future, when we implement user defined templated policies, we will move this to some sort of schema parsing.
|
|
|
|
// This implementation allows us to move forward without limiting ourselves when implementing user defined templated policies.
|
|
|
|
func (f *prettyFormatter) FormatTemplatedPolicy(templatedPolicy api.ACLTemplatedPolicyResponse) (string, error) {
|
|
|
|
var buffer bytes.Buffer
|
|
|
|
|
2023-09-15 14:20:44 +00:00
|
|
|
buffer.WriteString(fmt.Sprintf("Name: %s\n", templatedPolicy.TemplateName))
|
2023-09-14 20:14:55 +00:00
|
|
|
|
|
|
|
buffer.WriteString("Input variables:")
|
|
|
|
switch templatedPolicy.TemplateName {
|
|
|
|
case api.ACLTemplatedPolicyServiceName:
|
|
|
|
buffer.WriteString(fmt.Sprintf("\n%sName: String - Required - The name of the service.\n", WhitespaceIndent))
|
|
|
|
buffer.WriteString("Example usage:\n")
|
|
|
|
buffer.WriteString(WhitespaceIndent + "consul acl token create -templated-policy builtin/service -var name:api\n")
|
|
|
|
case api.ACLTemplatedPolicyNodeName:
|
|
|
|
buffer.WriteString(fmt.Sprintf("\n%sName: String - Required - The node name.\n", WhitespaceIndent))
|
|
|
|
buffer.WriteString("Example usage:\n")
|
|
|
|
buffer.WriteString(fmt.Sprintf("%sconsul acl token create -templated-policy builtin/node -var name:node-1\n", WhitespaceIndent))
|
2023-09-20 16:10:55 +00:00
|
|
|
case api.ACLTemplatedPolicyDNSName, api.ACLTemplatedPolicyNomadServerName:
|
|
|
|
noRequiredVariablesOutput(&buffer, templatedPolicy.TemplateName)
|
2023-09-14 20:14:55 +00:00
|
|
|
default:
|
|
|
|
buffer.WriteString(" None\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.showMeta {
|
|
|
|
if templatedPolicy.Schema != "" {
|
|
|
|
buffer.WriteString(fmt.Sprintf("Schema:\n%s\n\n", templatedPolicy.Schema))
|
|
|
|
}
|
|
|
|
buffer.WriteString(fmt.Sprintf("Raw Template:\n%s\n", templatedPolicy.Template))
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer.String(), nil
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:10:55 +00:00
|
|
|
func noRequiredVariablesOutput(buffer *bytes.Buffer, templateName string) {
|
|
|
|
buffer.WriteString(" None\n")
|
|
|
|
buffer.WriteString("Example usage:\n")
|
|
|
|
buffer.WriteString(fmt.Sprintf("%sconsul acl token create -templated-policy %s\n", WhitespaceIndent, templateName))
|
|
|
|
}
|
|
|
|
|
2023-09-14 20:14:55 +00:00
|
|
|
func (f *prettyFormatter) FormatTemplatedPolicyList(policies map[string]api.ACLTemplatedPolicyResponse) (string, error) {
|
|
|
|
var buffer bytes.Buffer
|
|
|
|
|
|
|
|
templateNames := make([]string, 0, len(policies))
|
|
|
|
for _, templatedPolicy := range policies {
|
|
|
|
templateNames = append(templateNames, templatedPolicy.TemplateName)
|
|
|
|
}
|
|
|
|
|
|
|
|
//ensure the list is consistently sorted by strings
|
|
|
|
sort.Strings(templateNames)
|
|
|
|
for _, name := range templateNames {
|
|
|
|
buffer.WriteString(fmt.Sprintf("%s\n", name))
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type jsonFormatter struct {
|
|
|
|
showMeta bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *jsonFormatter) FormatTemplatedPolicy(templatedPolicy api.ACLTemplatedPolicyResponse) (string, error) {
|
|
|
|
b, err := json.MarshalIndent(templatedPolicy, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to marshal templated policy: %v", err)
|
|
|
|
}
|
|
|
|
return string(b), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *jsonFormatter) FormatTemplatedPolicyList(templatedPolicies map[string]api.ACLTemplatedPolicyResponse) (string, error) {
|
|
|
|
b, err := json.MarshalIndent(templatedPolicies, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to marshal templated policies: %v", err)
|
|
|
|
}
|
|
|
|
return string(b), nil
|
|
|
|
}
|