Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy Java attacher jar to a tmp directory #8803

Merged
merged 29 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0f1c5c2
Copy Java attacher jar to a tmp directory
eyalkoren Aug 4, 2022
ee2a3fc
Fix tests on Windows
eyalkoren Aug 9, 2022
43961a0
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Aug 10, 2022
2bec8cd
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Aug 21, 2022
1b5182e
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Aug 23, 2022
48fb9e5
Create tmp dir per user with proper dir and jar modes
eyalkoren Aug 23, 2022
99c6e72
Defer file close and ignore errors
eyalkoren Aug 23, 2022
656f3c2
Avoid using non-Windows attacher method in test
eyalkoren Aug 23, 2022
d3cf8e7
Fix test for Windows
eyalkoren Aug 24, 2022
9af7a03
Use Atoi
eyalkoren Aug 24, 2022
79062fe
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Aug 24, 2022
7ddf938
Applying revierw changes
eyalkoren Aug 24, 2022
85a3d0a
Replace javaw.exe with java.exe
eyalkoren Aug 24, 2022
1faede3
go doc changes
eyalkoren Aug 25, 2022
8d42b10
remove redundant variable
eyalkoren Aug 25, 2022
a7589e8
Improving set user
eyalkoren Aug 25, 2022
42da18e
remove IDE instruction
eyalkoren Aug 25, 2022
553755e
Merge remote-tracking branch 'eyalkoren/java-attacher-copy-to-temp' i…
eyalkoren Aug 25, 2022
0a7e92c
Adding missing return statement
eyalkoren Aug 25, 2022
97e29b8
Separate tests into OS-specific files
eyalkoren Aug 25, 2022
d421b68
Remove redundant slice initialization
eyalkoren Aug 25, 2022
70b1655
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Aug 25, 2022
09cb624
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Sep 7, 2022
1442e5a
Adding go doc
eyalkoren Sep 7, 2022
8869366
Merge remote-tracking branch 'eyalkoren/java-attacher-copy-to-temp' i…
eyalkoren Sep 7, 2022
f304f4d
Applying review changes
eyalkoren Sep 7, 2022
865e5c3
Merge remote-tracking branch 'upstream/main' into java-attacher-copy-…
eyalkoren Sep 7, 2022
500f845
Adding to changelog
eyalkoren Sep 7, 2022
fb8f0ef
Update changelogs/head.asciidoc
eyalkoren Sep 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 84 additions & 5 deletions internal/beater/java_attacher/java_attacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"os/user"
Expand All @@ -40,8 +41,10 @@ import (
"github.com/elastic/apm-server/internal/beater/config"
)

// javaAttacher is bundled by the server
var javaAttacher = filepath.FromSlash("./java-attacher.jar")
const javaAttacherJarName = "java-attacher.jar"

// referring the Java attacher that is bundled at the server root
var bundledJavaAttacher = filepath.FromSlash(fmt.Sprintf("./%v", javaAttacherJarName))
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved

