Skip to content

Commit 813c927

Browse files
committed
Add Nginx service orchestration
1 parent bd0cdb3 commit 813c927

10 files changed

Lines changed: 398 additions & 19 deletions

File tree

‎cmd/install.go‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,27 @@ var installCmd = &cobra.Command{
2727
if err != nil {
2828
return err
2929
}
30-
return installPHP(m.Runtime.PHP)
30+
if err := installPHP(m.Runtime.PHP); err != nil {
31+
return err
32+
}
33+
if m.Server != nil && m.Server.Engine == "nginx" {
34+
return installNginx("1.30")
35+
}
36+
return nil
3137
}
3238

3339
runtime, version, err := parseRuntimeSpec(args[0])
3440
if err != nil {
3541
return err
3642
}
37-
if runtime != "php" {
38-
return fmt.Errorf("install currently supports PHP only")
43+
switch runtime {
44+
case "php":
45+
return installPHP(version)
46+
case "nginx":
47+
return installNginx(version)
48+
default:
49+
return fmt.Errorf("install currently supports php and nginx")
3950
}
40-
return installPHP(version)
4151
},
4252
}
4353

@@ -53,6 +63,18 @@ func installPHP(version string) error {
5363
return packs.InstallPHP(exact, info, installForce)
5464
}
5565

66+
func installNginx(version string) error {
67+
reg, err := registry.Load()
68+
if err != nil {
69+
return err
70+
}
71+
exact, info, err := reg.ResolveNginx(version)
72+
if err != nil {
73+
return err
74+
}
75+
return packs.InstallNginx(exact, info, installForce)
76+
}
77+
5678
func parseRuntimeSpec(spec string) (string, string, error) {
5779
parts := strings.Split(spec, "@")
5880
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {

‎cmd/list.go‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,24 @@ var listCmd = &cobra.Command{
2121
}
2222

2323
if listAvailable {
24-
if runtime != "php" {
25-
return fmt.Errorf("available registry listing currently supports php only")
26-
}
2724
reg, err := registry.Load()
2825
if err != nil {
2926
return err
3027
}
31-
versions := reg.AvailablePHP()
28+
var versions []string
29+
switch runtime {
30+
case "php":
31+
versions = reg.AvailablePHP()
32+
case "nginx":
33+
versions = reg.AvailableNginx()
34+
default:
35+
return fmt.Errorf("available registry listing currently supports php and nginx")
36+
}
3237
if len(versions) == 0 {
33-
fmt.Println("No PHP versions available in registry.")
38+
fmt.Printf("No %s versions available in registry.\n", runtime)
3439
return nil
3540
}
36-
fmt.Println("Available PHP runtimes:")
41+
fmt.Printf("Available %s runtimes:\n", runtime)
3742
for _, version := range versions {
3843
fmt.Printf(" %s\n", version)
3944
}

‎internal/packs/services.go‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package packs
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/Vikrez22/phpvm/internal/downloader"
9+
"github.com/Vikrez22/phpvm/internal/paths"
10+
"github.com/Vikrez22/phpvm/registry"
11+
)
12+
13+
func InstallNginx(version string, info *registry.BuildInfo, force bool) error {
14+
destDir := paths.ServiceDir("nginx", version)
15+
if _, err := os.Stat(destDir); err == nil {
16+
if !force {
17+
return fmt.Errorf("Nginx %s is already installed. Use --force to reinstall.", version)
18+
}
19+
if err := os.RemoveAll(destDir); err != nil {
20+
return fmt.Errorf("cannot remove existing Nginx %s: %w", version, err)
21+
}
22+
}
23+
24+
fmt.Printf("Installing Nginx %s...\n", version)
25+
zipPath, err := downloader.Download(info.URL, downloader.TempDownloadDir(), filepath.Base(info.URL))
26+
if err != nil {
27+
return err
28+
}
29+
30+
fmt.Print("Verifying checksum... ")
31+
if err := downloader.VerifySHA256(zipPath, info.SHA256); err != nil {
32+
_ = os.Remove(zipPath)
33+
return err
34+
}
35+
fmt.Println("ok")
36+
37+
stagingDir := filepath.Join(os.TempDir(), "phpvm", "staging", "nginx-"+version)
38+
_ = os.RemoveAll(stagingDir)
39+
if err := extractZip(zipPath, stagingDir); err != nil {
40+
return fmt.Errorf("extraction failed: %w", err)
41+
}
42+
43+
topLevel, err := findSingleDir(stagingDir)
44+
if err != nil {
45+
topLevel = stagingDir
46+
}
47+
48+
if err := os.MkdirAll(filepath.Dir(destDir), 0755); err != nil {
49+
return err
50+
}
51+
if err := os.Rename(topLevel, destDir); err != nil {
52+
return fmt.Errorf("cannot install Nginx: %w", err)
53+
}
54+
_ = os.RemoveAll(stagingDir)
55+
_ = os.Remove(zipPath)
56+
57+
fmt.Printf("Nginx %s installed\n", version)
58+
return nil
59+
}
60+
61+
func findSingleDir(parent string) (string, error) {
62+
entries, err := os.ReadDir(parent)
63+
if err != nil {
64+
return "", err
65+
}
66+
var dirs []string
67+
for _, entry := range entries {
68+
if entry.IsDir() {
69+
dirs = append(dirs, filepath.Join(parent, entry.Name()))
70+
}
71+
}
72+
if len(dirs) != 1 {
73+
return "", fmt.Errorf("expected one top-level directory, found %d", len(dirs))
74+
}
75+
return dirs[0], nil
76+
}

‎internal/state/state.go‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,28 @@ func (s *ProjectState) AllocatePort(service string, preferred int) (int, error)
8787
if existing, ok := s.AllocatedPorts[service]; ok {
8888
return existing, nil
8989
}
90-
port, err := findFreePort(preferred)
90+
reserved := map[int]bool{}
91+
for _, port := range s.AllocatedPorts {
92+
reserved[port] = true
93+
}
94+
port, err := findFreePort(preferred, reserved)
9195
if err != nil {
9296
return 0, err
9397
}
9498
s.AllocatedPorts[service] = port
9599
return port, nil
96100
}
97101

98-
func findFreePort(preferred int) (int, error) {
99-
if preferred > 0 {
102+
func findFreePort(preferred int, reserved map[int]bool) (int, error) {
103+
if preferred > 0 && !reserved[preferred] {
100104
if isPortFree(preferred) {
101105
return preferred, nil
102106
}
103107
}
104108
for port := 8080; port < 9000; port++ {
109+
if reserved[port] {
110+
continue
111+
}
105112
if isPortFree(port) {
106113
return port, nil
107114
}

‎internal/state/state_test.go‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,18 @@ func TestHashProjectStable(t *testing.T) {
4444
t.Fatalf("hashes differ: %q vs %q", a, b)
4545
}
4646
}
47+
48+
func TestAllocatePortDoesNotReuseReservedPort(t *testing.T) {
49+
s := New(t.TempDir())
50+
first, err := s.AllocatePort("web", 0)
51+
if err != nil {
52+
t.Fatalf("AllocatePort web returned error: %v", err)
53+
}
54+
second, err := s.AllocatePort("php-cgi", 0)
55+
if err != nil {
56+
t.Fatalf("AllocatePort php-cgi returned error: %v", err)
57+
}
58+
if first == second {
59+
t.Fatalf("ports reused: %d", first)
60+
}
61+
}

0 commit comments

Comments
 (0)