dev container provisioning code 12-14-25
This commit is contained in:
parent
463baf80e0
commit
9c8e3a2b22
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/devcontainer"
|
||||||
"zlh-agent/internal/provision/devcontainer/goenv"
|
"zlh-agent/internal/provision/devcontainer/go"
|
||||||
"zlh-agent/internal/provision/devcontainer/java"
|
"zlh-agent/internal/provision/devcontainer/java"
|
||||||
"zlh-agent/internal/provision/devcontainer/node"
|
"zlh-agent/internal/provision/devcontainer/node"
|
||||||
"zlh-agent/internal/provision/devcontainer/python"
|
"zlh-agent/internal/provision/devcontainer/python"
|
||||||
|
|||||||
35
internal/provision/addons/addons.go
Normal file
35
internal/provision/addons/addons.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package addons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"zlh-agent/internal/state"
|
||||||
|
"zlh-agent/internal/provision/addons/codeserver"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Provision installs requested addons.
|
||||||
|
|
||||||
|
IMPORTANT:
|
||||||
|
- Addons are role-agnostic (dev/game/etc)
|
||||||
|
- This function ONLY installs
|
||||||
|
- Verification happens later (ensureProvisioned)
|
||||||
|
*/
|
||||||
|
func Provision(cfg state.Config) error {
|
||||||
|
|
||||||
|
for _, addon := range cfg.Addons {
|
||||||
|
|
||||||
|
switch addon {
|
||||||
|
|
||||||
|
case "codeserver":
|
||||||
|
if err := codeserver.Install(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported addon: %s", addon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
42
internal/provision/addons/codeserver/install.go
Normal file
42
internal/provision/addons/codeserver/install.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package codeserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"zlh-agent/internal/provision"
|
||||||
|
"zlh-agent/internal/provision/addons"
|
||||||
|
"zlh-agent/internal/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Install installs the code-server addon.
|
||||||
|
|
||||||
|
IMPORTANT:
|
||||||
|
- Addon-only (not devcontainer-specific)
|
||||||
|
- No assumptions about users or workspaces
|
||||||
|
- Idempotent via addon marker
|
||||||
|
*/
|
||||||
|
func Install(cfg state.Config) error {
|
||||||
|
|
||||||
|
if addons.IsAddonProvisioned("codeserver") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptPath := filepath.Join(
|
||||||
|
provision.ScriptsRoot,
|
||||||
|
"addons",
|
||||||
|
"codeserver",
|
||||||
|
"install.sh",
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := provision.RunScript(scriptPath); err != nil {
|
||||||
|
return fmt.Errorf("codeserver install failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addons.WriteAddonMarker("codeserver"); err != nil {
|
||||||
|
return fmt.Errorf("failed to write codeserver marker: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
internal/provision/addons/codeserver/verify.go
Normal file
22
internal/provision/addons/codeserver/verify.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package codeserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"zlh-agent/internal/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Verify validates that code-server is installed and usable.
|
||||||
|
|
||||||
|
This does NOT start the service.
|
||||||
|
*/
|
||||||
|
func Verify(cfg state.Config) error {
|
||||||
|
|
||||||
|
if _, err := exec.LookPath("code-server"); err != nil {
|
||||||
|
return fmt.Errorf("code-server binary not found in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
internal/provision/addons/markers.go
Normal file
24
internal/provision/addons/markers.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package addons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
addonMarkerDir = "/opt/zlh/.zlh/addons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsAddonProvisioned(name string) bool {
|
||||||
|
path := filepath.Join(addonMarkerDir, name)
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteAddonMarker(name string) error {
|
||||||
|
if err := os.MkdirAll(addonMarkerDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path := filepath.Join(addonMarkerDir, name)
|
||||||
|
return os.WriteFile(path, []byte("ok"), 0644)
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
|
"zlh-agent/internal/provision/addons"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/devcontainer"
|
||||||
"zlh-agent/internal/provision/minecraft"
|
"zlh-agent/internal/provision/minecraft"
|
||||||
"zlh-agent/internal/provision/steam"
|
"zlh-agent/internal/provision/steam"
|
||||||
@ -20,12 +21,16 @@ import (
|
|||||||
func ProvisionAll(cfg state.Config) error {
|
func ProvisionAll(cfg state.Config) error {
|
||||||
|
|
||||||
/* ---------------------------------------------------------
|
/* ---------------------------------------------------------
|
||||||
DEV CONTAINERS
|
BASE ROLE PROVISIONING
|
||||||
--------------------------------------------------------- */
|
--------------------------------------------------------- */
|
||||||
if cfg.ContainerType == "dev" {
|
if cfg.ContainerType == "dev" {
|
||||||
return devcontainer.Provision(cfg)
|
|
||||||
|
if err := devcontainer.Provision(cfg); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
// Legacy / default behavior assumes game containers
|
// Legacy / default behavior assumes game containers
|
||||||
game := strings.ToLower(cfg.Game)
|
game := strings.ToLower(cfg.Game)
|
||||||
variant := strings.ToLower(cfg.Variant)
|
variant := strings.ToLower(cfg.Variant)
|
||||||
@ -73,14 +78,11 @@ func ProvisionAll(cfg state.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT VERIFY HERE.
|
} else if IsSteamGame(game) {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------
|
/* ---------------------------------------------------------
|
||||||
STEAM GAMES
|
STEAM GAMES
|
||||||
--------------------------------------------------------- */
|
--------------------------------------------------------- */
|
||||||
if IsSteamGame(game) {
|
|
||||||
|
|
||||||
// 1. SteamCMD install
|
// 1. SteamCMD install
|
||||||
if err := steam.EnsureSteamCMD(); err != nil {
|
if err := steam.EnsureSteamCMD(); err != nil {
|
||||||
@ -119,16 +121,23 @@ func ProvisionAll(cfg state.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT VERIFY HERE.
|
} else {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------
|
|
||||||
UNKNOWN CONTAINER TYPE
|
|
||||||
--------------------------------------------------------- */
|
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"unsupported container identity (containerType=%q game=%q)",
|
"unsupported container identity (containerType=%q game=%q)",
|
||||||
cfg.ContainerType,
|
cfg.ContainerType,
|
||||||
cfg.Game,
|
cfg.Game,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------
|
||||||
|
ADDONS (OPTIONAL, ROLE-AGNOSTIC)
|
||||||
|
--------------------------------------------------------- */
|
||||||
|
if len(cfg.Addons) > 0 {
|
||||||
|
if err := addons.Provision(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -14,6 +14,16 @@ import (
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
VMID int `json:"vmid"`
|
VMID int `json:"vmid"`
|
||||||
|
|
||||||
|
// Container identity
|
||||||
|
ContainerType string `json:"container_type,omitempty"`
|
||||||
|
|
||||||
|
// Dev runtime (only for dev containers)
|
||||||
|
Runtime string `json:"runtime,omitempty"`
|
||||||
|
|
||||||
|
// OPTIONAL addons (role-agnostic)
|
||||||
|
Addons []string `json:"addons,omitempty"`
|
||||||
|
|
||||||
Game string `json:"game"`
|
Game string `json:"game"`
|
||||||
Variant string `json:"variant"`
|
Variant string `json:"variant"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@ -32,6 +42,8 @@ type Config struct {
|
|||||||
AdminPass string `json:"admin_pass,omitempty"`
|
AdminPass string `json:"admin_pass,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
AGENT STATE ENUM
|
AGENT STATE ENUM
|
||||||
----------------------------------------------------------------------------*/
|
----------------------------------------------------------------------------*/
|
||||||
|
|||||||
9
scripts/addons/codeserver/install.sh
Normal file
9
scripts/addons/codeserver/install.sh
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "[addon:codeserver] installing code-server"
|
||||||
|
|
||||||
|
# Official install script (can be replaced later if you want full control)
|
||||||
|
curl -fsSL https://code-server.dev/install.sh | sh
|
||||||
|
|
||||||
|
echo "[addon:codeserver] install complete"
|
||||||
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] starting install"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/../workspace.sh"
|
||||||
|
|
||||||
# install runtime
|
echo "[devcontainer:go] installing golang"
|
||||||
# install basic tooling
|
|
||||||
# ensure binaries land in PATH
|
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] install complete"
|
apt update
|
||||||
|
apt install -y golang
|
||||||
|
|
||||||
|
echo "[devcontainer:go] install complete"
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] starting install"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/../workspace.sh"
|
||||||
|
|
||||||
# install runtime
|
echo "[devcontainer:java] installing jdk"
|
||||||
# install basic tooling
|
|
||||||
# ensure binaries land in PATH
|
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] install complete"
|
apt update
|
||||||
|
apt install -y openjdk-21-jdk
|
||||||
|
|
||||||
|
echo "[devcontainer:java] install complete"
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "Installing Node.js dev container runtime"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/../workspace.sh"
|
||||||
|
|
||||||
|
echo "[devcontainer:node] installing nodejs"
|
||||||
|
|
||||||
# example (placeholder)
|
|
||||||
apt update
|
apt update
|
||||||
apt install -y nodejs npm
|
apt install -y nodejs npm
|
||||||
|
|
||||||
echo "Node version:"
|
echo "[devcontainer:node] install complete"
|
||||||
node --version
|
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] starting install"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/../workspace.sh"
|
||||||
|
|
||||||
# install runtime
|
echo "[devcontainer:python] installing python"
|
||||||
# install basic tooling
|
|
||||||
# ensure binaries land in PATH
|
|
||||||
|
|
||||||
echo "[devcontainer:<runtime>] install complete"
|
apt update
|
||||||
|
apt install -y python3 python3-pip python3-venv
|
||||||
|
|
||||||
|
echo "[devcontainer:python] install complete"
|
||||||
|
|||||||
22
scripts/workspace.sh
Normal file
22
scripts/workspace.sh
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DEV_USER="devuser"
|
||||||
|
DEV_HOME="/home/${DEV_USER}"
|
||||||
|
WORKSPACE="${DEV_HOME}/Workspace"
|
||||||
|
|
||||||
|
echo "[workspace] ensuring dev user and workspace"
|
||||||
|
|
||||||
|
# Create user if missing
|
||||||
|
if ! id "${DEV_USER}" &>/dev/null; then
|
||||||
|
useradd -m -s /bin/bash "${DEV_USER}"
|
||||||
|
echo "[workspace] created user ${DEV_USER}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure workspace exists
|
||||||
|
mkdir -p "${WORKSPACE}"
|
||||||
|
|
||||||
|
# Ensure ownership
|
||||||
|
chown -R "${DEV_USER}:${DEV_USER}" "${DEV_HOME}"
|
||||||
|
|
||||||
|
echo "[workspace] ready (${DEV_USER}:${WORKSPACE})"
|
||||||
Loading…
Reference in New Issue
Block a user