-
Notifications
You must be signed in to change notification settings - Fork 4.4k
/
create.go
228 lines (187 loc) · 5.65 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package create
import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/flags"
"github.com/hashicorp/consul/command/intention/finder"
"github.com/mitchellh/cli"
)
func New(ui cli.Ui) *cmd {
c := &cmd{UI: ui}
c.init()
return c
}
type cmd struct {
UI cli.Ui
flags *flag.FlagSet
http *flags.HTTPFlags
help string
// flags
flagAllow bool
flagDeny bool
flagFile bool
flagReplace bool
flagMeta map[string]string
// testStdin is the input for testing.
testStdin io.Reader
}
func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.BoolVar(&c.flagAllow, "allow", false,
"Create an intention that allows when matched.")
c.flags.BoolVar(&c.flagDeny, "deny", false,
"Create an intention that denies when matched.")
c.flags.BoolVar(&c.flagFile, "file", false,
"Read intention data from one or more files.")
c.flags.BoolVar(&c.flagReplace, "replace", false,
"Replace matching intentions.")
c.flags.Var((*flags.FlagMapValue)(&c.flagMeta), "meta",
"Metadata to set on the intention, formatted as key=value. This flag "+
"may be specified multiple times to set multiple meta fields.")
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags())
c.help = flags.Usage(help, c.flags)
}
func (c *cmd) Run(args []string) int {
if err := c.flags.Parse(args); err != nil {
return 1
}
// Default to allow
if !c.flagAllow && !c.flagDeny {
c.flagAllow = true
}
// If both are specified it is an error
if c.flagAllow && c.flagDeny {
c.UI.Error("Only one of -allow or -deny may be specified.")
return 1
}
// Check for arg validation
args = c.flags.Args()
ixns, err := c.ixnsFromArgs(args)
if err != nil {
c.UI.Error(fmt.Sprintf("Error: %s", err))
return 1
}
// Create and test the HTTP client
client, err := c.http.APIClient()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}
// Create the finder in case we need it
find := &finder.Finder{Client: client}
// Go through and create each intention
for _, ixn := range ixns {
// If replace is set to true, then perform an update operation.
if c.flagReplace {
oldIxn, err := find.Find(ixn.SourceString(), ixn.DestinationString())
if err != nil {
c.UI.Error(fmt.Sprintf(
"Error looking up intention for replacement with source %q "+
"and destination %q: %s",
ixn.SourceString(),
ixn.DestinationString(),
err))
return 1
}
if oldIxn != nil {
// We set the ID of our intention so we overwrite it
ixn.ID = oldIxn.ID
if _, err := client.Connect().IntentionUpdate(ixn, nil); err != nil {
c.UI.Error(fmt.Sprintf(
"Error replacing intention with source %q "+
"and destination %q: %s",
ixn.SourceString(),
ixn.DestinationString(),
err))
return 1
}
// Continue since we don't want to try to insert a new intention
continue
}
}
_, _, err := client.Connect().IntentionCreate(ixn, nil)
if err != nil {
c.UI.Error(fmt.Sprintf("Error creating intention %q: %s", ixn, err))
return 1
}
c.UI.Output(fmt.Sprintf("Created: %s", ixn))
}
return 0
}
// ixnsFromArgs returns the set of intentions to create based on the arguments
// given and the flags set. This will call ixnsFromFiles if the -file flag
// was set.
func (c *cmd) ixnsFromArgs(args []string) ([]*api.Intention, error) {
// If we're in file mode, load from files
if c.flagFile {
return c.ixnsFromFiles(args)
}
// From args we require exactly two
if len(args) != 2 {
return nil, fmt.Errorf("Must specify two arguments: source and destination")
}
return []*api.Intention{&api.Intention{
SourceName: args[0],
DestinationName: args[1],
SourceType: api.IntentionSourceConsul,
Action: c.ixnAction(),
Meta: c.flagMeta,
}}, nil
}
func (c *cmd) ixnsFromFiles(args []string) ([]*api.Intention, error) {
var result []*api.Intention
for _, path := range args {
f, err := os.Open(path)
if err != nil {
return nil, err
}
var ixn api.Intention
err = json.NewDecoder(f).Decode(&ixn)
f.Close()
if err != nil {
return nil, err
}
result = append(result, &ixn)
}
return result, nil
}
// ixnAction returns the api.IntentionAction based on the flag set.
func (c *cmd) ixnAction() api.IntentionAction {
if c.flagAllow {
return api.IntentionActionAllow
}
return api.IntentionActionDeny
}
func (c *cmd) Synopsis() string {
return synopsis
}
func (c *cmd) Help() string {
return c.help
}
const synopsis = "Create intentions for service connections."
const help = `
Usage: consul intention create [options] SRC DST
Usage: consul intention create [options] -file FILE...
Create one or more intentions. The data can be specified as a single
source and destination pair or via a set of files when the "-file" flag
is specified.
$ consul intention create web db
To consume data from a set of files:
$ consul intention create -file one.json two.json
When specifying the "-file" flag, "-" may be used once to read from stdin:
$ echo "{ ... }" | consul intention create -file -
An "allow" intention is created by default (whitelist). To create a
"deny" intention, the "-deny" flag should be specified.
If a conflicting intention is found, creation will fail. To replace any
conflicting intentions, specify the "-replace" flag. This will replace any
conflicting intentions with the intention specified in this command.
Metadata and any other fields of the previous intention will not be
preserved.
Additional flags and more advanced use cases are detailed below.
`