type jvmDetails struct {
user string
Expand All @@ -62,11 +65,12 @@ type JavaAttacher struct {
agentConfigs map[string]string
downloadAgentVersion string
jvmCache map[int]*jvmDetails
tempAttacherDir string
}

func New(cfg config.JavaAttacherConfig) (*JavaAttacher, error) {
logger := logp.NewLogger("java-attacher")
if _, err := os.Stat(javaAttacher); err != nil {
if _, err := os.Stat(bundledJavaAttacher); err != nil {
return nil, err
}
if !cfg.Enabled {
Expand Down Expand Up @@ -145,7 +149,13 @@ func (j *JavaAttacher) Run(ctx context.Context) error {
return fmt.Errorf("java attacher is disabled")
}

// Non-Windows: run discovery and attachment until context is closed.
tempDir, err := j.createAttacherTempDir()
if err != nil {
j.logger.Errorf("failed to create a Java attacher temp dir: %v. Using the bundled attacher jar instead", err)
} else {
defer j.deleteTempDir(tempDir)
}
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved

ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
Expand All @@ -167,6 +177,71 @@ func (j *JavaAttacher) Run(ctx context.Context) error {
}
}

func (j *JavaAttacher) deleteTempDir(tempDir string) {
func(name string) {
err := os.RemoveAll(name)
if err != nil {
j.logger.Errorf("failed to delete Java attacher temp dir at %v: %v", tempDir, err)
}
}(tempDir)
}

// Creates a temp dir for the java attacher jar with the attacher jar in it, both with 0755 access permissions.
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
// The assumption is that the default temp dir will be readable for all users.
// However, there may be systems in which the temp directory is user-specific,
// in which case we'll need to modify this function to run a temp dir search per user,
// maybe by externally executing `echo XXX` and reading the output, relying on environment
// variables as implemented in os.TempDir, or looking in known paths.
// In such case, we would switch to using a cache of temp-agent-dir per user.
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
func (j *JavaAttacher) createAttacherTempDir() (string, error) {
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
tempDir, err := os.MkdirTemp("", "elasticapmagent")
if err != nil {
return "", err
}
// todo: check if we need to use different value for specific OSs
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
// make sure this dir is readable and executable by all users and writable only by the current user
err = os.Chmod(tempDir, 0755)
if err != nil {
j.logger.Errorf("failed to change permissions for %v to make it readable for all users: %v", tempDir, err)
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
}
bundledAttacherFile, err := os.Open(bundledJavaAttacher)
if err != nil {
defer j.deleteTempDir(tempDir)
return "", fmt.Errorf("failed to open bundled attacher jar: %w", err)
}
defer j.closeFile(bundledAttacherFile)
tmpAttacherJarPath := tmpJavaAttacherJarPath(tempDir)
tmpAttacherJarFile, err := os.Create(tmpAttacherJarPath)
if err != nil {
defer j.deleteTempDir(tempDir)
return "", fmt.Errorf("failed to create tmp attacher jar: %w", err)
}
defer j.closeFile(tmpAttacherJarFile)
nBytes, err := io.Copy(tmpAttacherJarFile, bundledAttacherFile)
if err != nil {
defer j.deleteTempDir(tempDir)
return "", fmt.Errorf("failed to copy bundled attacher jar to %v: %w", bundledJavaAttacher, err)
}
j.logger.Debugf("%v (%v bytes) successfully copied to %v", bundledJavaAttacher, nBytes, tmpAttacherJarPath)
err = os.Chmod(tmpAttacherJarPath, 0755)
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
j.logger.Errorf("failed to change permissions for %v to make it readable for all users: %v", tmpAttacherJarPath, err)
}
j.tempAttacherDir = tempDir
return tempDir, nil
}

func tmpJavaAttacherJarPath(tempDir string) string {
return filepath.Join(tempDir, javaAttacherJarName)
}

func (j *JavaAttacher) closeFile(file *os.File) {
err := file.Close()
if err != nil {
j.logger.Errorf("failed to close file %v: %v", file, err)
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
}
}

// this function blocks until discovery has ended, an error occurred or the context had been cancelled
func (j *JavaAttacher) discoverJVMsForAttachment(ctx context.Context) (map[int]*jvmDetails, error) {
jvms, err := j.discoverAllRunningJavaProcesses()
Expand Down Expand Up @@ -361,8 +436,12 @@ func (j *JavaAttacher) attachJVM(ctx context.Context, jvm *jvmDetails) error {
}

func (j *JavaAttacher) attachJVMCommand(ctx context.Context, jvm *jvmDetails) *exec.Cmd {
attacherJar := bundledJavaAttacher
if j.tempAttacherDir != "" {
attacherJar = tmpJavaAttacherJarPath(j.tempAttacherDir)
}
eyalkoren marked this conversation as resolved.
Show resolved Hide resolved
args := []string{
"-jar", javaAttacher,
"-jar", attacherJar,
"--log-level", "debug",
"--include-pid", strconv.Itoa(jvm.pid),
}
Expand Down
46 changes: 43 additions & 3 deletions internal/beater/java_attacher/java_attacher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package javaattacher

import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
Expand All @@ -41,7 +42,7 @@ func TestNoAttacherCreatedWithoutDiscoveryRules(t *testing.T) {

func TestBuild(t *testing.T) {
cfg := createTestConfig()
f, err := os.Create(javaAttacher)
f, err := os.Create(bundledJavaAttacher)
require.NoError(t, err)
//goland:noinspection GoUnhandledErrorResult
defer os.Remove(f.Name())
Expand All @@ -60,6 +61,16 @@ func TestBuild(t *testing.T) {
cmdArgs := strings.Join(cmd.Args, " ")
assert.Equal(t, want, cmdArgs)

tmpDir, err := attacher.createAttacherTempDir()
require.NoError(t, err)
defer os.Remove(tmpDir)
cmd = attacher.attachJVMCommand(context.Background(), jvm)
want = filepath.FromSlash(fmt.Sprintf("/home/someuser/java_home/bin/java -jar %v/java-attacher.jar"+
" --log-level debug --include-pid 12345 --download-agent-version 1.27.0 --config server_url=http://myhost:8200", tmpDir))

cmdArgs = strings.Join(cmd.Args, " ")
assert.Equal(t, want, cmdArgs)

cfg.Config["service_name"] = "my-cool-service"
attacher, err = New(cfg)
require.NoError(t, err)
Expand Down Expand Up @@ -99,7 +110,7 @@ func TestDiscoveryRulesAllowlist(t *testing.T) {
Enabled: true,
DiscoveryRules: args,
}
f, err := os.Create(javaAttacher)
f, err := os.Create(bundledJavaAttacher)
require.NoError(t, err)
//goland:noinspection GoUnhandledErrorResult
defer os.Remove(f.Name())
Expand All @@ -125,7 +136,7 @@ func TestConfig(t *testing.T) {
},
DownloadAgentVersion: "1.25.0",
}
f, err := os.Create(javaAttacher)
f, err := os.Create(bundledJavaAttacher)
require.NoError(t, err)
//goland:noinspection GoUnhandledErrorResult
defer os.Remove(f.Name())
Expand Down Expand Up @@ -181,3 +192,32 @@ func TestConfig(t *testing.T) {
javaAttacher.discoveryRules[4] = &userDiscoveryRule{}
require.Nil(t, javaAttacher.findFirstMatch(&jvmDetails))
}

func TestTempDirCreation(t *testing.T) {
cfg := createTestConfig()
f, err := os.Create(bundledJavaAttacher)
require.NoError(t, err)
//goland:noinspection GoUnhandledErrorResult
defer os.Remove(f.Name())
attacher, err := New(cfg)
require.NoError(t, err)

tempAttacherDir, err := attacher.createAttacherTempDir()
require.NoError(t, err)
//goland:noinspection GoUnhandledErrorResult
defer os.RemoveAll(tempAttacherDir)
require.DirExists(t, tempAttacherDir)
attacherDirInfo, err := os.Stat(tempAttacherDir)
require.NoError(t, err)
// todo - this may fail on Windows
require.Equal(t, os.FileMode(0755), attacherDirInfo.Mode().Perm())
tempDir, err := os.Open(tempAttacherDir)
require.NoError(t, err)
files, err := tempDir.ReadDir(0)
require.NoError(t, err)
require.Len(t, files, 1)
attacherJarFileInfo, err := files[0].Info()
require.NoError(t, err)
require.Equal(t, os.FileMode(0755), attacherJarFileInfo.Mode())
require.FileExists(t, attacherJarFileInfo.Name())
}
Loading