1package bindingrulecreate
2
3import (
4	"flag"
5	"fmt"
6	"strings"
7
8	"github.com/hashicorp/consul/api"
9	"github.com/hashicorp/consul/command/acl/bindingrule"
10	"github.com/hashicorp/consul/command/flags"
11	"github.com/mitchellh/cli"
12)
13
14func New(ui cli.Ui) *cmd {
15	c := &cmd{UI: ui}
16	c.init()
17	return c
18}
19
20type cmd struct {
21	UI    cli.Ui
22	flags *flag.FlagSet
23	http  *flags.HTTPFlags
24	help  string
25
26	authMethodName string
27	description    string
28	selector       string
29	bindType       string
30	bindName       string
31
32	showMeta bool
33	format   string
34}
35
36func (c *cmd) init() {
37	c.flags = flag.NewFlagSet("", flag.ContinueOnError)
38
39	c.flags.BoolVar(
40		&c.showMeta,
41		"meta",
42		false,
43		"Indicates that binding rule metadata such "+
44			"as the raft indices should be shown for each entry.",
45	)
46
47	c.flags.StringVar(
48		&c.authMethodName,
49		"method",
50		"",
51		"The auth method's name for which this binding rule applies. "+
52			"This flag is required.",
53	)
54	c.flags.StringVar(
55		&c.description,
56		"description",
57		"",
58		"A description of the binding rule.",
59	)
60	c.flags.StringVar(
61		&c.selector,
62		"selector",
63		"",
64		"Selector is an expression that matches against verified identity "+
65			"attributes returned from the auth method during login.",
66	)
67	c.flags.StringVar(
68		&c.bindType,
69		"bind-type",
70		string(api.BindingRuleBindTypeService),
71		"Type of binding to perform (\"service\" or \"role\").",
72	)
73	c.flags.StringVar(
74		&c.bindName,
75		"bind-name",
76		"",
77		"Name to bind on match. Can use ${var} interpolation. "+
78			"This flag is required.",
79	)
80	c.flags.StringVar(
81		&c.format,
82		"format",
83		bindingrule.PrettyFormat,
84		fmt.Sprintf("Output format {%s}", strings.Join(bindingrule.GetSupportedFormats(), "|")),
85	)
86
87	c.http = &flags.HTTPFlags{}
88	flags.Merge(c.flags, c.http.ClientFlags())
89	flags.Merge(c.flags, c.http.ServerFlags())
90	flags.Merge(c.flags, c.http.NamespaceFlags())
91	c.help = flags.Usage(help, c.flags)
92}
93
94func (c *cmd) Run(args []string) int {
95	if err := c.flags.Parse(args); err != nil {
96		return 1
97	}
98
99	if c.authMethodName == "" {
100		c.UI.Error(fmt.Sprintf("Missing required '-method' flag"))
101		c.UI.Error(c.Help())
102		return 1
103	} else if c.bindType == "" {
104		c.UI.Error(fmt.Sprintf("Missing required '-bind-type' flag"))
105		c.UI.Error(c.Help())
106		return 1
107	} else if c.bindName == "" {
108		c.UI.Error(fmt.Sprintf("Missing required '-bind-name' flag"))
109		c.UI.Error(c.Help())
110		return 1
111	}
112
113	newRule := &api.ACLBindingRule{
114		Description: c.description,
115		AuthMethod:  c.authMethodName,
116		BindType:    api.BindingRuleBindType(c.bindType),
117		BindName:    c.bindName,
118		Selector:    c.selector,
119	}
120
121	client, err := c.http.APIClient()
122	if err != nil {
123		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
124		return 1
125	}
126
127	rule, _, err := client.ACL().BindingRuleCreate(newRule, nil)
128	if err != nil {
129		c.UI.Error(fmt.Sprintf("Failed to create new binding rule: %v", err))
130		return 1
131	}
132
133	formatter, err := bindingrule.NewFormatter(c.format, c.showMeta)
134	if err != nil {
135		c.UI.Error(err.Error())
136		return 1
137	}
138
139	out, err := formatter.FormatBindingRule(rule)
140	if err != nil {
141		c.UI.Error(err.Error())
142		return 1
143	}
144	if out != "" {
145		c.UI.Info(out)
146	}
147
148	return 0
149}
150
151func (c *cmd) Synopsis() string {
152	return synopsis
153}
154
155func (c *cmd) Help() string {
156	return flags.Usage(c.help, nil)
157}
158
159const synopsis = "Create an ACL binding rule"
160
161const help = `
162Usage: consul acl binding-rule create [options]
163
164  Create a new binding rule:
165
166    $ consul acl binding-rule create \
167          -method=minikube \
168          -bind-type=service \
169          -bind-name='k8s-${serviceaccount.name}' \
170          -selector='serviceaccount.namespace==default and serviceaccount.name==web'
171`
172