mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-05-01 06:24:12 +00:00
refactor(cli): extract AddEnvironment/RemoveEnvironment functions
- support upsert in AddEnvironment, no-op RemoveEnvironment if absent - fallback active env to devnet on remove, add tests - integrate with sandbox create/destroy, ignore core/plans/
This commit is contained in:
parent
fd59131ff4
commit
318eea33ae
3
.gitignore
vendored
3
.gitignore
vendored
@ -89,3 +89,6 @@ os/output/
|
|||||||
.dev/
|
.dev/
|
||||||
.local/
|
.local/
|
||||||
local/
|
local/
|
||||||
|
|
||||||
|
# Implementation plans (not committed)
|
||||||
|
core/plans/
|
||||||
|
|||||||
@ -164,30 +164,8 @@ func handleEnvAdd(args []string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
envConfig, err := LoadEnvironmentConfig()
|
if err := AddEnvironment(name, gatewayURL, description); err != nil {
|
||||||
if err != nil {
|
fmt.Fprintf(os.Stderr, "❌ Failed to add environment: %v\n", err)
|
||||||
fmt.Fprintf(os.Stderr, "❌ Failed to load environment config: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if environment already exists
|
|
||||||
for _, env := range envConfig.Environments {
|
|
||||||
if env.Name == name {
|
|
||||||
fmt.Fprintf(os.Stderr, "❌ Environment '%s' already exists\n", name)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new environment
|
|
||||||
envConfig.Environments = append(envConfig.Environments, Environment{
|
|
||||||
Name: name,
|
|
||||||
GatewayURL: gatewayURL,
|
|
||||||
Description: description,
|
|
||||||
IsActive: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := SaveEnvironmentConfig(envConfig); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "❌ Failed to save environment config: %v\n", err)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,37 +184,8 @@ func handleEnvRemove(args []string) {
|
|||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
envConfig, err := LoadEnvironmentConfig()
|
if err := RemoveEnvironment(name); err != nil {
|
||||||
if err != nil {
|
fmt.Fprintf(os.Stderr, "❌ Failed to remove environment: %v\n", err)
|
||||||
fmt.Fprintf(os.Stderr, "❌ Failed to load environment config: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find and remove environment
|
|
||||||
found := false
|
|
||||||
newEnvs := make([]Environment, 0, len(envConfig.Environments))
|
|
||||||
for _, env := range envConfig.Environments {
|
|
||||||
if env.Name == name {
|
|
||||||
found = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newEnvs = append(newEnvs, env)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
fmt.Fprintf(os.Stderr, "❌ Environment '%s' not found\n", name)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
envConfig.Environments = newEnvs
|
|
||||||
|
|
||||||
// If we removed the active environment, switch to devnet
|
|
||||||
if envConfig.ActiveEnvironment == name {
|
|
||||||
envConfig.ActiveEnvironment = "devnet"
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := SaveEnvironmentConfig(envConfig); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "❌ Failed to save environment config: %v\n", err)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,8 +45,11 @@ var DefaultEnvironments = []Environment{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvironmentConfigPath returns the path to the environment config file
|
// getEnvironmentConfigPathFn is the function used to resolve the config path.
|
||||||
func GetEnvironmentConfigPath() (string, error) {
|
// Tests override this to point at a temp file.
|
||||||
|
var getEnvironmentConfigPathFn = getEnvironmentConfigPathDefault
|
||||||
|
|
||||||
|
func getEnvironmentConfigPathDefault() (string, error) {
|
||||||
configDir, err := config.ConfigDir()
|
configDir, err := config.ConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get config directory: %w", err)
|
return "", fmt.Errorf("failed to get config directory: %w", err)
|
||||||
@ -54,6 +57,11 @@ func GetEnvironmentConfigPath() (string, error) {
|
|||||||
return filepath.Join(configDir, "environments.json"), nil
|
return filepath.Join(configDir, "environments.json"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEnvironmentConfigPath returns the path to the environment config file
|
||||||
|
func GetEnvironmentConfigPath() (string, error) {
|
||||||
|
return getEnvironmentConfigPathFn()
|
||||||
|
}
|
||||||
|
|
||||||
// LoadEnvironmentConfig loads the environment configuration
|
// LoadEnvironmentConfig loads the environment configuration
|
||||||
func LoadEnvironmentConfig() (*EnvironmentConfig, error) {
|
func LoadEnvironmentConfig() (*EnvironmentConfig, error) {
|
||||||
path, err := GetEnvironmentConfigPath()
|
path, err := GetEnvironmentConfigPath()
|
||||||
@ -170,6 +178,63 @@ func GetEnvironmentByName(name string) (*Environment, error) {
|
|||||||
return nil, fmt.Errorf("environment '%s' not found", name)
|
return nil, fmt.Errorf("environment '%s' not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddEnvironment adds a new environment or updates an existing one.
|
||||||
|
// If an environment with the same name already exists, its gateway URL and
|
||||||
|
// description are updated in place.
|
||||||
|
func AddEnvironment(name, gatewayURL, description string) error {
|
||||||
|
envConfig, err := LoadEnvironmentConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, env := range envConfig.Environments {
|
||||||
|
if env.Name == name {
|
||||||
|
envConfig.Environments[i].GatewayURL = gatewayURL
|
||||||
|
envConfig.Environments[i].Description = description
|
||||||
|
return SaveEnvironmentConfig(envConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
envConfig.Environments = append(envConfig.Environments, Environment{
|
||||||
|
Name: name,
|
||||||
|
GatewayURL: gatewayURL,
|
||||||
|
Description: description,
|
||||||
|
})
|
||||||
|
|
||||||
|
return SaveEnvironmentConfig(envConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEnvironment removes an environment by name. If the removed environment
|
||||||
|
// was active, the active environment falls back to "devnet".
|
||||||
|
func RemoveEnvironment(name string) error {
|
||||||
|
envConfig, err := LoadEnvironmentConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newEnvs := make([]Environment, 0, len(envConfig.Environments))
|
||||||
|
found := false
|
||||||
|
for _, env := range envConfig.Environments {
|
||||||
|
if env.Name == name {
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newEnvs = append(newEnvs, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil // already absent, nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
envConfig.Environments = newEnvs
|
||||||
|
|
||||||
|
if envConfig.ActiveEnvironment == name {
|
||||||
|
envConfig.ActiveEnvironment = "devnet"
|
||||||
|
}
|
||||||
|
|
||||||
|
return SaveEnvironmentConfig(envConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// InitializeEnvironments initializes the environment config with defaults
|
// InitializeEnvironments initializes the environment config with defaults
|
||||||
func InitializeEnvironments() error {
|
func InitializeEnvironments() error {
|
||||||
path, err := GetEnvironmentConfigPath()
|
path, err := GetEnvironmentConfigPath()
|
||||||
|
|||||||
131
core/pkg/cli/environment_test.go
Normal file
131
core/pkg/cli/environment_test.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// writeTestConfig writes an EnvironmentConfig to a temp file and returns
|
||||||
|
// a helper that patches GetEnvironmentConfigPath to return that path.
|
||||||
|
// The returned cleanup restores the original function.
|
||||||
|
func writeTestConfig(t *testing.T, cfg *EnvironmentConfig) func() {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
f, err := os.CreateTemp(t.TempDir(), "envconfig-*.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create temp file: %v", err)
|
||||||
|
}
|
||||||
|
data, _ := json.MarshalIndent(cfg, "", " ")
|
||||||
|
if _, err := f.Write(data); err != nil {
|
||||||
|
t.Fatalf("write temp file: %v", err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
origFn := getEnvironmentConfigPathFn
|
||||||
|
getEnvironmentConfigPathFn = func() (string, error) { return f.Name(), nil }
|
||||||
|
return func() { getEnvironmentConfigPathFn = origFn }
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultTestConfig() *EnvironmentConfig {
|
||||||
|
return &EnvironmentConfig{
|
||||||
|
Environments: []Environment{
|
||||||
|
{Name: "sandbox", GatewayURL: "https://dbrs.space", Description: "Sandbox cluster"},
|
||||||
|
{Name: "devnet", GatewayURL: "https://orama-devnet.network", Description: "Development network"},
|
||||||
|
{Name: "testnet", GatewayURL: "https://orama-testnet.network", Description: "Test network"},
|
||||||
|
},
|
||||||
|
ActiveEnvironment: "sandbox",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddEnvironment_new(t *testing.T) {
|
||||||
|
cleanup := writeTestConfig(t, defaultTestConfig())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := AddEnvironment("staging", "https://staging.example.com", "Staging env"); err != nil {
|
||||||
|
t.Fatalf("AddEnvironment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := GetEnvironmentByName("staging")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetEnvironmentByName: %v", err)
|
||||||
|
}
|
||||||
|
if env.GatewayURL != "https://staging.example.com" {
|
||||||
|
t.Errorf("GatewayURL = %q, want %q", env.GatewayURL, "https://staging.example.com")
|
||||||
|
}
|
||||||
|
if env.Description != "Staging env" {
|
||||||
|
t.Errorf("Description = %q, want %q", env.Description, "Staging env")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddEnvironment_update(t *testing.T) {
|
||||||
|
cleanup := writeTestConfig(t, defaultTestConfig())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := AddEnvironment("sandbox", "https://new.example.com", "Updated sandbox"); err != nil {
|
||||||
|
t.Fatalf("AddEnvironment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := GetEnvironmentByName("sandbox")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetEnvironmentByName: %v", err)
|
||||||
|
}
|
||||||
|
if env.GatewayURL != "https://new.example.com" {
|
||||||
|
t.Errorf("GatewayURL = %q, want %q", env.GatewayURL, "https://new.example.com")
|
||||||
|
}
|
||||||
|
if env.Description != "Updated sandbox" {
|
||||||
|
t.Errorf("Description = %q, want %q", env.Description, "Updated sandbox")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify upsert didn't create a duplicate
|
||||||
|
cfg, _ := LoadEnvironmentConfig()
|
||||||
|
count := 0
|
||||||
|
for _, e := range cfg.Environments {
|
||||||
|
if e.Name == "sandbox" {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("sandbox entries = %d, want 1", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveEnvironment_existing(t *testing.T) {
|
||||||
|
cleanup := writeTestConfig(t, defaultTestConfig())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := RemoveEnvironment("testnet"); err != nil {
|
||||||
|
t.Fatalf("RemoveEnvironment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := GetEnvironmentByName("testnet")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for removed environment, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveEnvironment_absent(t *testing.T) {
|
||||||
|
cleanup := writeTestConfig(t, defaultTestConfig())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := RemoveEnvironment("nonexistent"); err != nil {
|
||||||
|
t.Errorf("RemoveEnvironment(absent) = %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveEnvironment_active_falls_back(t *testing.T) {
|
||||||
|
cleanup := writeTestConfig(t, defaultTestConfig())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := RemoveEnvironment("sandbox"); err != nil {
|
||||||
|
t.Fatalf("RemoveEnvironment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := LoadEnvironmentConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadEnvironmentConfig: %v", err)
|
||||||
|
}
|
||||||
|
if cfg.ActiveEnvironment != "devnet" {
|
||||||
|
t.Errorf("ActiveEnvironment = %q, want %q", cfg.ActiveEnvironment, "devnet")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DeBrosOfficial/network/pkg/cli"
|
||||||
"github.com/DeBrosOfficial/network/pkg/cli/remotessh"
|
"github.com/DeBrosOfficial/network/pkg/cli/remotessh"
|
||||||
"github.com/DeBrosOfficial/network/pkg/inspector"
|
"github.com/DeBrosOfficial/network/pkg/inspector"
|
||||||
"github.com/DeBrosOfficial/network/pkg/rwagent"
|
"github.com/DeBrosOfficial/network/pkg/rwagent"
|
||||||
@ -144,6 +145,15 @@ func Create(name string) error {
|
|||||||
return fmt.Errorf("save final state: %w", err)
|
return fmt.Errorf("save final state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register sandbox as an environment and switch to it
|
||||||
|
gatewayURL := "https://" + cfg.Domain
|
||||||
|
desc := fmt.Sprintf("Sandbox cluster: %s (%s)", state.Name, cfg.Domain)
|
||||||
|
if err := cli.AddEnvironment("sandbox", gatewayURL, desc); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Warning: failed to register sandbox environment: %v\n", err)
|
||||||
|
} else if err := cli.SwitchEnvironment("sandbox"); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Warning: failed to switch to sandbox environment: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
printCreateSummary(cfg, state)
|
printCreateSummary(cfg, state)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/DeBrosOfficial/network/pkg/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Destroy tears down a sandbox cluster.
|
// Destroy tears down a sandbox cluster.
|
||||||
@ -100,6 +102,11 @@ func Destroy(name string, force bool) error {
|
|||||||
return fmt.Errorf("delete state: %w", err)
|
return fmt.Errorf("delete state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove sandbox environment entry, fall back to devnet
|
||||||
|
if err := cli.RemoveEnvironment("sandbox"); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Warning: failed to remove sandbox environment: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("\nSandbox %q destroyed (%d servers deleted)\n", state.Name, len(state.Servers))
|
fmt.Printf("\nSandbox %q destroyed (%d servers deleted)\n", state.Name, len(state.Servers))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -390,7 +390,17 @@ func (ci *CaddyInstaller) generateCaddyfile(domain, email, acmeEndpoint, baseDom
|
|||||||
sb.WriteString(fmt.Sprintf("\n%s {\n%s\n reverse_proxy localhost:6001\n}\n", baseDomain, tlsBlock))
|
sb.WriteString(fmt.Sprintf("\n%s {\n%s\n reverse_proxy localhost:6001\n}\n", baseDomain, tlsBlock))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP fallback (handles plain HTTP and ACME challenges)
|
// HTTP blocks — serve traffic over plain HTTP so the gateway is reachable
|
||||||
|
// even when TLS certificates are unavailable (e.g., Let's Encrypt rate limits).
|
||||||
|
// Without these, Caddy auto-redirects HTTP→HTTPS for the named domain blocks above.
|
||||||
|
sb.WriteString(fmt.Sprintf("\nhttp://*.%s {\n reverse_proxy localhost:6001\n}\n", domain))
|
||||||
|
sb.WriteString(fmt.Sprintf("\nhttp://%s {\n reverse_proxy localhost:6001\n}\n", domain))
|
||||||
|
if baseDomain != "" && baseDomain != domain {
|
||||||
|
sb.WriteString(fmt.Sprintf("\nhttp://*.%s {\n reverse_proxy localhost:6001\n}\n", baseDomain))
|
||||||
|
sb.WriteString(fmt.Sprintf("\nhttp://%s {\n reverse_proxy localhost:6001\n}\n", baseDomain))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP catch-all fallback (handles remaining plain HTTP traffic)
|
||||||
sb.WriteString("\n:80 {\n reverse_proxy localhost:6001\n}\n")
|
sb.WriteString("\n:80 {\n reverse_proxy localhost:6001\n}\n")
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user