Skip to content

Commit 6c24c54

Browse files
pjanottiCopilot
andauthored
[chore][ta-v2] Add introspection scheme (#7258)
* Initial support for TA introspection * Generate inputs.conf(.spec) from scheme * Fix lint * Avoid macOS AppleDouble inclusion * Add script to run splunk-appinspect * Add splunk-appinspect check to CI * Do not use test file for generating TA v2 inputs.conf global settings * Rename collector TA scheme file * Test TA launcher writing schema when requested * Use generic file concatenation * Avoid test flakiness in Windows smoke test * Surface error if introspection writing fails Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Ensure no white space after "=" for empty values --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6d0cb38 commit 6c24c54

File tree

20 files changed

+1056
-88
lines changed

20 files changed

+1056
-88
lines changed

.github/workflows/splunk-ta-otel.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ jobs:
8383
set -o pipefail
8484
pushd ${{github.workspace}}
8585
make -e bundle.d otelcol
86-
GOOS=windows GOARCH=amd64 make -e otelcol
87-
mv ./bin/otelcol_windows_amd64 ./bin/otelcol_windows_amd64.exe
86+
GOOS=windows GOARCH=amd64 EXTENSION=.exe make -e otelcol
8887
popd
8988
PLATFORM=all make -e generate-technical-addon copy-local-build-to-ta package-ta
9089

.github/workflows/ta-v2.yaml

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ jobs:
5757
set -eo pipefail
5858
make test-ta-v2
5959
60+
- name: Test package TA v2 with splunk-appinspect
61+
working-directory: packaging/ta-v2
62+
run: |
63+
set -eo pipefail
64+
./appinspect.sh ./out/distribution
65+
6066
- name: Upload TA v2 artifacts
6167
uses: actions/upload-artifact@v6
6268
with:
@@ -257,21 +263,27 @@ jobs:
257263
$timeout = 120
258264
$elapsed = 0
259265
$otelcol_pid = $null
260-
Write-Host -NoNewline "Waiting for Splunk_TA_OTel_Collector process: "
266+
Write-Host -NoNewline "Waiting for a single Splunk_TA_OTel_Collector process: "
267+
# Sleep a bit so the introspection launch can terminate before we try to get the PID, otherwise we might get the wrong PID or two PIDs
268+
Start-Sleep -Seconds 5
261269
while ($elapsed -lt $timeout -and $null -eq $otelcol_pid) {
262270
try {
263-
$otelcol_pid = (Get-Process -Name Splunk_TA_OTel_Collector -ErrorAction Stop).Id
264-
if ($null -ne $otelcol_pid) {
271+
# splunkd may briefly launch two instances: one with --scheme for introspection
272+
# and then the actual execution instance. Wait until only one remains.
273+
$procs = @(Get-Process -Name Splunk_TA_OTel_Collector -ErrorAction Stop)
274+
if ($procs.Count -eq 1) {
275+
$otelcol_pid = $procs[0].Id
265276
Write-Host ""
266277
Write-Host "Found Splunk_TA_OTel_Collector PID: $otelcol_pid"
267-
break
268278
}
269279
} catch {
270280
# Process not found, continue waiting
271281
}
272-
Start-Sleep -Seconds 1
273-
$elapsed += 1
274-
Write-Host -NoNewline "."
282+
if ($null -eq $otelcol_pid) {
283+
Start-Sleep -Seconds 1
284+
$elapsed += 1
285+
Write-Host -NoNewline "."
286+
}
275287
}
276288
if ($null -eq $otelcol_pid) {
277289
Write-Host ""

cmd/otelcol/main.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package main
1919

2020
import (
2121
"context"
22+
_ "embed"
2223
"errors"
2324
"fmt"
2425
"log"
@@ -38,18 +39,21 @@ import (
3839
"github.com/signalfx/splunk-otel-collector/pkg/modularinput"
3940
)
4041

42+
const modularinputStanzaPrefix = "Splunk_TA_OTel_Collector://"
43+
44+
//go:embed ta_scheme.xml
45+
var modularInputSchemeXML string
46+
4147
func main() {
4248
runFromCmdLine(os.Args)
4349
}
4450

45-
const modularinputStanzaPrefix = "Splunk_TA_OTel_Collector://"
46-
4751
func runFromCmdLine(args []string) {
4852
// TODO: Use same format as the collector
4953
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
5054

5155
// Handle the cases of running as a TA
52-
err := modularinput.HandleLaunchAsTA(args, os.Stdin, modularinputStanzaPrefix)
56+
err := modularinput.HandleLaunchAsTA(args, os.Stdin, modularinputStanzaPrefix, modularInputSchemeXML)
5357
if err != nil {
5458
if errors.Is(err, modularinput.ErrQueryMode) {
5559
// Query modes (scheme/validate) do not write anything to stdout.

cmd/otelcol/ta_scheme.xml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<scheme>
2+
<title>Splunk Add-on for OpenTelemetry Collector</title>
3+
<description>Deploys the Splunk OpenTelemetry Collector as a modular input for the
4+
Splunk Universal Forwarder</description>
5+
<streaming_mode>simple</streaming_mode>
6+
<use_single_instance>false</use_single_instance>
7+
<endpoint>
8+
<args>
9+
<arg name="splunk_access_token" defaultValue="">
10+
<title>Splunk Access Token</title>
11+
<description>Access token used to send data to Splunk Observability</description>
12+
<data_type>string</data_type>
13+
<required_on_edit>true</required_on_edit>
14+
</arg>
15+
<arg name="splunk_config" defaultValue="$SPLUNK_HOME/etc/apps/Splunk_TA_OTel_Collector/configs/agent_config.yaml">
16+
<title>Splunk Config</title>
17+
<description>Config file that will be used by the Splunk_TA_OTel_Collector</description>
18+
<data_type>string</data_type>
19+
<required_on_edit>true</required_on_edit>
20+
</arg>
21+
<arg name="splunk_realm" defaultValue="us0">
22+
<title>Splunk Realm</title>
23+
<description>Splunk Observability realm to which data will be sent to</description>
24+
<data_type>string</data_type>
25+
<required_on_edit>true</required_on_edit>
26+
</arg>
27+
<arg name="splunk_api_url" defaultValue="https://api.$SPLUNK_REALM.signalfx.com">
28+
<title>Splunk API URL</title>
29+
<description>Specifies the Splunk Observability API endpoint</description>
30+
<data_type>string</data_type>
31+
<required_on_edit>true</required_on_edit>
32+
</arg>
33+
<arg name="splunk_ingest_url" defaultValue="https://ingest.$SPLUNK_REALM.signalfx.com">
34+
<title>Splunk Ingest URL</title>
35+
<description>Specifies the Splunk Observability ingest endpoint</description>
36+
<data_type>string</data_type>
37+
<required_on_edit>true</required_on_edit>
38+
</arg>
39+
<arg name="splunk_listen_interface" defaultValue="localhost">
40+
<title>Splunk Listen Interface</title>
41+
<description>Address for the listening interfaces opened by the
42+
Splunk_TA_OTel_Collector</description>
43+
<data_type>string</data_type>
44+
<required_on_edit>true</required_on_edit>
45+
</arg>
46+
<arg name="splunk_collector_log_level" defaultValue="error">
47+
<title>Splunk Collector Log Level</title>
48+
<description>Specifies the log level to be used by the
49+
Splunk_TA_OTel_Collector</description>
50+
<data_type>string</data_type>
51+
<required_on_edit>true</required_on_edit>
52+
</arg>
53+
</args>
54+
</endpoint>
55+
</scheme>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# ta-inputs-from-schema
2+
3+
A build tool that generates Splunk modular input configuration files (`inputs.conf` and `inputs.conf.spec`) from an XML scheme file.
4+
5+
## Usage
6+
7+
```bash
8+
go run ./cmd/ta-inputs-from-schema \
9+
-scheme <path-to-scheme.xml> \
10+
-global-settings <path-to-global-settings.txt> \
11+
-name <modular-input-name> \
12+
-assets <path-to-assets-directory>
13+
```
14+
15+
### Parameters
16+
17+
- `-scheme`: Path to the XML scheme file (required)
18+
- `-global-settings`: Path to the global settings file containing Splunk input configuration (required)
19+
- `-name`: Name of the modular input (required)
20+
- `-assets`: Path to the assets directory where files will be generated (required)
21+
22+
### Example
23+
24+
```bash
25+
go run ./cmd/ta-inputs-from-schema \
26+
-scheme cmd/otelcol/ta_scheme.xml \
27+
-global-settings cmd/ta-inputs-from-schema/testdata/global_settings.txt \
28+
-name Splunk_TA_OTel_Collector \
29+
-assets packaging/ta-v2/assets
30+
```
31+
32+
This will generate:
33+
- `packaging/ta-v2/assets/default/inputs.conf`
34+
- `packaging/ta-v2/assets/README/inputs.conf.spec`
35+
36+
## Input Files
37+
38+
### XML Scheme File
39+
40+
The XML scheme file defines the modular input arguments. Example:
41+
42+
```xml
43+
<scheme>
44+
<title>Splunk Add-on for OpenTelemetry Collector</title>
45+
<description>Deploys the Splunk OpenTelemetry Collector as a modular input</description>
46+
<endpoint>
47+
<args>
48+
<arg name="splunk_access_token" defaultValue="">
49+
<title>Splunk Access Token</title>
50+
<description>Access token used to send data to Splunk Observability</description>
51+
<data_type>string</data_type>
52+
<required_on_edit>true</required_on_edit>
53+
</arg>
54+
</args>
55+
</endpoint>
56+
</scheme>
57+
```
58+
59+
### Global Settings File
60+
61+
A plain text file containing Splunk input global settings. Example:
62+
63+
```
64+
# Global settings
65+
disabled=false
66+
start_by_shell=false
67+
interval = 0
68+
index = _internal
69+
sourcetype = Splunk_TA_OTel_Collector
70+
```
71+
72+
## Output Files
73+
74+
### inputs.conf
75+
76+
Generated at `<assets>/default/inputs.conf`, this file contains the default configuration for the modular input with all arguments set to their default values.
77+
78+
### inputs.conf.spec
79+
80+
Generated at `<assets>/README/inputs.conf.spec`, this file contains the specification describing all available configuration parameters, their requirements, and default values.
81+
82+
## Testing
83+
84+
Run the unit tests:
85+
86+
```bash
87+
go test ./cmd/ta-inputs-from-schema/... -v
88+
```
89+
90+
## Development
91+
92+
The tool is structured into several files:
93+
94+
- `main.go`: Entry point and command-line argument parsing
95+
- `parser.go`: XML scheme parsing
96+
- `generator.go`: Configuration file generation
97+
- `parser_test.go`: Tests for XML parsing
98+
- `generator_test.go`: Tests for file generation
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright Splunk, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
)
21+
22+
// generateInputsConf generates the inputs.conf file content
23+
func generateInputsConf(scheme *Scheme, globalSettings, inputName string) string {
24+
var sb strings.Builder
25+
26+
// Write header with input name
27+
sb.WriteString(fmt.Sprintf("[%s://%s]\n\n", inputName, inputName))
28+
29+
// Write global settings
30+
sb.WriteString(globalSettings)
31+
if !strings.HasSuffix(globalSettings, "\n") {
32+
sb.WriteString("\n")
33+
}
34+
35+
// Write TA specific settings header
36+
sb.WriteString("\n# TA specific settings\n")
37+
38+
// Write each argument with its default value
39+
for _, arg := range scheme.Endpoint.Args {
40+
if arg.DefaultValue == "" {
41+
sb.WriteString(arg.Name + " =\n")
42+
} else {
43+
sb.WriteString(arg.Name + " = " + arg.DefaultValue + "\n")
44+
}
45+
}
46+
47+
return sb.String()
48+
}
49+
50+
// generateInputsConfSpec generates the inputs.conf.spec file content
51+
func generateInputsConfSpec(scheme *Scheme, inputName string) string {
52+
var sb strings.Builder
53+
54+
// Write header with input name
55+
sb.WriteString(fmt.Sprintf("[%s://<name>]\n\n", inputName))
56+
57+
// Write each argument specification
58+
for _, arg := range scheme.Endpoint.Args {
59+
// Argument name and value placeholder
60+
sb.WriteString(arg.Name + " = <value>\n")
61+
62+
// Description (normalize whitespace)
63+
desc := normalizeDescription(arg.Description)
64+
65+
sb.WriteString("* " + desc + "\n")
66+
67+
// Default value
68+
if arg.DefaultValue == "" {
69+
sb.WriteString("* Default =\n")
70+
} else {
71+
sb.WriteString("* Default = " + arg.DefaultValue + "\n")
72+
}
73+
74+
sb.WriteString("\n")
75+
}
76+
77+
return sb.String()
78+
}
79+
80+
// normalizeDescription normalizes the description by removing extra whitespace
81+
func normalizeDescription(desc string) string {
82+
// Replace multiple whitespace (including newlines) with single space
83+
return strings.Join(strings.Fields(desc), " ")
84+
}

0 commit comments

Comments
 (0)