-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcmd.go
More file actions
164 lines (145 loc) · 4.06 KB
/
cmd.go
File metadata and controls
164 lines (145 loc) · 4.06 KB
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
//go:build !test
// +build !test
package haproxy
import (
"bufio"
"errors"
"fmt"
"net"
"regexp"
"strconv"
"strings"
)
type ACL struct {
// haproxy-assigned ID
ID int `json:"id"`
// path of external file that is source of this ACL entries
SourceFile string `json:"source_file"`
// type of ACL if it is from config or "file" if it sourced from file (haproxy doesnt return type here)
Type string `json:"type"`
// Line of inline ACL
Line int `json:"line"`
}
// HAProxy socket interface
type Conn struct {
socketPath string
}
// pattern for ACLs originating from config files
var inlineACLRegex = regexp.MustCompile(`^(\d+) \((.*)\) acl '(.+)' file '(.*)' line (\d+)`)
// pattern for ACLs included via -f option
var fileACLRegex = regexp.MustCompile(`^(\d+) \((.*)\) pattern loaded from file '(.*)' used by`)
// Setup new connection
// accepts path to haproxy unix socket
func New(path string) Conn {
var c Conn
c.socketPath = path
return c
}
// Add new entry to acl
// ACL name is either file path ( if haproxy config uses -f option to load acls from file ) or ACL id prepended with hash
func (c *Conn) AddACL(acl string, pattern string) error {
var err error
out, err := c.RunCmd(fmt.Sprintf("add acl %s %s\n", acl, pattern))
if err != nil {
return err
}
if out[0] != "" && out[0] != "Done." {
return errors.New(fmt.Sprintf("error: %s", out[0]))
}
return err
}
// Delete entry from ACL
// ID is value of map returned by GetACL
func (c *Conn) DeleteACL(acl string, id string) error {
var err error
if strings.ContainsAny(id, " \t\n") || id == "" {
return errors.New("id should not contain whitespaces or be empty as that would remove every ACL, use ClearACL for that")
}
out, err := c.RunCmd(fmt.Sprintf("del acl %s %s\n", acl, id))
if strings.Contains(out[0], "Key not found") {
return errors.New("Key not found")
}
return err
}
// Get map of all entries in ACL
// map is value => ID for easy lookup
// so deleting acls is just
// err := ha.DeleteACL( acls["/test/acl"] )
func (c *Conn) GetACL(acl string) (map[string]string, error) {
var err error
out, err := c.RunCmd(fmt.Sprintf("show acl %s", acl))
acls := make(map[string]string)
for _, line := range out {
parts := strings.Split(line, " ")
if len(parts) > 1 {
acls[parts[1]] = parts[0]
}
}
return acls, err
}
// List all ACLs that haproxy currenty uses
func (c *Conn) ListACL() ([]ACL, error) {
var err error
var acl []ACL
out, err := c.RunCmd("show acl")
for _, line := range out {
var a ACL
matches := inlineACLRegex.FindStringSubmatch(line)
if len(matches) > 2 {
a.ID, _ = strconv.Atoi(matches[1])
a.Type = matches[3]
a.Line, _ = strconv.Atoi(matches[5])
acl = append(acl, a)
} else {
matches = fileACLRegex.FindStringSubmatch(line)
if len(matches) > 2 {
a.ID, _ = strconv.Atoi(matches[1])
a.SourceFile = matches[2]
a.Type = "file"
acl = append(acl, a)
} else {
continue
}
}
}
return acl, err
}
// Return map with all external files that are used as ACL entry source with first occurence of ACL as a value
func (c *Conn) ListACLFiles() (map[string]ACL, error) {
var err error
acl_list, err := c.ListACL()
out := make(map[string]ACL)
for _, acl := range acl_list {
fmt.Printf("OUT: [%+v]\n", acl)
// HAProxy appears to not display same file
// used by different ACLs, but just in case don't overwrite
if _, ok := out[acl.SourceFile]; acl.Type == "file" && !ok {
out[acl.SourceFile] = acl
}
}
return out, err
}
// Clear all entries in ACL
func (c *Conn) ClearACL(acl string) error {
var err error
out, err := c.RunCmd(fmt.Sprintf("clear acl %s", acl))
if out[0] != "" && out[0] != "Done." {
return errors.New(fmt.Sprintf("error: %+v", out))
}
return err
}
// Run arbitrary haproxy command and return output
func (c *Conn) RunCmd(cmd string) ([]string, error) {
conn, err := net.Dial("unix", c.socketPath)
var out []string
if err != nil {
return out, err
}
defer conn.Close()
fmt.Fprintf(conn, "%s\n", cmd)
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
out = append(out, scanner.Text())
}
return out, scanner.Err()
}