feat: backend executor implement
This commit is contained in:
@@ -19,7 +19,11 @@ const (
|
||||
// DefaultPath is the only default location used by the daemon entrypoint.
|
||||
DefaultPath = "/etc/yms-daemon/yms-daemon.toml"
|
||||
|
||||
BackendTypeNative = "native"
|
||||
EnvironmentDev = "dev"
|
||||
EnvironmentProd = "prod"
|
||||
|
||||
BackendTypeNative = "native"
|
||||
BackendTypeContainer = "container"
|
||||
|
||||
BackendPort8080 = 8080
|
||||
BackendPort8081 = 8081
|
||||
@@ -34,14 +38,32 @@ const (
|
||||
nativeSlotJAR8081 = "/home/yms/lib/glory-soft-yms-8081.jar"
|
||||
nativeHealthURL8080 = "http://127.0.0.1:8080/yms/actuator/health"
|
||||
nativeHealthURL8081 = "http://127.0.0.1:8081/yms/actuator/health"
|
||||
containerName8080 = "backend-8080"
|
||||
containerName8081 = "backend-8081"
|
||||
)
|
||||
|
||||
const (
|
||||
ContainerConfigSource = "/home/yms/conf/yms.yaml"
|
||||
ContainerConfigTarget = "/app/config/yms.yaml"
|
||||
ContainerTmpSource = "/home/yms/tmp"
|
||||
ContainerTmpTarget = "/home/yms/tmp"
|
||||
ContainerPortEnvironment = "SERVER_PORT"
|
||||
ContainerConfigEnvironment = "SPRING_CONFIG_LOCATION"
|
||||
ContainerConfigLocation = "file:/app/config/yms.yaml"
|
||||
)
|
||||
|
||||
// Config is the complete local deployment configuration currently understood by the daemon.
|
||||
type Config struct {
|
||||
Daemon Daemon `toml:"daemon"`
|
||||
Backend Backend `toml:"backend"`
|
||||
}
|
||||
|
||||
// Backend describes the explicitly selected backend runtime and its native blue/green slots.
|
||||
// Daemon contains machine-wide behavior that is independent of a component runtime.
|
||||
type Daemon struct {
|
||||
Environment string `toml:"environment"`
|
||||
}
|
||||
|
||||
// Backend describes the explicitly selected backend runtime and its blue/green slots.
|
||||
type Backend struct {
|
||||
Type string `toml:"type"`
|
||||
ReleaseDir string `toml:"release_dir"`
|
||||
@@ -50,20 +72,21 @@ type Backend struct {
|
||||
Slot BackendSlots `toml:"slot"`
|
||||
}
|
||||
|
||||
// BackendSlots lists the only native backend ports supported by the current deployment contract.
|
||||
// BackendSlots lists the only backend ports supported by the current deployment contract.
|
||||
type BackendSlots struct {
|
||||
Port8080 BackendSlot `toml:"8080"`
|
||||
Port8081 BackendSlot `toml:"8081"`
|
||||
}
|
||||
|
||||
// BackendSlot contains values that are passed to the native backend executor without derivation.
|
||||
// BackendSlot contains exact values for one native or container backend slot.
|
||||
type BackendSlot struct {
|
||||
Unit string `toml:"unit"`
|
||||
JAR string `toml:"jar"`
|
||||
ContainerName string `toml:"container_name"`
|
||||
HealthEndpoint string `toml:"health_endpoint"`
|
||||
}
|
||||
|
||||
// Load opens path, performs strict TOML decoding, and validates the native backend contract.
|
||||
// Load opens path, performs strict TOML decoding, and validates the selected backend contract.
|
||||
func Load(path string) (Config, error) {
|
||||
if err := validateAbsolutePath("deployment configuration", path); err != nil {
|
||||
return Config{}, err
|
||||
@@ -106,7 +129,14 @@ func validateExactDocumentKeys(document []byte) error {
|
||||
if err := toml.Unmarshal(document, &root); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(root, "", "backend"); err != nil {
|
||||
if err := rejectUnknownKeys(root, "", "daemon", "backend"); err != nil {
|
||||
return err
|
||||
}
|
||||
daemon, err := exactTable(root, "", "daemon")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(daemon, "daemon", "environment"); err != nil {
|
||||
return err
|
||||
}
|
||||
backend, err := exactTable(root, "", "backend")
|
||||
@@ -128,7 +158,7 @@ func validateExactDocumentKeys(document []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(slot, "backend.slot."+port, "unit", "jar", "health_endpoint"); err != nil {
|
||||
if err := rejectUnknownKeys(slot, "backend.slot."+port, "unit", "jar", "container_name", "health_endpoint"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -176,23 +206,46 @@ func rejectUnknownKeys(table map[string]any, parent string, allowed ...string) e
|
||||
|
||||
// Validate rejects incomplete or altered local deployment identifiers.
|
||||
func (c Config) Validate() error {
|
||||
if c.Backend.Type != BackendTypeNative {
|
||||
return fmt.Errorf("backend.type must be %q", BackendTypeNative)
|
||||
}
|
||||
if c.Backend.ReleaseDir != nativeReleaseDir {
|
||||
return fmt.Errorf("backend.release_dir must be %q", nativeReleaseDir)
|
||||
}
|
||||
if c.Backend.ActiveJAR != nativeActiveJAR {
|
||||
return fmt.Errorf("backend.active_jar must be %q", nativeActiveJAR)
|
||||
switch c.Daemon.Environment {
|
||||
case EnvironmentDev, EnvironmentProd:
|
||||
default:
|
||||
return fmt.Errorf("daemon.environment must be %q or %q", EnvironmentDev, EnvironmentProd)
|
||||
}
|
||||
if err := validateAbsolutePath("backend.systemctl_path", c.Backend.SystemctlPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateSlot("backend.slot.8080", c.Backend.Slot.Port8080, nativeUnit8080, nativeSlotJAR8080, nativeHealthURL8080); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateSlot("backend.slot.8081", c.Backend.Slot.Port8081, nativeUnit8081, nativeSlotJAR8081, nativeHealthURL8081); err != nil {
|
||||
return err
|
||||
switch c.Backend.Type {
|
||||
case BackendTypeNative:
|
||||
if c.Backend.Slot.Port8080.ContainerName != "" || c.Backend.Slot.Port8081.ContainerName != "" {
|
||||
return errors.New("native backend slots do not accept container_name")
|
||||
}
|
||||
if c.Backend.ReleaseDir != nativeReleaseDir {
|
||||
return fmt.Errorf("backend.release_dir must be %q", nativeReleaseDir)
|
||||
}
|
||||
if c.Backend.ActiveJAR != nativeActiveJAR {
|
||||
return fmt.Errorf("backend.active_jar must be %q", nativeActiveJAR)
|
||||
}
|
||||
if err := validateNativeSlot("backend.slot.8080", c.Backend.Slot.Port8080, nativeUnit8080, nativeSlotJAR8080, nativeHealthURL8080); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateNativeSlot("backend.slot.8081", c.Backend.Slot.Port8081, nativeUnit8081, nativeSlotJAR8081, nativeHealthURL8081); err != nil {
|
||||
return err
|
||||
}
|
||||
case BackendTypeContainer:
|
||||
if c.Backend.ReleaseDir != "" || c.Backend.ActiveJAR != "" {
|
||||
return errors.New("container backend does not accept release_dir or active_jar")
|
||||
}
|
||||
if c.Backend.Slot.Port8080.Unit != "" || c.Backend.Slot.Port8080.JAR != "" || c.Backend.Slot.Port8081.Unit != "" || c.Backend.Slot.Port8081.JAR != "" {
|
||||
return errors.New("container backend slots do not accept unit or jar")
|
||||
}
|
||||
if err := validateContainerSlot("backend.slot.8080", c.Backend.Slot.Port8080, containerName8080, nativeHealthURL8080); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateContainerSlot("backend.slot.8081", c.Backend.Slot.Port8081, containerName8081, nativeHealthURL8081); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("backend.type must be %q or %q", BackendTypeNative, BackendTypeContainer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -209,7 +262,7 @@ func (b Backend) SlotForPort(port int) (BackendSlot, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func validateSlot(field string, slot BackendSlot, unit string, jar string, endpoint string) error {
|
||||
func validateNativeSlot(field string, slot BackendSlot, unit string, jar string, endpoint string) error {
|
||||
if slot.Unit != unit {
|
||||
return fmt.Errorf("%s.unit must be %q", field, unit)
|
||||
}
|
||||
@@ -222,6 +275,16 @@ func validateSlot(field string, slot BackendSlot, unit string, jar string, endpo
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateContainerSlot(field string, slot BackendSlot, containerName string, endpoint string) error {
|
||||
if slot.ContainerName != containerName {
|
||||
return fmt.Errorf("%s.container_name must be %q", field, containerName)
|
||||
}
|
||||
if slot.HealthEndpoint != endpoint {
|
||||
return fmt.Errorf("%s.health_endpoint must be %q", field, endpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAbsolutePath(field string, value string) error {
|
||||
if value == "" {
|
||||
return fmt.Errorf("%s is required", field)
|
||||
|
||||
Reference in New Issue
Block a user