-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
jboss.go
306 lines (284 loc) · 10.1 KB
/
jboss.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package usm
import (
"encoding/xml"
"errors"
"fmt"
"io/fs"
"path"
"strings"
"go.uber.org/zap"
)
const (
jbossServerName = "-D[Server:"
jbossHomeDirSysProp = "-Djboss.home.dir="
jbossConfigDir = "configuration"
jbossHostXMLFile = "host.xml"
jbossDefaultStandaloneXMLFile = "standalone.xml"
jbossDefaultDomainXMLFile = "domain.xml"
jbossDomainBase = "domain"
jbossStandaloneBase = "standalone"
jbossDataDir = "data"
jbossContentDir = "content"
jbossServerConfigShort = "-c"
jbossServerConfig = "--server-config"
jbossDomainConfig = "--domain-config"
jbossWebXMLFileMetaInf = "META-INF/jboss-web.xml"
jbossWebXMLFileWebInf = "WEB-INF/jboss-web.xml"
)
type (
jbossExtractor struct {
cxt DetectionContext
}
// jbossHostsXML allows unmarshalling the host.xml file
jbossHostXML struct {
XMLName xml.Name `xml:"host"`
Servers []jbossHostServer `xml:"servers>server"`
}
// jbossHostsServer contains the mapping between server name and server group
jbossHostServer struct {
Name string `xml:"name,attr"`
Group string `xml:"group,attr"`
}
// jbossStandaloneXML allows unmarshalling the standalone.xml file
jbossStandaloneXML struct {
XMLName xml.Name `xml:"server"`
Deployments []jbossServerDeployment `xml:"deployments>deployment"`
}
// jbossServerDeployment contains information about deployed content on a jboss instance
jbossServerDeployment struct {
Name string `xml:"name,attr"`
RuntimeName string `xml:"runtime-name,attr"`
// Enabled by default is true.
Enabled string `xml:"enabled,attr,"`
Content jbossDeployedContent `xml:"content"`
}
// jbossDeployedContent hold the sha1 of a deployed content (will map to a dir)
jbossDeployedContent struct {
Hash string `xml:"sha1,attr"`
}
//jbossServerGroup contains information about the server groups defined for a host.
jbossServerGroup struct {
Name string `xml:"name,attr"`
Deployments []jbossServerDeployment `xml:"deployments>deployment"`
}
// jbossDomainXML allows unmarshalling the domain.xml file
jbossDomainXML struct {
XMLName xml.Name `xml:"domain"`
Deployments []jbossServerDeployment `xml:"deployments>deployment"`
ServerGroups []jbossServerGroup `xml:"server-groups>server-group"`
}
// jbossWebXML allows unmarshalling the jboss-web.xml file
jbossWebXML struct {
XMLName xml.Name `xml:"jboss-web"`
ContextRoot string `xml:"context-root"`
}
)
func newJbossExtractor(ctx DetectionContext) vendorExtractor {
return &jbossExtractor{cxt: ctx}
}
// findDeployedApps is the entry point function to find deployed application on a jboss instance
// It detects if the instance is standalone or part of a cluster (domain). It returns a slice of jeeDeployment and a bool.
// That will be false in case no deployments have been found
func (j jbossExtractor) findDeployedApps(domainHome string) ([]jeeDeployment, bool) {
baseDir, ok := extractJavaPropertyFromArgs(j.cxt.args, jbossHomeDirSysProp)
if !ok {
j.cxt.logger.Debug("jboss: unable to extract the home directory")
return nil, false
}
serverName, domainMode := jbossExtractServerName(j.cxt.args)
if domainMode && len(serverName) == 0 {
j.cxt.logger.Debug("jboss: domain mode with missing server name")
return nil, false
}
configFile := jbossExtractConfigFileName(j.cxt.args, domainMode)
var deployments []jbossServerDeployment
var err error
if domainMode {
var sub fs.FS
baseDir = path.Join(baseDir, jbossDomainBase)
sub, err = j.cxt.fs.Sub(baseDir)
if err != nil {
j.cxt.logger.Debug("jboss: cannot open jboss home", zap.String("path", baseDir), zap.Error(err))
return nil, false
}
deployments, err = jbossDomainFindDeployments(sub, configFile, serverName)
} else {
var sub fs.FS
baseDir = path.Join(baseDir, jbossStandaloneBase)
sub, err = j.cxt.fs.Sub(baseDir)
if err != nil {
j.cxt.logger.Debug("jboss: cannot open jboss home", zap.String("path", baseDir), zap.Error(err))
return nil, false
}
deployments, err = jbossStandaloneFindDeployments(sub, configFile)
}
if err != nil || len(deployments) == 0 {
j.cxt.logger.Debug("jboss: cannot find deployments", zap.Error(err))
return nil, false
}
ret := make([]jeeDeployment, 0, len(deployments))
for _, d := range deployments {
// applications are in /data/xx/yyyyy (xx are the first two chars of the sha1)
ret = append(ret, jeeDeployment{name: d.RuntimeName,
path: path.Join(domainHome, jbossDataDir, jbossContentDir, d.Content.Hash[:2], d.Content.Hash[2:], jbossContentDir),
})
}
return ret, true
}
// customExtractWarContextRoot inspects a war deployment in order to find the jboss-web.xml file under META-INF or WEB-INF.
// If a context root is found it's returned otherwise the function will return "", false
func (j jbossExtractor) customExtractWarContextRoot(warFS fs.FS) (string, bool) {
file, err := warFS.Open(jbossWebXMLFileWebInf)
if err != nil {
// that file can be in WEB-INF or META-INF.
file, err = warFS.Open(jbossWebXMLFileMetaInf)
if err != nil {
return "", false
}
}
defer file.Close()
var jwx jbossWebXML
if xml.NewDecoder(file).Decode(&jwx) != nil || len(jwx.ContextRoot) == 0 {
return "", false
}
return jwx.ContextRoot, true
}
func (j jbossExtractor) defaultContextRootFromFile(fileName string) (string, bool) {
return standardExtractContextFromWarName(fileName)
}
// jbossExtractConfigFileName allows to parse from the args the right filename that's containing the server configuration.
// For standalone, it defaults to standalone.xml, for domain, to domain.xml
func jbossExtractConfigFileName(args []string, domain bool) string {
// determine which long argument look for depending on the mode (standalone or domain)
longArg := jbossServerConfig
defaultConfig := jbossDefaultStandaloneXMLFile
if domain {
longArg = jbossDomainConfig
defaultConfig = jbossDefaultDomainXMLFile
}
for i, a := range args {
if !strings.HasPrefix(a, jbossServerConfigShort) && !strings.HasPrefix(a, longArg) {
continue
}
// the argument can be declared either with space (i.e. `-c conf.xml`) either with = (i.e. `-c=conf.xml`)
if _, val, ok := strings.Cut(a, "="); ok {
// return the right part if separated by an equal
return val
}
// return the next arg if separated by a space
if len(args) > i {
return args[i+1]
}
}
return defaultConfig
}
// jbossDomainFindDeployments finds active deployments for a server in domain mode.
func jbossDomainFindDeployments(basePathFs fs.FS, configFile string, serverName string) ([]jbossServerDeployment, error) {
serverGroup, ok, err := jbossFindServerGroup(basePathFs, serverName)
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("jboss: unable to find server group")
}
file, err := basePathFs.Open(path.Join(jbossConfigDir, configFile))
if err != nil {
return nil, err
}
defer file.Close()
var descriptor jbossDomainXML
err = xml.NewDecoder(file).Decode(&descriptor)
if err != nil {
return nil, err
}
var currentGroup *jbossServerGroup
// find the deployments enabled matching the current server group
for _, group := range descriptor.ServerGroups {
if group.Name == serverGroup {
currentGroup = &group
break
}
}
if currentGroup == nil {
return nil, fmt.Errorf("jboss: unable to locate server group %s into domain.xml", serverGroup)
}
// index deployments for faster lookup
indexed := make(map[string]jbossServerDeployment, len(descriptor.Deployments))
for _, deployment := range descriptor.Deployments {
indexed[deployment.Name] = deployment
}
ret := make([]jbossServerDeployment, 0, len(indexed))
for _, deployment := range currentGroup.Deployments {
if !xmlStringToBool(deployment.Enabled) {
continue
}
value, ok := indexed[deployment.Name]
if !ok {
continue
}
ret = append(ret, value)
}
return ret, nil
}
// jbossStandaloneFindDeployments finds active deployments for a server in standalone mode.
func jbossStandaloneFindDeployments(basePathFs fs.FS, configFile string) ([]jbossServerDeployment, error) {
file, err := basePathFs.Open(path.Join(jbossConfigDir, configFile))
if err != nil {
return nil, err
}
defer file.Close()
var descriptor jbossStandaloneXML
err = xml.NewDecoder(file).Decode(&descriptor)
if err != nil {
return nil, err
}
var ret = make([]jbossServerDeployment, 0, len(descriptor.Deployments))
for _, value := range descriptor.Deployments {
if len(value.Enabled) == 0 || xmlStringToBool(value.Enabled) {
ret = append(ret, value)
}
}
return ret, nil
}
// jbossExtractServerName extracts the server name from the command line.
func jbossExtractServerName(args []string) (string, bool) {
value, ok := extractJavaPropertyFromArgs(args, jbossServerName)
if !ok {
return "", false
}
// the property is on the form -D[Server:servername]. Closing bracket is removed
return value[:len(value)-1], true
}
// jbossFindServerGroup parses host.xml file and finds the server group where the provided serverName belongs to.
// domainFs should be the relative fs rooted to the domain home
func jbossFindServerGroup(domainFs fs.FS, serverName string) (string, bool, error) {
file, err := domainFs.Open(path.Join(jbossConfigDir, jbossHostXMLFile))
if err != nil {
return "", false, err
}
defer file.Close()
decoder := xml.NewDecoder(file)
var decoded jbossHostXML
err = decoder.Decode(&decoded)
if err != nil || len(decoded.Servers) == 0 {
return "", false, err
}
for _, server := range decoded.Servers {
if server.Name == serverName {
return server.Group, true, nil
}
}
return "", false, nil
}
// XMLStringToBool parses string element value and return false if explicitly set to `false` or `0`
func xmlStringToBool(s string) bool {
switch strings.ToLower(s) {
case "0", "false":
return false
}
return true
}