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