master
go 198 lines 4.89 KB
Raw
1 //go:build cgo
2
3 package as400
4
5 import (
6 "fmt"
7 "strings"
8 )
9
10 type queueTarget struct {
11 Library string
12 Name string
13 }
14
15 func (t queueTarget) ID() string {
16 return t.Library + "/" + t.Name
17 }
18
19 type activeJobTarget struct {
20 Number string
21 User string
22 Name string
23 }
24
25 func (t activeJobTarget) ID() string {
26 return t.Number + "/" + t.User + "/" + t.Name
27 }
28
29 func parseQueueTargets(entries []string) ([]queueTarget, error) {
30 var targets []queueTarget
31 seen := make(map[string]struct{})
32
33 for _, raw := range entries {
34 trimmed := strings.TrimSpace(raw)
35 if trimmed == "" {
36 continue
37 }
38 parts := strings.SplitN(trimmed, "/", 2)
39 if len(parts) != 2 {
40 return nil, fmt.Errorf("invalid queue identifier %q (expected LIBRARY/QUEUE)", raw)
41 }
42 lib := strings.ToUpper(strings.TrimSpace(parts[0]))
43 name := strings.ToUpper(strings.TrimSpace(parts[1]))
44 if lib == "" || name == "" {
45 return nil, fmt.Errorf("invalid queue identifier %q (library and queue must be non-empty)", raw)
46 }
47 if !validObjectName(lib) || !validObjectName(name) {
48 return nil, fmt.Errorf("invalid queue identifier %q (only A-Z, 0-9, _, $, #, @ allowed)", raw)
49 }
50 key := lib + "/" + name
51 if _, exists := seen[key]; exists {
52 continue
53 }
54 seen[key] = struct{}{}
55 targets = append(targets, queueTarget{
56 Library: lib,
57 Name: name,
58 })
59 }
60
61 return targets, nil
62 }
63
64 func parseActiveJobTargets(entries []string) ([]activeJobTarget, error) {
65 var targets []activeJobTarget
66 seen := make(map[string]struct{})
67
68 for _, raw := range entries {
69 trimmed := strings.TrimSpace(raw)
70 if trimmed == "" {
71 continue
72 }
73
74 parts := strings.Split(trimmed, "/")
75 if len(parts) != 3 {
76 return nil, fmt.Errorf("invalid active job identifier %q (expected JOB_NUMBER/USER/JOB_NAME)", raw)
77 }
78
79 number := strings.TrimSpace(parts[0])
80 user := strings.ToUpper(strings.TrimSpace(parts[1]))
81 name := strings.ToUpper(strings.TrimSpace(parts[2]))
82
83 if !validJobNumber(number) {
84 return nil, fmt.Errorf("invalid active job identifier %q (job number must be six digits)", raw)
85 }
86 if !validObjectName(user) || !validObjectName(name) {
87 return nil, fmt.Errorf("invalid active job identifier %q (only A-Z, 0-9, _, $, #, @ allowed)", raw)
88 }
89
90 key := number + "/" + user + "/" + name
91 if _, exists := seen[key]; exists {
92 continue
93 }
94 seen[key] = struct{}{}
95 targets = append(targets, activeJobTarget{
96 Number: number,
97 User: user,
98 Name: name,
99 })
100 }
101
102 return targets, nil
103 }
104
105 func (c *Collector) configureTargets() error {
106 targets, err := parseQueueTargets(c.MessageQueues)
107 if err != nil {
108 return fmt.Errorf("message_queues configuration error: %w", err)
109 }
110 c.messageQueueTargets = targets
111
112 targets, err = parseQueueTargets(c.JobQueues)
113 if err != nil {
114 return fmt.Errorf("job_queues configuration error: %w", err)
115 }
116 c.jobQueueTargets = targets
117
118 targets, err = parseQueueTargets(c.OutputQueues)
119 if err != nil {
120 return fmt.Errorf("output_queues configuration error: %w", err)
121 }
122 c.outputQueueTargets = targets
123
124 jobTargets, err := parseActiveJobTargets(c.ActiveJobs)
125 if err != nil {
126 return fmt.Errorf("active_jobs configuration error: %w", err)
127 }
128 c.activeJobTargets = jobTargets
129
130 if len(c.messageQueueTargets) == 0 {
131 c.Infof("message queue metrics disabled: no queues configured")
132 } else {
133 c.Infof("message queue metrics enabled for %d queue(s): %s", len(c.messageQueueTargets), queueTargetList(c.messageQueueTargets))
134 }
135
136 if len(c.jobQueueTargets) == 0 {
137 c.Infof("job queue metrics disabled: no queues configured")
138 } else {
139 c.Infof("job queue metrics enabled for %d queue(s): %s", len(c.jobQueueTargets), queueTargetList(c.jobQueueTargets))
140 }
141
142 if len(c.outputQueueTargets) == 0 {
143 c.Infof("output queue metrics disabled: no queues configured")
144 } else {
145 c.Infof("output queue metrics enabled for %d queue(s): %s", len(c.outputQueueTargets), queueTargetList(c.outputQueueTargets))
146 }
147 if len(c.activeJobTargets) == 0 {
148 c.Infof("active job metrics disabled: no jobs configured")
149 } else {
150 c.Infof("active job metrics enabled for %d job(s): %s", len(c.activeJobTargets), activeJobTargetList(c.activeJobTargets))
151 }
152
153 return nil
154 }
155
156 func queueTargetList(targets []queueTarget) string {
157 ids := make([]string, 0, len(targets))
158 for _, t := range targets {
159 ids = append(ids, t.ID())
160 }
161 return strings.Join(ids, ", ")
162 }
163
164 func activeJobTargetList(targets []activeJobTarget) string {
165 ids := make([]string, 0, len(targets))
166 for _, t := range targets {
167 ids = append(ids, t.ID())
168 }
169 return strings.Join(ids, ", ")
170 }
171
172 func validObjectName(value string) bool {
173 for _, r := range value {
174 switch {
175 case r >= 'A' && r <= 'Z':
176 continue
177 case r >= '0' && r <= '9':
178 continue
179 case r == '_' || r == '$' || r == '#' || r == '@':
180 continue
181 default:
182 return false
183 }
184 }
185 return true
186 }
187
188 func validJobNumber(value string) bool {
189 if len(value) != 6 {
190 return false
191 }
192 for _, r := range value {
193 if r < '0' || r > '9' {
194 return false
195 }
196 }
197 return true
198 }