zlh-agent/internal/provision/java.go
2025-12-13 20:54:18 +00:00

134 lines
3.0 KiB
Go
Executable File

package provision
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"zlh-agent/internal/state"
)
// InstallJava downloads and extracts the Java runtime, then creates a stable
// symlink at /opt/zlh/runtime/java → <version>/bin/java
func InstallJava(cfg state.Config) error {
if cfg.JavaPath == "" {
return fmt.Errorf("java_path missing in config")
}
url := BuildArtifactURL(cfg.JavaPath)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("download java from %s: %w", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("download java: status %s", resp.Status)
}
runtimeRoot := JavaDir(cfg)
if err := os.MkdirAll(runtimeRoot, 0755); err != nil {
return fmt.Errorf("create java root dir: %w", err)
}
// Extract into temp directory
tmpDir := filepath.Join(runtimeRoot, "tmp-extract")
_ = os.RemoveAll(tmpDir)
if err := os.MkdirAll(tmpDir, 0755); err != nil {
return fmt.Errorf("create tmp java dir: %w", err)
}
if err := extractTarGz(resp.Body, tmpDir); err != nil {
return fmt.Errorf("extract java: %w", err)
}
// Detect root folder (JDK name)
entries, err := os.ReadDir(tmpDir)
if err != nil {
return fmt.Errorf("read tmp java dir: %w", err)
}
if len(entries) != 1 || !entries[0].IsDir() {
return fmt.Errorf("java extract: unexpected folder layout in %s", tmpDir)
}
rootFolder := entries[0].Name()
versionPath := filepath.Join(runtimeRoot, rootFolder)
// Move extracted Java dir into runtime/
_ = os.RemoveAll(versionPath)
if err := os.Rename(filepath.Join(tmpDir, rootFolder), versionPath); err != nil {
return fmt.Errorf("move java folder: %w", err)
}
// Cleanup temp
_ = os.RemoveAll(tmpDir)
// Make java binary executable
javaBin := filepath.Join(versionPath, "bin", "java")
if err := os.Chmod(javaBin, 0755); err != nil {
return fmt.Errorf("chmod java binary: %w", err)
}
// Symlink: java → <version>/bin/java
linkPath := filepath.Join(JavaRoot, "java")
_ = os.Remove(linkPath)
if err := os.Symlink(javaBin, linkPath); err != nil {
return fmt.Errorf("create java symlink: %w", err)
}
fmt.Println("[java] Installed runtime:", versionPath, "->", linkPath)
return nil
}
// extractTarGz restores the original signature returning only "error"
func extractTarGz(r io.Reader, dest string) error {
gz, err := gzip.NewReader(r)
if err != nil {
return err
}
defer gz.Close()
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
target := filepath.Join(dest, hdr.Name)
switch hdr.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err
}
f, err := os.Create(target)
if err != nil {
return err
}
if _, err := io.Copy(f, tr); err != nil {
f.Close()
return err
}
f.Close()
}
}
return nil
}