diff --git a/packaging/technical-addon/Makefile b/packaging/technical-addon/Makefile index 3970429975..f578206e19 100644 --- a/packaging/technical-addon/Makefile +++ b/packaging/technical-addon/Makefile @@ -8,6 +8,9 @@ SPLUNK_OTELCOL_DOWNLOAD_BASE?=https://github.com/signalfx/splunk-otel-collector/ PLATFORM?=linux ARCH?=amd64 +# include autoinstrumentation +AUTOINSTRUMENTATION_DIR:=$(SRC_ROOT)/instrumentation + # Used for testing & validation ORCA_CLOUD?=kubernetes UF_VERSION?=8.2.7 @@ -29,15 +32,16 @@ MODINPUT_CONFIG_GENERATOR := $(TOOLS_DIR)/modinput_config_generator ta-build-tools: @echo "Building tools..." @mkdir -p $(TOOLS_DIR) - go build -o $(MODINPUT_CONFIG_GENERATOR) ./cmd/modinput_config_generator + cd $(ADDONS_SOURCE_DIR) && go build -o $(MODINPUT_CONFIG_GENERATOR) ./cmd/modinput_config_generator .PHONY: test-ta-build-tools test-ta-build-tools: ta-build-tools $(MODINPUT_CONFIG_GENERATOR) -source-dir=$(ADDONS_SOURCE_DIR)/cmd/modinput_config_generator/internal/testdata -schema-name=Sample_Addon --build-dir=$(BUILD_DIR) || exit 1; \ mkdir -p $(BUILD_DIR)/Sample_Addon/$(PLATFORM)_$(SPLUNK_ARCH)/bin; \ - go build $(GO_BUILD_FLAGS) -o $(BUILD_DIR)/Sample_Addon/$(PLATFORM)_$(SPLUNK_ARCH)/bin/Sample_Addon $(ADDONS_SOURCE_DIR)/cmd/modinput_config_generator/internal/testdata/pkg/sample_addon/runner || exit 1; \ + cd $(ADDONS_SOURCE_DIR) && go build $(GO_BUILD_FLAGS) -o $(BUILD_DIR)/Sample_Addon/$(PLATFORM)_$(SPLUNK_ARCH)/bin/Sample_Addon $(ADDONS_SOURCE_DIR)/cmd/modinput_config_generator/internal/testdata/pkg/sample_addon/runner || exit 1; \ echo "built addon, testing..." - go test -v ./... + cd $(ADDONS_SOURCE_DIR) && go test -v `go list ./... | grep --invert-match pkg` + .PHONY: gen-modinput-config gen-modinput-config: @@ -45,11 +49,16 @@ gen-modinput-config: @for schema in $(MODULAR_INPUT_SCHEMAS); do \ schema_lower=$$(echo $$schema | tr '[:upper:]' '[:lower:]'); \ echo "Generating config and TA scaffold for $$schema..."; \ + rm -rf $(BUILD_DIR)/$$schema/; \ mkdir -p $(BUILD_DIR)/$$schema/; \ $(MODINPUT_CONFIG_GENERATOR) -source-dir=$(ADDONS_SOURCE_DIR) -schema-name=$$schema --build-dir=$(BUILD_DIR) || exit 1; \ ls -lAh $(BUILD_DIR); \ done +.PHONY: build-tas +build-tas: gen-modinput-config ta-build-deps-all build-ta-runners + +.PHONY: build-ta-runners build-ta-runners: @echo "Building runner binaries..." @for schema in $(MODULAR_INPUT_SCHEMAS); do \ @@ -65,6 +74,21 @@ build-ta-runners: done \ done +.PHONY: generate-technical-addon-linux-autoinstrumentation +generate-technical-addon-linux-autoinstrumentation: + cd $(AUTOINSTRUMENTATION_DIR) && $(MAKE) dist + mkdir -p $(BUILD_DIR)/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin; + mkdir -p $(BUILD_DIR) + PLATFORM="all" \ + BUILD_DIR="$(BUILD_DIR)" \ + SOURCE_DIR="$(ADDONS_SOURCE_DIR)" \ + AUTOINSTRUMENTATION_DIR="$(AUTOINSTRUMENTATION_DIR)" \ + $(ADDONS_SOURCE_DIR)/packaging-scripts/download-autoinstrumentation.sh + + +.PHONY: ta-build-deps-all +ta-build-deps-all: generate-technical-addon-linux-autoinstrumentation + .PHONY: generate-technical-addon generate-technical-addon: env-guard-all BUILD_DIR="$(BUILD_DIR)" \ diff --git a/packaging/technical-addon/README.md b/packaging/technical-addon/README.md index 39ec15c9f3..ebce4572af 100644 --- a/packaging/technical-addon/README.md +++ b/packaging/technical-addon/README.md @@ -36,3 +36,9 @@ Further, they may remove the agent bundle downloaded to the `bin/` folder in the As with all TAs, any changes made to `configs` will be overwritten. Customers should copy any relevant custom configuration from `configs/` or `defaults/` to `local/`. + +# Autoinstrumentation +For all, you may run `build-linux-autoinstrumentation-ta`. +For specific targets, you may run +1. `make generate-technical-addon-linux-autoinstrumentation` (makes autoinstrumentation and dependencies) +2. `make gen-modinput-config && make build-ta-runners` diff --git a/packaging/technical-addon/cmd/modinput_config_generator/generator_test.go b/packaging/technical-addon/cmd/modinput_config_generator/generator_test.go index 624a1fa779..df361a3528 100644 --- a/packaging/technical-addon/cmd/modinput_config_generator/generator_test.go +++ b/packaging/technical-addon/cmd/modinput_config_generator/generator_test.go @@ -23,6 +23,8 @@ import ( "testing" "time" + "github.com/splunk/splunk-technical-addon/internal/testaddon" + "github.com/splunk/splunk-technical-addon/internal/packaging" "github.com/splunk/splunk-technical-addon/internal/testcommon" "github.com/stretchr/testify/assert" @@ -81,7 +83,7 @@ func TestRunner(t *testing.T) { require.NotEmpty(t, buildDir) err := packaging.PackageAddon(filepath.Join(buildDir, "Sample_Addon"), addonPath) require.NoError(t, err) - tc := testcommon.StartSplunk(t, testcommon.SplunkStartOpts{ + tc := testaddon.StartSplunk(t, testaddon.SplunkStartOpts{ AddonPaths: []string{addonPath}, WaitStrategy: wait.ForExec([]string{"sudo", "stat", "/opt/splunk/var/log/splunk/Sample_Addon.log"}).WithStartupTimeout(time.Minute * 4), }) diff --git a/packaging/technical-addon/internal/modularinput/spec.go b/packaging/technical-addon/internal/modularinput/spec.go index 5d759e4ee7..fe05549a66 100644 --- a/packaging/technical-addon/internal/modularinput/spec.go +++ b/packaging/technical-addon/internal/modularinput/spec.go @@ -40,7 +40,6 @@ type GenericModularInput struct { ModularInputs map[string]*ModInput SchemaName string } - type TemplateData struct { ModularInputs map[string]ModInputConfig `yaml:"modular-inputs"` SchemaName string `yaml:"modular-input-schema-name"` diff --git a/packaging/technical-addon/internal/modularinput/spec_test.go b/packaging/technical-addon/internal/modularinput/spec_test.go new file mode 100644 index 0000000000..0398a47d58 --- /dev/null +++ b/packaging/technical-addon/internal/modularinput/spec_test.go @@ -0,0 +1,49 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package modularinput + +import ( + "path/filepath" + "testing" + + "github.com/splunk/splunk-technical-addon/internal/testcommon" + "github.com/stretchr/testify/require" +) + +func TestLoadConfig(t *testing.T) { + actual, err := LoadConfig("./testdata/sample-modular-inputs.yaml") + require.NoError(t, err) + require.EqualValues(t, + &TemplateData{ + ModularInputs: map[string]ModInputConfig{ + "everything_set": {Description: "SET ALL THE THINGS", Default: "$SPLUNK_OTEL_TA_HOME/local/access_token", Flag: Flag{Name: "test-flag", IsUnary: false}, Required: false, PassthroughEnvVar: true, ReplaceableEnvVar: true}, + "minimal_set": {Description: "This is all you need", Default: "", Flag: Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + "minimal_set_required": {Description: "hello", Default: "", Flag: Flag{Name: "", IsUnary: false}, Required: true, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + "unary_flag_with_everything_set": {Description: "Unary flags don't take arguments/values and are either present or not", Default: "$SPLUNK_OTEL_TA_HOME/local/access_token", Flag: Flag{Name: "test-flag", IsUnary: true}, Required: false, PassthroughEnvVar: true, ReplaceableEnvVar: true}, + }, + SchemaName: "Sample_Addon", + Version: "1.2.3", + }, actual) + +} + +func TestRenderTemplate(t *testing.T) { + sampleTemplateData, err := LoadConfig("./testdata/sample-modular-inputs.yaml") + require.NoError(t, err) + actualRender := filepath.Join(t.TempDir(), "render_template.txt") + err = RenderTemplate("./testdata/sample-template.tmpl", actualRender, sampleTemplateData) + require.NoError(t, err) + testcommon.AssertFilesMatch(t, "./testdata/expected-template-rendered.txt", actualRender) +} diff --git a/packaging/technical-addon/internal/modularinput/testdata/expected-template-rendered.txt b/packaging/technical-addon/internal/modularinput/testdata/expected-template-rendered.txt new file mode 100644 index 0000000000..ae639493d4 --- /dev/null +++ b/packaging/technical-addon/internal/modularinput/testdata/expected-template-rendered.txt @@ -0,0 +1,9 @@ +[Sample_Addon://Sample_Addon] +disabled=false +start_by_shell=false +interval=60 +index=_internal +sourcetype=Sample_Addon +everything_set=$SPLUNK_OTEL_TA_HOME/local/access_token +minimal_set_required= +unary_flag_with_everything_set=$SPLUNK_OTEL_TA_HOME/local/access_token diff --git a/packaging/technical-addon/internal/modularinput/testdata/sample-modular-inputs.yaml b/packaging/technical-addon/internal/modularinput/testdata/sample-modular-inputs.yaml new file mode 100644 index 0000000000..4f57a6af4f --- /dev/null +++ b/packaging/technical-addon/internal/modularinput/testdata/sample-modular-inputs.yaml @@ -0,0 +1,28 @@ +modular-input-schema-name: Sample_Addon +version: "1.2.3" +modular-inputs: + everything_set: + description: "SET ALL THE THINGS" + default: "$SPLUNK_OTEL_TA_HOME/local/access_token" + passthrough: true + replaceable: true + flag: + name: "test-flag" + is-unary: false + + minimal_set: + description: "This is all you need" + + unary_flag_with_everything_set: + description: "Unary flags don't take arguments/values and are either present or not" + default: "$SPLUNK_OTEL_TA_HOME/local/access_token" + passthrough: true + replaceable: true + flag: + name: "test-flag" + is-unary: true + + minimal_set_required: + description: "hello" + required: true + \ No newline at end of file diff --git a/packaging/technical-addon/internal/modularinput/testdata/sample-template.tmpl b/packaging/technical-addon/internal/modularinput/testdata/sample-template.tmpl new file mode 100644 index 0000000000..9f0c4630e7 --- /dev/null +++ b/packaging/technical-addon/internal/modularinput/testdata/sample-template.tmpl @@ -0,0 +1,14 @@ +[{{ .SchemaName }}://{{ .SchemaName }}] +disabled=false +start_by_shell=false +interval=60 +index=_internal +sourcetype={{ .SchemaName }} + +{{- range $name, $inputConfig := .ModularInputs }} +{{- if $inputConfig.Default }} +{{ $name }}={{ $inputConfig.Default }} +{{- else if $inputConfig.Required }} +{{ $name }}= +{{- end }} +{{- end }} diff --git a/packaging/technical-addon/internal/packaging/targz.go b/packaging/technical-addon/internal/packaging/targz.go index 1632ce4ccb..99c6f80fcc 100644 --- a/packaging/technical-addon/internal/packaging/targz.go +++ b/packaging/technical-addon/internal/packaging/targz.go @@ -17,9 +17,11 @@ package packaging import ( "archive/tar" "compress/gzip" + "fmt" "io" "os" "path/filepath" + "strings" ) func PackageAddon(sourceDir, outputFile string) error { @@ -76,3 +78,84 @@ func PackageAddon(sourceDir, outputFile string) error { return err } + +// ExtractAddon extracts a .tar.gz file from sourcePath to destinationPath +func ExtractAddon(sourcePath, destinationPath string) error { + file, err := os.Open(sourcePath) + if err != nil { + return fmt.Errorf("failed to open archive: %w", err) + } + defer file.Close() + + gzipReader, err := gzip.NewReader(file) + if err != nil { + return fmt.Errorf("failed to create gzip reader: %w", err) + } + defer gzipReader.Close() + + tarReader := tar.NewReader(gzipReader) + + for { + header, err := tarReader.Next() + if err == io.EOF { + // End of archive + break + } + if err != nil { + return fmt.Errorf("error reading tar header: %w", err) + } + + // Create the full path for the file + target := filepath.Join(destinationPath, header.Name) // #nosec G305 + + // Mitigation for gosec G305 + if !isInDirectory(target, destinationPath) { + return fmt.Errorf("illegal path traversal attempt: %s", header.Name) + } + + switch header.Typeflag { + case tar.TypeDir: + if err := os.MkdirAll(target, header.FileInfo().Mode()); err != nil { + return fmt.Errorf("failed to create directory %s: %w", target, err) + } + + case tar.TypeReg: + if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { + return fmt.Errorf("failed to create directory for file %s: %w", target, err) + } + + file, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, header.FileInfo().Mode()) + if err != nil { + return fmt.Errorf("failed to create file %s: %w", target, err) + } + + // #nosec G110 We only use this for packaging on our systems, this module not included for code in addon itself + if _, err := io.Copy(file, tarReader); err != nil { + file.Close() + return fmt.Errorf("failed to write file %s: %w", target, err) + } + file.Close() + + default: + fmt.Printf("Skipping unsupported file type: %c for %s\n", header.Typeflag, header.Name) + } + } + + return nil +} + +// isInDirectory checks if the path is inside the specified directory (prevents path traversal) +func isInDirectory(path, directory string) bool { + // Convert to absolute paths + absPath, err := filepath.Abs(path) + if err != nil { + return false + } + absDir, err := filepath.Abs(directory) + if err != nil { + return false + } + + // Check if the path is within the directory + return strings.HasPrefix(absPath, absDir) +} diff --git a/packaging/technical-addon/internal/packaging/testdata/sampletosha.txt b/packaging/technical-addon/internal/packaging/testdata/sampletosha.txt new file mode 100644 index 0000000000..b45ef6fec8 --- /dev/null +++ b/packaging/technical-addon/internal/packaging/testdata/sampletosha.txt @@ -0,0 +1 @@ +Hello, World! \ No newline at end of file diff --git a/packaging/technical-addon/internal/packaging/validation.go b/packaging/technical-addon/internal/packaging/validation.go new file mode 100644 index 0000000000..e17916d865 --- /dev/null +++ b/packaging/technical-addon/internal/packaging/validation.go @@ -0,0 +1,36 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package packaging + +import ( + "crypto/sha256" + "encoding/hex" + "io" + "os" + "strings" +) + +func Sha256Sum(filePath string) (string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", err + } + data, err := io.ReadAll(file) + if err != nil { + return "", err + } + sum := sha256.Sum256([]byte(strings.TrimSpace(string(data)))) + return hex.EncodeToString(sum[:]), nil +} diff --git a/packaging/technical-addon/internal/packaging/validation_test.go b/packaging/technical-addon/internal/packaging/validation_test.go new file mode 100644 index 0000000000..fb44df2daf --- /dev/null +++ b/packaging/technical-addon/internal/packaging/validation_test.go @@ -0,0 +1,29 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package packaging + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSha256(t *testing.T) { + sum, err := Sha256Sum("./testdata/sampletosha.txt") + require.NoError(t, err) + // Expected generated with linux sha256sum utility + assert.Equal(t, "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", sum) +} diff --git a/packaging/technical-addon/internal/testcommon/splunk.go b/packaging/technical-addon/internal/testaddon/splunk.go similarity index 72% rename from packaging/technical-addon/internal/testcommon/splunk.go rename to packaging/technical-addon/internal/testaddon/splunk.go index 0eeb822b8a..cbb31f2e44 100644 --- a/packaging/technical-addon/internal/testcommon/splunk.go +++ b/packaging/technical-addon/internal/testaddon/splunk.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package testcommon +package testaddon import ( "context" @@ -26,36 +26,21 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/pkg/fileutils" - "github.com/google/go-cmp/cmp" "github.com/splunk/splunk-technical-addon/internal/modularinput" "github.com/splunk/splunk-technical-addon/internal/packaging" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" "go.uber.org/zap" ) -func AssertFilesMatch(tt *testing.T, expectedPath string, actualPath string) { - require.FileExists(tt, actualPath) - require.FileExists(tt, expectedPath) - expected, err := os.ReadFile(expectedPath) - if err != nil { - tt.Fatalf("Failed to read expected file: %v", err) - } - - actual, err := os.ReadFile(actualPath) - if err != nil { - tt.Fatalf("Failed to read actual file: %v", err) - } - - if diff := cmp.Diff(string(expected), string(actual)); diff != "" { - tt.Errorf("File contents mismatch (-expected +actual)\npaths: (%s, %s):\n%s", expectedPath, actualPath, diff) - } -} - type SplunkStartOpts struct { WaitStrategy wait.Strategy + SplunkUser string + SplunkGroup string AddonPaths []string + Timeout time.Duration } func StartSplunk(t *testing.T, startOpts SplunkStartOpts) testcontainers.Container { @@ -74,10 +59,20 @@ func StartSplunk(t *testing.T, startOpts SplunkStartOpts) testcontainers.Contain require.NoError(t, err) addonNames = append(addonNames, filepath.Join(containerAddonsDir, addonFileName)) } + if startOpts.SplunkUser == "" { + startOpts.SplunkUser = "splunk" + } + if startOpts.SplunkGroup == "" { + startOpts.SplunkGroup = "splunk" + } + if startOpts.Timeout == 0 { + startOpts.Timeout = 10 * time.Minute + } splunkStartURL := strings.Join(addonNames, ",") + t.Logf("Local addons packaged under: %s", localAddonsDir) t.Logf("Splunk start url: %s", splunkStartURL) req := testcontainers.ContainerRequest{ - Image: "splunk/splunk:latest", + Image: "splunk/splunk:9.4.1", HostConfigModifier: func(c *container.HostConfig) { c.NetworkMode = "host" c.Mounts = append(c.Mounts, mount.Mount{ @@ -85,17 +80,19 @@ func StartSplunk(t *testing.T, startOpts SplunkStartOpts) testcontainers.Contain Target: containerAddonsDir, Type: mount.TypeBind, }) - c.AutoRemove = false // change to false for debugging + c.AutoRemove = true // change to false for debugging }, Env: map[string]string{ "SPLUNK_START_ARGS": "--accept-license", "SPLUNK_PASSWORD": "Chang3d!", "SPLUNK_APPS_URL": splunkStartURL, + "SPLUNK_USER": startOpts.SplunkUser, + "SPLUNK_GROUP": startOpts.SplunkGroup, }, WaitingFor: wait.ForAll( - wait.NewHTTPStrategy("/en-US/account/login").WithPort("8000"), + wait.NewHTTPStrategy("/en-US/account/login").WithPort("8000").WithStartupTimeout(startOpts.Timeout), startOpts.WaitStrategy, - ).WithStartupTimeoutDefault(4 * time.Minute).WithDeadline(4*time.Minute + 20*time.Second), + ).WithStartupTimeoutDefault(startOpts.Timeout).WithDeadline(startOpts.Timeout + 20*time.Second), LogConsumerCfg: &testcontainers.LogConsumerConfig{ Consumers: []testcontainers.LogConsumer{&testLogConsumer{t: t}}, }, @@ -108,13 +105,13 @@ func StartSplunk(t *testing.T, startOpts SplunkStartOpts) testcontainers.Contain if err != nil { logger.Error("error starting up splunk") } - // Uncomment this line if you'd like to debug the container + // time.Sleep(20 * time.Minute) + // Then, run the following to inspect // docker container ls --all // Grab id of splunk container // docker exec -it $container_id bash // See README.md in this package for more info - // time.Sleep(20 * time.Minute) require.NoError(t, err) return tc } @@ -127,7 +124,7 @@ func (l *testLogConsumer) Accept(log testcontainers.Log) { l.t.Log(log.LogType + ": " + strings.TrimSpace(string(log.Content))) } -type RepackFunc func(t *testing.T, addonPath string) error +type RepackFunc func(t *testing.T, addonDir string) error func PackAddon(t *testing.T, defaultModInputs *modularinput.GenericModularInput, repackFunc RepackFunc) string { packedDir := filepath.Join(t.TempDir(), defaultModInputs.SchemaName) @@ -147,3 +144,21 @@ func PackAddon(t *testing.T, defaultModInputs *modularinput.GenericModularInput, return addonPath } + +func AssertFileShasEqual(t *testing.T, expected string, actual string) { + expectedSum, err := packaging.Sha256Sum(expected) + require.NoError(t, err) + actualSum, err := packaging.Sha256Sum(actual) + require.NoError(t, err) + assert.Equal(t, expectedSum, actualSum) +} + +func RepackAddon(t *testing.T, sourceAddonPath string, repackFunc RepackFunc) string { + repackedDir := t.TempDir() + err := packaging.ExtractAddon(sourceAddonPath, repackedDir) + require.NoError(t, err) + require.NoError(t, repackFunc(t, repackedDir)) + repackedAddon := filepath.Join(t.TempDir(), filepath.Base(sourceAddonPath)) + require.NoError(t, packaging.PackageAddon(repackedDir, repackedAddon)) + return repackedAddon +} diff --git a/packaging/technical-addon/internal/testcommon/utils.go b/packaging/technical-addon/internal/testcommon/utils.go new file mode 100644 index 0000000000..5019174083 --- /dev/null +++ b/packaging/technical-addon/internal/testcommon/utils.go @@ -0,0 +1,41 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testcommon + +import ( + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" +) + +func AssertFilesMatch(tt *testing.T, expectedPath string, actualPath string) { + require.FileExists(tt, actualPath) + require.FileExists(tt, expectedPath) + expected, err := os.ReadFile(expectedPath) + if err != nil { + tt.Fatalf("Failed to read expected file: %v", err) + } + + actual, err := os.ReadFile(actualPath) + if err != nil { + tt.Fatalf("Failed to read actual file: %v", err) + } + + if diff := cmp.Diff(string(expected), string(actual)); diff != "" { + tt.Errorf("File contents mismatch (-expected +actual)\npaths: (%s, %s):\n%s", expectedPath, actualPath, diff) + } +} diff --git a/packaging/technical-addon/packaging-scripts/download-autoinstrumentation.sh b/packaging/technical-addon/packaging-scripts/download-autoinstrumentation.sh new file mode 100755 index 0000000000..e0ce380975 --- /dev/null +++ b/packaging/technical-addon/packaging-scripts/download-autoinstrumentation.sh @@ -0,0 +1,24 @@ +#!/bin/bash -eux +set -o pipefail + +[[ -z "$AUTOINSTRUMENTATION_DIR" ]] && echo "AUTOINSTRUMENTATION_DIR not set" && exit 1 +[[ -z "$SOURCE_DIR" ]] && echo "SOURCE_DIR not set" && exit 1 + +JAVA_VERSION="$(cat "${AUTOINSTRUMENTATION_DIR}/packaging/java-agent-release.txt")" +NODEJS_VERSION="$(cat "${AUTOINSTRUMENTATION_DIR}/packaging/nodejs-agent-release.txt")" + +if [ "$PLATFORM" == "linux" ] || [ "$PLATFORM" == "all" ]; then + mkdir -p "${BUILD_DIR}/Splunk_TA_otel_linux_autoinstrumentation/config" + # Copy our .so, which is made in the build step of the autoinstrumentation Makefile + cp "${AUTOINSTRUMENTATION_DIR}/dist/libsplunk_amd64.so" "${BUILD_DIR}/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/libsplunk_amd64.so" + + nodejs_agent_path="${BUILD_DIR}/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-${NODEJS_VERSION#v}.tgz" + java_agent_path="${BUILD_DIR}/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-javaagent.jar" + wget --timestamping "https://github.com/signalfx/splunk-otel-js/releases/download/${NODEJS_VERSION}/splunk-otel-${NODEJS_VERSION#v}.tgz" --output-document "$nodejs_agent_path" + wget --timestamping "https://github.com/signalfx/splunk-otel-java/releases/download/${JAVA_VERSION}/splunk-otel-javaagent.jar" --output-document "$java_agent_path" + # Needed for go:embed + echo "$JAVA_VERSION" > "${SOURCE_DIR}/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-release.txt" + echo "$NODEJS_VERSION" > "${SOURCE_DIR}/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-release.txt" + sha256sum "$java_agent_path" | cut -d' ' --fields=1 > "${SOURCE_DIR}/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-sha256sum.txt" + sha256sum "$nodejs_agent_path" | cut -d' ' --fields=1 > "${SOURCE_DIR}/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-sha256sum.txt" +fi diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/README.md b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/README.md new file mode 100644 index 0000000000..d1e2b932df --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/README.md @@ -0,0 +1 @@ +Re-implements [linux autoinstrumentation](https://docs.splunk.com/observability/en/gdi/opentelemetry/automatic-discovery/linux/linux-backend.html#linux-backend-auto-discovery) installation from the splunk otel [installer script](https://dl.signalfx.com/splunk-otel-collector.sh) diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/README/inputs.conf.spec.tmpl b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/README/inputs.conf.spec.tmpl new file mode 100644 index 0000000000..c05b8c628f --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/README/inputs.conf.spec.tmpl @@ -0,0 +1,5 @@ +[{{.SchemaName}}://] +{{ range $name, $config := .ModularInputs }} +{{ $name }} = +* {{ $config.Description }}{{ if $config.Default }} (Default: {{$config.Default}} ){{ end }} +{{ end }} \ No newline at end of file diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/config/instrumentation.conf b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/config/instrumentation.conf new file mode 100644 index 0000000000..d7229bbd7b --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/config/instrumentation.conf @@ -0,0 +1,11 @@ +java_agent_jar=/usr/lib/splunk-instrumentation/splunk-otel-javaagent.jar +#resource_attributes=deployment.environment=my.environment +#service_name=hardcoded.service + +# note: any of the the following lines may be uncommented to override defaults +#disable_telemetry=true +#generate_service_name=false +#enable_profiler=true +#enable_profiler_memory=true +#enable_metrics=true + diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/app.conf.tmpl b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/app.conf.tmpl new file mode 100644 index 0000000000..b9e7672b90 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/app.conf.tmpl @@ -0,0 +1,28 @@ +###################################################### +# +# {{ .SchemaName }} +# +# Copyright (C) 2005-2020 Splunk Inc. All Rights Reserved. +# +###################################################### + +[install] +state = enabled +is_configured = false +build = 0 + +[ui] +is_visible = false +label = Splunk Add-on for Opentelemetry Linux Autoinstrumentation (java, nodejs) + +[launcher] +author = Splunk, Inc. +description = Linux autoinstrumentation for splunk addons +version = {{ .Version }} + +[package] +id = {{ .SchemaName }} + +[id] +name = {{ .SchemaName }} +version = {{ .Version }} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/inputs.conf.tmpl b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/inputs.conf.tmpl new file mode 100644 index 0000000000..9f0c4630e7 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/assets/default/inputs.conf.tmpl @@ -0,0 +1,14 @@ +[{{ .SchemaName }}://{{ .SchemaName }}] +disabled=false +start_by_shell=false +interval=60 +index=_internal +sourcetype={{ .SchemaName }} + +{{- range $name, $inputConfig := .ModularInputs }} +{{- if $inputConfig.Default }} +{{ $name }}={{ $inputConfig.Default }} +{{- else if $inputConfig.Required }} +{{ $name }}= +{{- end }} +{{- end }} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/expected/expected-zeroconfig.txt b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/expected/expected-zeroconfig.txt new file mode 100644 index 0000000000..3593f324cf --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/expected/expected-zeroconfig.txt @@ -0,0 +1,5 @@ +JAVA_TOOL_OPTIONS=-javaagent:/foo/bar +OTEL_RESOURCE_ATTRIBUTES=splunk.zc.method=splunk-otel-auto-instrumentation-v2.15.0,asdasd +SPLUNK_PROFILER_ENABLED=false +SPLUNK_PROFILER_MEMORY_ENABLED=false +SPLUNK_METRICS_ENABLED=false \ No newline at end of file diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/local/inputs.conf b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/local/inputs.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/local/ta-agent-config.yaml b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/local/ta-agent-config.yaml new file mode 100644 index 0000000000..cc649d8ae5 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/local/ta-agent-config.yaml @@ -0,0 +1,138 @@ +# Default configuration file for the Linux (deb/rpm) and Windows MSI collector packages + +# If the collector is installed without the Linux/Windows installer script, the following +# environment variables are required to be manually defined or configured below: +# - SPLUNK_ACCESS_TOKEN: The Splunk access token to authenticate requests +# - SPLUNK_API_URL: The Splunk API URL, e.g. https://api.us0.signalfx.com +# - SPLUNK_BUNDLE_DIR: The path to the Smart Agent bundle, e.g. /usr/lib/splunk-otel-collector/agent-bundle +# - SPLUNK_COLLECTD_DIR: The path to the collectd config directory for the Smart Agent, e.g. /usr/lib/splunk-otel-collector/agent-bundle/run/collectd +# - SPLUNK_INGEST_URL: The Splunk ingest URL, e.g. https://ingest.us0.signalfx.com +# - SPLUNK_LISTEN_INTERFACE: The network interface the agent receivers listen on. + +extensions: + headers_setter: + headers: + - action: upsert + key: X-SF-TOKEN + from_context: X-SF-TOKEN + default_value: "${SPLUNK_ACCESS_TOKEN}" + health_check: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:13133" + http_forwarder: + ingress: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:6060" + egress: + endpoint: "${env:SPLUNK_API_URL}" + zpages: + #endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:55679" + +receivers: + fluentforward: + endpoint: 127.0.0.1:8006 + hostmetrics: + collection_interval: 10s + scrapers: + cpu: + disk: + filesystem: + memory: + network: + # System load average metrics https://en.wikipedia.org/wiki/Load_(computing) + load: + # Paging/Swap space utilization and I/O metrics + paging: + # Aggregated system process count metrics + processes: + # System processes metrics, disabled by default + # process: + jaeger: + protocols: + grpc: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:14250" + thrift_binary: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:6832" + thrift_compact: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:6831" + thrift_http: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:14268" + otlp: + protocols: + grpc: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:4317" + # Uncomment below config to preserve incoming access token and use it instead of the token value set in exporter config + # include_metadata: true + http: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:4318" + # Uncomment below config to preserve incoming access token and use it instead of the token value set in exporter config + # include_metadata: true + # This section is used to collect the OpenTelemetry Collector metrics + # Even if just a Splunk APM customer, these metrics are included + prometheus/internal: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: ["${env:SPLUNK_LISTEN_INTERFACE}:8888"] + metric_relabel_configs: + - source_labels: [ __name__ ] + regex: '.*grpc_io.*' + action: drop + smartagent/processlist: + type: processlist + signalfx: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:9943" + # Whether to preserve incoming access token and use instead of exporter token + # default = false + #access_token_passthrough: true + zipkin: + endpoint: "${env:SPLUNK_LISTEN_INTERFACE}:9411" + +processors: + batch: + metadata_keys: + - X-SF-Token + # Enabling the memory_limiter is strongly recommended for every pipeline. + # Configuration is based on the amount of memory allocated to the collector. + # For more information about memory limiter, see + # https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/memorylimiter/README.md + memory_limiter: + check_interval: 2s + limit_mib: ${env:SPLUNK_MEMORY_LIMIT_MIB} + resourcedetection: + detectors: [gcp, ecs, ec2, azure, system] + override: true + resource/telemetry: + attributes: + - action: insert + key: splunk.distribution + value: otel-ta-test + +exporters: + # Debug + debug: + verbosity: detailed + +service: + extensions: [headers_setter, health_check, http_forwarder, zpages, smartagent] + pipelines: + traces: + receivers: [jaeger, otlp, zipkin] + processors: + - memory_limiter + - batch + - resourcedetection + #- resource/add_environment + exporters: [otlphttp, signalfx] + metrics: + receivers: [hostmetrics, otlp, signalfx] + processors: [memory_limiter, batch, resourcedetection] + exporters: [debug] + logs/signalfx: + receivers: [signalfx, smartagent/processlist] + processors: [memory_limiter, batch, resourcedetection] + exporters: [debug] + metrics/telemetry: + receivers: [prometheus/internal] + processors: [memory_limiter, batch, resourcedetection, resource/telemetry] + exporters: [debug] diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/sample/sample_input.xml b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/sample/sample_input.xml new file mode 100644 index 0000000000..81cc0a7d55 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/internal/testdata/happypath/sample/sample_input.xml @@ -0,0 +1,28 @@ + + + jamehugh-l-PW0EPTQZ + https://127.0.0.1:8089 + REDACTED + /opt/splunk/var/lib/splunk/modinputs/Splunk_TA_otel_linux_autoinstrumentation + + + "/etc/splunk/zeroconfig" + false + "false" + $decideOnStartup + _internal + 60 + "false" + "false" + "false" + "false" + "false" + Splunk_TA_otel_linux_autoinstrumentation + "$SPLUNK_OTEL_TA_HOME/" + "/etc/ld.so.preload" + "$SPLUNK_OTEL_TA_HOME/linux_x86_64/bin/javaagent.jar" + false + "/etc/splunk/zeroconfig" + + + \ No newline at end of file diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-release.txt b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-release.txt new file mode 100644 index 0000000000..f855fc0f82 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-release.txt @@ -0,0 +1 @@ +v2.15.0 diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-sha256sum.txt b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-sha256sum.txt new file mode 100644 index 0000000000..4315594518 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/java-agent-sha256sum.txt @@ -0,0 +1 @@ +645b2afeb1729d9ff8acd6f1c54e02ddd28b3f70b0464b15cc9e365500bdf755 diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig.go b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig.go new file mode 100644 index 0000000000..3a3ea31489 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig.go @@ -0,0 +1,140 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + _ "embed" + "errors" + "fmt" + "log" + "os" + "strings" + "text/template" +) + +//go:embed java-agent-release.txt +var javaVersion string + +//go:embed java-agent-sha256sum.txt +var javaAgent256Sum string + +const configTemplate = `JAVA_TOOL_OPTIONS=-javaagent:{{.InstrumentationJarPath}} +OTEL_RESOURCE_ATTRIBUTES={{.ResourceAttributes}} +SPLUNK_PROFILER_ENABLED={{.EnableProfiler}} +SPLUNK_PROFILER_MEMORY_ENABLED={{.EnableProfilerMemory}} +SPLUNK_METRICS_ENABLED={{.EnableMetrics}} +{{- if .ServiceName }} +OTEL_SERVICE_NAME={{ .ServiceName }} +{{- end }} +{{- if .OtlpEndpoint }} +OTEL_EXPORTER_OTLP_ENDPOINT={{.OtlpEndpoint}} +{{- end }} +{{- if .OtlpEndpointProtocol }} +OTEL_EXPORTER_OTLP_PROTOCOL={{.OtlpEndpointProtocol}} +{{- end }} +{{- if .MetricsExporter }} +OTEL_METRICS_EXPORTER={{.MetricsExporter}} +{{- end }} +{{- if .LogsExporter }} +OTEL_LOGS_EXPORTER={{.LogsExporter}} +{{- end }} +` + +// TemplateData hughesjj@ +// I've gone back and forth on whether to just wrap modInputs and add custom as needed +// As of now, only resourceattributes differ from what's default in modInputs +// I can't just add a transformer, given it would differ from the nodejs case +// Since goland does not support autocompletion in the template, I've decided +// to just duplicate them all for now, to decouple from the customer interface +// as defined in inputs.conf +type TemplateData struct { + InstrumentationJarPath string + ResourceAttributes string + EnableProfiler string + EnableProfilerMemory string + EnableMetrics string + ServiceName string + OtlpEndpoint string + OtlpEndpointProtocol string + MetricsExporter string + LogsExporter string +} + +// CreateZeroConfigJava reimplements create_zeroconfig_java from installer script +func CreateZeroConfigJava(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + if "" == modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value { + log.Printf("Not instrumenting java, as %s was not set", modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Name) + return nil + } + if "" == modInputs.AutoinstrumentationPath.Value { + log.Printf("Not instrumenting java, as %s was not set", modInputs.AutoinstrumentationPath.Name) + return nil + } + resourceAttributes := fmt.Sprintf("splunk.zc.method=splunk-otel-auto-instrumentation-%s", strings.TrimSpace(javaVersion)) + + if "" != modInputs.DeploymentEnvironment.Value { + resourceAttributes = fmt.Sprintf("%s,deployment.environment=%s", resourceAttributes, modInputs.DeploymentEnvironment.Value) + } + if "" != modInputs.ResourceAttributes.Value { + resourceAttributes = fmt.Sprintf("%s,%s", resourceAttributes, modInputs.ResourceAttributes.Value) + } + + tmpl, err := template.New("JavaZeroConfig").Parse(configTemplate) + if err != nil { + log.Fatalf("error generating zeroconfig file at %s from template: %#v", modInputs.ZeroconfigPath.Value, err) + return err + } + templateData := TemplateData{ + InstrumentationJarPath: modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value, + ResourceAttributes: resourceAttributes, + EnableProfiler: modInputs.ProfilerEnabled.Value, + EnableProfilerMemory: modInputs.ProfilerMemoryEnabled.Value, + EnableMetrics: modInputs.MetricsEnabled.Value, + ServiceName: modInputs.OtelServiceName.Value, + OtlpEndpoint: modInputs.OtelExporterOtlpEndpoint.Value, + OtlpEndpointProtocol: modInputs.OtelExporterOtlpProtocol.Value, + MetricsExporter: modInputs.OtelMetricsExporter.Value, + LogsExporter: modInputs.OtelLogsExporter.Value, + } + filePath, err := os.Create(modInputs.ZeroconfigPath.Value) + if err != nil && !errors.Is(err, os.ErrExist) { + return err + } + if err = tmpl.Execute(filePath, templateData); err != nil { + return fmt.Errorf("failed to execute template: %w %v", err, templateData) + } + log.Printf("Successfully generated java autoinstrumentation config at %s \n", filePath.Name()) + return nil +} + +func InstrumentJava(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + if err := CreateZeroConfigJava(modInputs); err != nil { + return err + } + return nil +} + +func RemoveJavaInstrumentation(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + if strings.ToLower(modInputs.Backup.Value) != "false" { + if err := backupFile(modInputs.ZeroconfigPath.Value); err != nil && !errors.Is(err, os.ErrNotExist) { + log.Fatalf("error backing up java auto instrumentation configuration, refusing to remove (specify backup=false in inputs.conf if backup not needed): %v", err) + return err + } + } + if err := os.Remove(modInputs.ZeroconfigPath.Value); !errors.Is(err, os.ErrNotExist) { + return err + } + return nil +} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig_test.go b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig_test.go new file mode 100644 index 0000000000..cacd0ad0bd --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/javazeroconfig_test.go @@ -0,0 +1,214 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/docker/docker/pkg/fileutils" + "github.com/splunk/splunk-technical-addon/internal/packaging" + "github.com/splunk/splunk-technical-addon/internal/testaddon" + "github.com/splunk/splunk-technical-addon/internal/testcommon" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + tcexec "github.com/testcontainers/testcontainers-go/exec" + "github.com/testcontainers/testcontainers-go/wait" +) + +func TestZeroConfig(t *testing.T) { + expectedJar := filepath.Join(packaging.GetBuildDir(), "Splunk_TA_otel_linux_autoinstrumentation", "linux_x86_64", "bin", "splunk-otel-javaagent.jar") + require.FileExists(t, expectedJar) + + tests := []struct { + testname string + testDir string + modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs + expectedConfig string + preload bool + }{ + { + testname: "happypath-preload", + testDir: t.TempDir(), + preload: false, + modInputs: &SplunkTAOtelLinuxAutoinstrumentationModularInputs{ + SplunkOtelJavaAutoinstrumentationJarPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-javaagent.jar", + Name: "splunk_otel_java_autoinstrumentation_jar_path", + }, + AutoinstrumentationPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/libsplunk_amd64.so", + Name: "autoinstrumentation_path", + }, + AutoinstrumentationPreloadPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "/etc/ld.so.preload", + Name: "autoinstrumentation_preload_path", + }, + ZeroconfigPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "zero.conf", + Name: "zeroconfig_path", + }, + ResourceAttributes: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "asdasd", + Name: "resource_attributes", + }, + ProfilerEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "false", + Name: "profiler_enabled", + }, + ProfilerMemoryEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "false", + Name: "profiler_memory_enabled", + }, + MetricsEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "false", + Name: "metrics_enabled", + }, + LogsEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "false", + Name: "logs_enabled", + }, + Remove: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: "false", + Name: "remove", + }, + }, + expectedConfig: `JAVA_TOOL_OPTIONS=-javaagent:REPLACED_WITH_TESTDIR/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-javaagent.jar +OTEL_RESOURCE_ATTRIBUTES=splunk.zc.method=splunk-otel-auto-instrumentation-v2.15.0,asdasd +SPLUNK_PROFILER_ENABLED=false +SPLUNK_PROFILER_MEMORY_ENABLED=false +SPLUNK_METRICS_ENABLED=false +`, + }, + } + for _, tc := range tests { + t.Run(tc.testname, func(tt *testing.T) { + require.NoError(tt, os.CopyFS(filepath.Join(tc.testDir, "Splunk_TA_otel_linux_autoinstrumentation"), os.DirFS(filepath.Join(packaging.GetBuildDir(), "Splunk_TA_otel_linux_autoinstrumentation")))) + + tc.modInputs.ZeroconfigPath.Value = filepath.Join(tc.testDir, tc.modInputs.ZeroconfigPath.Value) + tc.modInputs.AutoinstrumentationPath.Value = filepath.Join(tc.testDir, tc.modInputs.AutoinstrumentationPath.Value) + tc.modInputs.AutoinstrumentationPreloadPath.Value = filepath.Join(tc.testDir, tc.modInputs.AutoinstrumentationPreloadPath.Value) + tc.modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value = filepath.Join(tc.testDir, tc.modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value) + + require.NoError(tt, CreateZeroConfigJava(tc.modInputs)) + assert.FileExists(tt, tc.modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value) + testaddon.AssertFileShasEqual(t, expectedJar, tc.modInputs.SplunkOtelJavaAutoinstrumentationJarPath.Value) + assert.FileExists(tt, tc.modInputs.AutoinstrumentationPath.Value) + if tc.preload { + assert.FileExists(tt, tc.modInputs.AutoinstrumentationPreloadPath.Value) + } else { + assert.NoFileExists(tt, tc.modInputs.AutoinstrumentationPreloadPath.Value) + } + require.FileExists(tt, tc.modInputs.ZeroconfigPath.Value) + expectedPath := filepath.Join(tc.testDir, "expected-zeroconfig.conf") + assert.NoFileExists(tt, expectedPath) + require.NoError(tt, os.WriteFile(expectedPath, []byte(strings.ReplaceAll(tc.expectedConfig, "REPLACED_WITH_TESTDIR", tc.testDir)), 0o600)) + testcommon.AssertFilesMatch(tt, expectedPath, tc.modInputs.ZeroconfigPath.Value) + }) + } +} + +func TestHappyPath(t *testing.T) { + defaultModInputs := GetDefaultSplunkTAOtelLinuxAutoinstrumentationModularInputs() + + sourcedir, err := packaging.GetSourceDir() + require.NoError(t, err) + + addonFunc := func(_ *testing.T, addonPath string) error { + // Copies "local/inputs.conf" + err2 := os.CopyFS(addonPath, os.DirFS(filepath.Join( + sourcedir, + "pkg", + "splunk_ta_otel_linux_autoinstrumentation", + "runner", + "internal", + "testdata", + "happypath", + ))) + return err2 + } + zcAddonPath := testaddon.PackAddon(t, &defaultModInputs, addonFunc) + otelAddonPath := filepath.Join(packaging.GetBuildDir(), "out", "distribution", "Splunk_TA_otel.tgz") + repackedOtelAddon := testaddon.RepackAddon(t, otelAddonPath, func(tt *testing.T, addonDir string) error { + // TODO copy over a debug output config + _, err = fileutils.CopyFile("internal/testdata/happypath/local/ta-agent-config.yaml", filepath.Join(addonDir, "Splunk_TA_otel", "configs", "ta-agent-config.yaml")) + require.NoError(tt, err) + return nil + }) + startupTimeout := 20 * time.Minute + tc := testaddon.StartSplunk(t, testaddon.SplunkStartOpts{ + AddonPaths: []string{zcAddonPath, repackedOtelAddon}, + SplunkUser: "root", + SplunkGroup: "root", + Timeout: startupTimeout, + WaitStrategy: wait.ForAll( + wait.ForExec([]string{"sudo", "stat", "/opt/splunk/var/log/splunk/splunkd.log"}).WithStartupTimeout(startupTimeout), + wait.ForExec([]string{"sudo", "stat", "/opt/splunk/var/log/splunk/Splunk_TA_otel_linux_autoinstrumentation.log"}).WithStartupTimeout(startupTimeout), + ).WithDeadline(startupTimeout + 15*time.Second).WithStartupTimeoutDefault(startupTimeout)}) + + // Check Schema + ctx := context.Background() + code, output, err := tc.Exec(ctx, []string{"sudo", "/opt/splunk/bin/splunk", "btool", "check", "--debug"}) + assert.NoError(t, err) + assert.LessOrEqual(t, code, 1) // Other stanzas may be missing and thus have this be 0 or 1 + assert.GreaterOrEqual(t, code, 0) // bound to [0,1] + read, err := io.ReadAll(output) + assert.NoError(t, err) + assert.NotContains(t, string(read), "Invalid Key in Stanza") + + // check log output + _, output, err = tc.Exec(ctx, []string{"sudo", "cat", "/opt/splunk/var/log/splunk/Splunk_TA_otel_linux_autoinstrumentation.log"}) + require.NoError(t, err) + read, err = io.ReadAll(output) + assert.NoError(t, err) + assert.Contains(t, string(read), "Successfully generated java autoinstrumentation config at /opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/config/zero.conf") + + // Check zeroconfig value + _, output, err = tc.Exec(ctx, []string{"sudo", "cat", "/opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/config/zero.conf"}, tcexec.Multiplexed()) + require.NoError(t, err) + read, err = io.ReadAll(output) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("JAVA_TOOL_OPTIONS=-javaagent:/opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-javaagent.jar\nOTEL_RESOURCE_ATTRIBUTES=splunk.zc.method=splunk-otel-auto-instrumentation-%s\nSPLUNK_PROFILER_ENABLED=false\nSPLUNK_PROFILER_MEMORY_ENABLED=false\nSPLUNK_METRICS_ENABLED=false", strings.TrimSpace(javaVersion)), strings.TrimSpace(string(read))) + + // Check preload config + _, output, err = tc.Exec(ctx, []string{"sudo", "cat", "/etc/ld.so.preload"}, tcexec.Multiplexed()) + require.NoError(t, err) + read, err = io.ReadAll(output) + assert.NoError(t, err) + assert.NotEmpty(t, read) + assert.Equal(t, "/opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/libsplunk_amd64.so", strings.TrimSpace(string(read))) + + // Check preload binary + _, output, err = tc.Exec(ctx, []string{"sudo", "sha256sum", "/opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/libsplunk_amd64.so"}, tcexec.Multiplexed()) + require.NoError(t, err) + read, err = io.ReadAll(output) + assert.NoError(t, err) + assert.Contains(t, string(read), "4a9944614212c477cd63f5354026850052f2aa495312fb79ebd24e22dc8953bd") + + // check jar + _, output, err = tc.Exec(ctx, []string{"sudo", "sha256sum", "/opt/splunk/etc/apps/Splunk_TA_otel_linux_autoinstrumentation/linux_x86_64/bin/splunk-otel-javaagent.jar"}, tcexec.Multiplexed()) + require.NoError(t, err) + read, err = io.ReadAll(output) + assert.NoError(t, err) + assert.Contains(t, string(read), strings.TrimSpace(javaAgent256Sum)) + + assert.NoError(t, tc.Terminate(ctx)) +} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/main.go b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/main.go new file mode 100644 index 0000000000..ee4118ae83 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/main.go @@ -0,0 +1,195 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bufio" + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/docker/docker/pkg/fileutils" + + log "github.com/sirupsen/logrus" + "github.com/splunk/splunk-technical-addon/internal/modularinput" +) + +func main() { + os.Exit(run()) +} + +func run() int { + config := GetDefaultSplunkTAOtelLinuxAutoinstrumentationModularInputs() + + schemeFlag := flag.Bool("scheme", false, "Print the scheme and exit") + validateFlag := flag.Bool("validate-arguments", false, "Validate the arguments and exit") + flag.Parse() + if *schemeFlag { + return 0 + } + defaultLogFilepath, err := modularinput.GetDefaultLogFilePath(config.SchemaName) + if err != nil { + panic(err) + } + logCloseFunc, err := modularinput.SetupAddonLogger(defaultLogFilepath) + if err != nil { + panic(err) + } + defer logCloseFunc() + + // Create a new modular input processor with the embedded configuration + mip := modularinput.NewModinputProcessor(config.SchemaName, config.ModularInputs) + + xmlInput, err := modularinput.ReadXML(os.Stdin) + if err != nil { + panic(err) + } + err = mip.ProcessXML(xmlInput) + + if *validateFlag { + if err != nil { + return -1 + } + return 0 + } + if err != nil { + log.Errorf("Error parsing modinput: %+v", err) + panic(err) + } + modInputs := GetSplunkTAOtelLinuxAutoinstrumentationModularInputs(mip) + err = Run(modInputs) + if err != nil { + log.Errorf("error running splunk linux autoinstrumentation addon: %+v", err) + panic(err) + } + // set up trap + return 0 +} + +func Run(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + lowerModInput := strings.ToLower(modInputs.Remove.Value) + if "false" == lowerModInput { + return Instrument(modInputs) + } + if "true" == lowerModInput { + return DeInstrument(modInputs) + } + + return fmt.Errorf("unknown value for 'remove' modular input, expected (true|false) given %v", strings.ToLower(modInputs.Remove.Value)) +} + +func DeInstrument(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + if err := RemoveJavaInstrumentation(modInputs); err != nil { + return err + } + if err := RemovePreloadInstrumentation(modInputs); err != nil { + return err + } + return nil +} + +func Instrument(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + if err := InstrumentJava(modInputs); err != nil { + return err + } + if err := AutoinstrumentLdPreload(modInputs); err != nil { + return err + } + return nil +} + +func AutoinstrumentLdPreload(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + found, err := grepFile(modInputs.AutoinstrumentationPath.Value, modInputs.AutoinstrumentationPreloadPath.Value) + if err != nil { + return err + } + if !found { + // todo check modinputs for backup of file + if err = backupFile(modInputs.AutoinstrumentationPreloadPath.Value); err != nil { + return err + } + f, err2 := os.OpenFile(modInputs.AutoinstrumentationPreloadPath.Value, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err2 != nil { + return fmt.Errorf("error opening %s: %w", modInputs.AutoinstrumentationPreloadPath.Value, err2) + } + defer f.Close() + if _, err2 = f.WriteString(modInputs.AutoinstrumentationPath.Value + "\n"); err2 != nil { + return fmt.Errorf("error writing to %s: %w", modInputs.AutoinstrumentationPreloadPath.Value, err2) + } + log.Printf("Successfully autoinstrumented preload at %v with %v\n", modInputs.AutoinstrumentationPreloadPath.Value, modInputs.AutoinstrumentationPath.Value) + } else { + log.Printf("Preload already autoinstrumented with %v at %v\n", modInputs.AutoinstrumentationPath.Value, modInputs.AutoinstrumentationPreloadPath.Value) + } + return nil +} + +func RemovePreloadInstrumentation(modInputs *SplunkTAOtelLinuxAutoinstrumentationModularInputs) error { + found, err := grepFile(modInputs.Remove.Value, modInputs.AutoinstrumentationPreloadPath.Value) + if err != nil { + return err + } + if found { + content, err := os.ReadFile(modInputs.AutoinstrumentationPreloadPath.Value) + if err != nil { + return err + } + if modInputs.Backup.Value != "false" { + if err = backupFile(modInputs.AutoinstrumentationPreloadPath.Value); err != nil { + return err + } + } + newContent := strings.ReplaceAll(string(content), modInputs.AutoinstrumentationPreloadPath.Value, "") + return os.WriteFile(modInputs.AutoinstrumentationPreloadPath.Value, []byte(newContent), 0644) // #nosec G306 + + } + log.Printf("Autoinstrumentation preload (%s) does not exist or does not contain configured autoinstrumentation (%s)", modInputs.AutoinstrumentationPreloadPath.Value, modInputs.AutoinstrumentationPath.Value) + return nil +} + +func backupFile(currPath string) error { + if _, err := os.Stat(currPath); errors.Is(err, os.ErrNotExist) { + return nil + } + dir := filepath.Dir(currPath) + ogFilename := filepath.Base(currPath) + newPathName := filepath.Join(dir, fmt.Sprintf("%s.%v", ogFilename, time.Now().UnixNano())) + if _, err := fileutils.CopyFile(currPath, newPathName); err != nil { + return err + } + return nil +} + +func grepFile(search string, filepath string) (bool, error) { + file, err := os.Open(filepath) + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + if err != nil { + return false, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.Contains(scanner.Text(), search) { + return true, nil + } + } + return false, nil +} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modinput_config.go b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modinput_config.go new file mode 100644 index 0000000000..c4be68e89a --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modinput_config.go @@ -0,0 +1,236 @@ +// Code generated by modinput_config_generator; DO NOT EDIT. +package main + +import ( + "github.com/splunk/splunk-technical-addon/internal/modularinput" +) + +const SchemaName = "Splunk_TA_otel_linux_autoinstrumentation" + +type SplunkTAOtelLinuxAutoinstrumentationModInput struct { + Value string + Name string +} + +type SplunkTAOtelLinuxAutoinstrumentationModularInputs struct { + AutoinstrumentationPath SplunkTAOtelLinuxAutoinstrumentationModInput + AutoinstrumentationPreloadPath SplunkTAOtelLinuxAutoinstrumentationModInput + Backup SplunkTAOtelLinuxAutoinstrumentationModInput + DeploymentEnvironment SplunkTAOtelLinuxAutoinstrumentationModInput + Force SplunkTAOtelLinuxAutoinstrumentationModInput + LogsEnabled SplunkTAOtelLinuxAutoinstrumentationModInput + MetricsEnabled SplunkTAOtelLinuxAutoinstrumentationModInput + OtelExporterOtlpEndpoint SplunkTAOtelLinuxAutoinstrumentationModInput + OtelExporterOtlpProtocol SplunkTAOtelLinuxAutoinstrumentationModInput + OtelLogsExporter SplunkTAOtelLinuxAutoinstrumentationModInput + OtelMetricsExporter SplunkTAOtelLinuxAutoinstrumentationModInput + OtelServiceName SplunkTAOtelLinuxAutoinstrumentationModInput + ProfilerEnabled SplunkTAOtelLinuxAutoinstrumentationModInput + ProfilerMemoryEnabled SplunkTAOtelLinuxAutoinstrumentationModInput + Remove SplunkTAOtelLinuxAutoinstrumentationModInput + ResourceAttributes SplunkTAOtelLinuxAutoinstrumentationModInput + SplunkOtelAutoinstrumentationNodejsPath SplunkTAOtelLinuxAutoinstrumentationModInput + SplunkOtelJavaAutoinstrumentationJarPath SplunkTAOtelLinuxAutoinstrumentationModInput + SplunkOtelLogFile SplunkTAOtelLinuxAutoinstrumentationModInput + ZeroconfigPath SplunkTAOtelLinuxAutoinstrumentationModInput +} + +func GetSplunkTAOtelLinuxAutoinstrumentationModularInputs(mip *modularinput.ModinputProcessor) *SplunkTAOtelLinuxAutoinstrumentationModularInputs { + return &SplunkTAOtelLinuxAutoinstrumentationModularInputs{ + AutoinstrumentationPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["autoinstrumentation_path"].Value, + Name: "autoinstrumentation_path", + }, + AutoinstrumentationPreloadPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["autoinstrumentation_preload_path"].Value, + Name: "autoinstrumentation_preload_path", + }, + Backup: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["backup"].Value, + Name: "backup", + }, + DeploymentEnvironment: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["deployment_environment"].Value, + Name: "deployment_environment", + }, + Force: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["force"].Value, + Name: "force", + }, + LogsEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["logs_enabled"].Value, + Name: "logs_enabled", + }, + MetricsEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["metrics_enabled"].Value, + Name: "metrics_enabled", + }, + OtelExporterOtlpEndpoint: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["otel_exporter_otlp_endpoint"].Value, + Name: "otel_exporter_otlp_endpoint", + }, + OtelExporterOtlpProtocol: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["otel_exporter_otlp_protocol"].Value, + Name: "otel_exporter_otlp_protocol", + }, + OtelLogsExporter: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["otel_logs_exporter"].Value, + Name: "otel_logs_exporter", + }, + OtelMetricsExporter: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["otel_metrics_exporter"].Value, + Name: "otel_metrics_exporter", + }, + OtelServiceName: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["otel_service_name"].Value, + Name: "otel_service_name", + }, + ProfilerEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["profiler_enabled"].Value, + Name: "profiler_enabled", + }, + ProfilerMemoryEnabled: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["profiler_memory_enabled"].Value, + Name: "profiler_memory_enabled", + }, + Remove: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["remove"].Value, + Name: "remove", + }, + ResourceAttributes: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["resource_attributes"].Value, + Name: "resource_attributes", + }, + SplunkOtelAutoinstrumentationNodejsPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["splunk_otel_autoinstrumentation_nodejs_path"].Value, + Name: "splunk_otel_autoinstrumentation_nodejs_path", + }, + SplunkOtelJavaAutoinstrumentationJarPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["splunk_otel_java_autoinstrumentation_jar_path"].Value, + Name: "splunk_otel_java_autoinstrumentation_jar_path", + }, + SplunkOtelLogFile: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["splunk_otel_log_file"].Value, + Name: "splunk_otel_log_file", + }, + ZeroconfigPath: SplunkTAOtelLinuxAutoinstrumentationModInput{ + Value: mip.ModularInputs["zeroconfig_path"].Value, + Name: "zeroconfig_path", + }, + } +} + +// GetDefaultSplunkTAOtelLinuxAutoinstrumentationModularInputs returns the embedded modular input configuration +func GetDefaultSplunkTAOtelLinuxAutoinstrumentationModularInputs() modularinput.GenericModularInput { + return modularinput.GenericModularInput{ + SchemaName: "Splunk_TA_otel_linux_autoinstrumentation", + ModularInputs: map[string]*modularinput.ModInput{ + "autoinstrumentation_path": { + Config: modularinput.ModInputConfig{Description: "Path for .so", Default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/libsplunk_amd64.so", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Value: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/libsplunk_amd64.so", + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "autoinstrumentation_preload_path": { + Config: modularinput.ModInputConfig{Description: "Path to add preload configuration to.", Default: "/etc/ld.so.preload", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Value: "/etc/ld.so.preload", + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "backup": { + Config: modularinput.ModInputConfig{Description: "Backup files before writing to them or deleting them. Backup files are not deleted by this addon.", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "deployment_environment": { + Config: modularinput.ModInputConfig{Description: "Same as --deployment environment", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "force": { + Config: modularinput.ModInputConfig{Description: "whether to force installation ", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "logs_enabled": { + Config: modularinput.ModInputConfig{Description: "Enables logs exporting of autoinstrumented.", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "metrics_enabled": { + Config: modularinput.ModInputConfig{Description: "Enabled metrics exporting of autoinstrumented.", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "otel_exporter_otlp_endpoint": { + Config: modularinput.ModInputConfig{Description: "The endpoint for OTLP exporter.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "otel_exporter_otlp_protocol": { + Config: modularinput.ModInputConfig{Description: "The protocol for OTLP exporter.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "otel_logs_exporter": { + Config: modularinput.ModInputConfig{Description: "The exporter to use for logs.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "otel_metrics_exporter": { + Config: modularinput.ModInputConfig{Description: "The exporter to use for metrics.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "otel_service_name": { + Config: modularinput.ModInputConfig{Description: "The service name to use for telemetry data.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "profiler_enabled": { + Config: modularinput.ModInputConfig{Description: "Enables system wide java cpu profiler in autoinstrumentation.", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "profiler_memory_enabled": { + Config: modularinput.ModInputConfig{Description: "Enables system wide java memory profiler in autoinstrumentation.", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "remove": { + Config: modularinput.ModInputConfig{Description: "If true, removes the installation", Default: "false", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Value: "false", + Transformers: []modularinput.TransformerFunc{}, + }, + "resource_attributes": { + Config: modularinput.ModInputConfig{Description: "String representing the OTEL_RESOURCE_ATTRIBUTES.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: false}, + Transformers: []modularinput.TransformerFunc{}, + }, + "splunk_otel_autoinstrumentation_nodejs_path": { + Config: modularinput.ModInputConfig{Description: "Path for nodejs autoinstrumentation", Default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-nodejs.tgz", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Value: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-nodejs.tgz", + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "splunk_otel_java_autoinstrumentation_jar_path": { + Config: modularinput.ModInputConfig{Description: "Path for the java jar used in autoinstumentation.", Default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-javaagent.jar", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Value: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-javaagent.jar", + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "splunk_otel_log_file": { + Config: modularinput.ModInputConfig{Description: "Log file for otel collector.", Default: "", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + "zeroconfig_path": { + Config: modularinput.ModInputConfig{Description: "Path for zeroconfig", Default: "$SPLUNK_OTEL_TA_HOME/config/zero.conf", Flag: modularinput.Flag{Name: "", IsUnary: false}, Required: false, PassthroughEnvVar: false, ReplaceableEnvVar: true}, + Value: "$SPLUNK_OTEL_TA_HOME/config/zero.conf", + Transformers: []modularinput.TransformerFunc{ + modularinput.DefaultReplaceEnvVarTransformer, + }, + }, + }, + } +} diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modular-inputs.yaml b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modular-inputs.yaml new file mode 100644 index 0000000000..f21a718264 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/modular-inputs.yaml @@ -0,0 +1,89 @@ +modular-input-schema-name: Splunk_TA_otel_linux_autoinstrumentation +version: 0.0.0 +modular-inputs: + splunk_otel_log_file: + description: "Log file for otel collector." + default: "" + replaceable: true + + force: + description: "whether to force installation " + default: "false" + + remove: + description: "If true, removes the installation" + default: "false" + + backup: + description: "Backup files before writing to them or deleting them. Backup files are not deleted by this addon." + default: "false" + + resource_attributes: + description: "String representing the OTEL_RESOURCE_ATTRIBUTES." + default: "" + + profiler_enabled: + description: "Enables system wide java cpu profiler in autoinstrumentation." + default: "false" + + profiler_memory_enabled: + description: "Enables system wide java memory profiler in autoinstrumentation." + default: "false" + + metrics_enabled: + description: "Enabled metrics exporting of autoinstrumented." + default: "false" + + logs_enabled: + description: "Enables logs exporting of autoinstrumented." + default: "false" + + otel_service_name: + description: "The service name to use for telemetry data." + default: "" + + otel_exporter_otlp_endpoint: + description: "The endpoint for OTLP exporter." + default: "" + + otel_exporter_otlp_protocol: + description: "The protocol for OTLP exporter." + default: "" + + otel_metrics_exporter: + description: "The exporter to use for metrics." + default: "" + + otel_logs_exporter: + description: "The exporter to use for logs." + default: "" + + zeroconfig_path: + description: "Path for zeroconfig" + default: "$SPLUNK_OTEL_TA_HOME/config/zero.conf" + replaceable: true + + autoinstrumentation_path: + description: "Path for .so" + default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/libsplunk_amd64.so" + replaceable: true + + autoinstrumentation_preload_path: + description: "Path to add preload configuration to." + default: "/etc/ld.so.preload" + replaceable: true + + splunk_otel_java_autoinstrumentation_jar_path: + description: "Path for the java jar used in autoinstumentation." + default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-javaagent.jar" + replaceable: true + + splunk_otel_autoinstrumentation_nodejs_path: + description: "Path for nodejs autoinstrumentation" + default: "$SPLUNK_OTEL_TA_PLATFORM_HOME/bin/splunk-otel-nodejs.tgz" + replaceable: true + + deployment_environment: + description: "Same as --deployment environment" + default: "" + replaceable: true \ No newline at end of file diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-release.txt b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-release.txt new file mode 100644 index 0000000000..d95827c3d9 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-release.txt @@ -0,0 +1 @@ +v3.1.2 diff --git a/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-sha256sum.txt b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-sha256sum.txt new file mode 100644 index 0000000000..9483650797 --- /dev/null +++ b/packaging/technical-addon/pkg/splunk_ta_otel_linux_autoinstrumentation/runner/nodejs-agent-sha256sum.txt @@ -0,0 +1 @@ +d38ce3d49fd1895c8df759fe153f1b6d50f545db3ab347121e2ae646219da2ef