Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//go:build !windows

package main

import (
"fmt"
"net/http"
"os"
"strconv"
"syscall"
"time"
)

Expand Down Expand Up @@ -35,9 +38,7 @@ func main() {
go func() {
time.Sleep(ExitDelay)
if exitCode == SigabrtExitCode {
// Simulate SIGABRT behavior cross-platform
// Use panic to simulate abnormal termination (similar to SIGABRT)
panic("simulated SIGABRT exit")
syscall.Kill(syscall.Getpid(), syscall.SIGABRT)
} else {
os.Exit(exitCode)
}
Expand Down
28 changes: 12 additions & 16 deletions src/code.cloudfoundry.org/inigo/cell/assets/fake_app_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//go:build !windows

package assets_test

import (
"fmt"
"net/http"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"time"

. "github.com/onsi/ginkgo/v2"
Expand All @@ -27,10 +29,6 @@ var _ = Describe("Fake App Exit Behavior", Serial, func() {

AfterEach(func() {
gexec.CleanupBuildArtifacts()
// On Windows, add a small delay to ensure ports are released
if runtime.GOOS == "windows" {
time.Sleep(500 * time.Millisecond)
}
})

DescribeTable("fake app should exit with correct exit codes",
Expand Down Expand Up @@ -66,9 +64,13 @@ var _ = Describe("Fake App Exit Behavior", Serial, func() {
// Verify exit code
actualExitCode := session.ExitCode()
if exitCode == 134 {
// SIGABRT simulation via panic results in exit code 2 cross-platform
Expect(actualExitCode).To(Equal(2),
fmt.Sprintf("Expected panic exit code 2 for SIGABRT simulation, got %d", actualExitCode))
// SIGABRT can result in different exit codes depending on system
Expect(actualExitCode).To(SatisfyAny(
Equal(134), // Direct SIGABRT exit code
Equal(2), // Common SIGABRT exit code
Equal(128+int(syscall.SIGABRT)), // 128 + signal number
BeNumerically("<", 0), // Negative signal codes
), fmt.Sprintf("Expected SIGABRT-related exit code, got %d", actualExitCode))
} else {
Expect(actualExitCode).To(Equal(exitCode))
}
Expand Down Expand Up @@ -157,14 +159,8 @@ var _ = Describe("Fake App Exit Behavior", Serial, func() {
// Send SIGTERM to simulate natural shutdown
session.Terminate()

// Cross-platform exit code handling
if runtime.GOOS == "windows" {
// Windows doesn't have SIGTERM, process exits naturally
Eventually(session, 10*time.Second).Should(gexec.Exit(42))
} else {
// Unix systems: SIGTERM results in exit code 143 (128 + 15)
Eventually(session, 10*time.Second).Should(gexec.Exit(143))
}
// Should exit with code 42
Eventually(session, 10*time.Second).Should(gexec.Exit(143))
})
})
})
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !windows

package main

import (
Expand All @@ -6,6 +8,7 @@ import (
"net/http"
"os"
"strconv"
"syscall"
"time"
)

Expand Down Expand Up @@ -59,9 +62,7 @@ func main() {
go func() {
time.Sleep(ExitDelay)
if exitCode == SigabrtExitCode {
// Simulate SIGABRT behavior cross-platform
// Use panic to simulate abnormal termination (similar to SIGABRT)
panic("simulated SIGABRT exit")
syscall.Kill(syscall.Getpid(), syscall.SIGABRT)
} else {
os.Exit(exitCode)
}
Expand Down
28 changes: 12 additions & 16 deletions src/code.cloudfoundry.org/inigo/cell/assets/fake_proxy_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//go:build !windows

package assets_test

import (
"fmt"
"net/http"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"time"

. "github.com/onsi/ginkgo/v2"
Expand All @@ -27,10 +29,6 @@ var _ = Describe("Fake Proxy Exit Behavior", Serial, func() {

AfterEach(func() {
gexec.CleanupBuildArtifacts()
// On Windows, add a small delay to ensure ports are released
if runtime.GOOS == "windows" {
time.Sleep(500 * time.Millisecond)
}
})

// Helper function for normal exit codes
Expand Down Expand Up @@ -88,10 +86,14 @@ var _ = Describe("Fake Proxy Exit Behavior", Serial, func() {
// Verify process exits
Eventually(session, 10*time.Second).Should(gexec.Exit())

// Verify SIGABRT simulation exit code (panic results in exit code 2)
// Verify SIGABRT exit code (can vary by system)
actualExitCode := session.ExitCode()
Expect(actualExitCode).To(Equal(2),
fmt.Sprintf("Expected panic exit code 2 for SIGABRT simulation, got %d", actualExitCode))
Expect(actualExitCode).To(SatisfyAny(
Equal(134), // Direct SIGABRT exit code
Equal(2), // Common SIGABRT exit code
Equal(128+int(syscall.SIGABRT)), // 128 + signal number
BeNumerically("<", 0), // Negative signal codes
), fmt.Sprintf("Expected SIGABRT-related exit code, got %d", actualExitCode))
}

// Use ordered specs to prevent port conflicts
Expand Down Expand Up @@ -173,13 +175,7 @@ var _ = Describe("Fake Proxy Exit Behavior", Serial, func() {
// Send SIGTERM to simulate natural shutdown
session.Terminate()

// Cross-platform exit code handling
if runtime.GOOS == "windows" {
// Windows doesn't have SIGTERM, process exits naturally
Eventually(session, 10*time.Second).Should(gexec.Exit(42))
} else {
// Unix systems: SIGTERM results in exit code 143 (128 + 15)
Eventually(session, 10*time.Second).Should(gexec.Exit(143))
}
// Should exit with code 42
Eventually(session, 10*time.Second).Should(gexec.Exit(143))
})
})
104 changes: 24 additions & 80 deletions src/code.cloudfoundry.org/inigo/cell/codependency_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !windows

package cell_test

import (
Expand All @@ -7,7 +9,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"time"

"code.cloudfoundry.org/durationjson"
Expand All @@ -25,60 +26,20 @@ import (
"github.com/tedsuo/ifrit/grouper"
)

// Cross-platform build configuration
func getBuildConfig() (goos, goarch, binSuffix string) {
// Build for the target platform (Linux containers on Linux/macOS, Windows containers on Windows)
if runtime.GOOS == "windows" {
return "windows", "amd64", ".exe"
}
return "linux", "amd64", ""
}

// Cross-platform shell command configuration
func getShellCommand(command string) (shell string, args []string) {
if runtime.GOOS == "windows" {
return "cmd", []string{"/C", command}
}
return "sh", []string{"-c", command}
}

// Cross-platform temp directory
func getTempDir() string {
if runtime.GOOS == "windows" {
return "C:\\temp"
}
return "/tmp"
}

// Cross-platform file permissions (noop on Windows)
func setExecutablePermissions(path string) error {
if runtime.GOOS == "windows" {
return nil // Windows doesn't use Unix permissions
}
return os.Chmod(path, 0755)
}

func buildFakeApp() (string, string) {
// Build the fake app from the assets directory
fakeAppPath := filepath.Join(".", "assets", "fake_app")

// Create temporary output file
tempDir := world.TempDirWithParent("", "fake-app-build")
goos, goarch, binSuffix := getBuildConfig()
binPath := filepath.Join(tempDir, "fake-app"+binSuffix)

buildFlags := []string{"build", "-a", "-o", binPath}
// Only add static linking flags for Linux builds
if goos == "linux" {
buildFlags = append(buildFlags, "-tags", "netgo", "-ldflags", "-extldflags=-static")
}
binPath := filepath.Join(tempDir, "fake-app")

cmd := exec.Command("go", buildFlags...)
cmd := exec.Command("go", "build", "-a", "-tags", "netgo", "-ldflags", "-extldflags=-static", "-o", binPath)
cmd.Dir = fakeAppPath // Set working directory to the source directory
cmd.Env = append(os.Environ(),
"CGO_ENABLED=0",
"GOOS="+goos,
"GOARCH="+goarch)
"GOOS=linux",
"GOARCH=amd64")

err := cmd.Run()
Expect(err).NotTo(HaveOccurred())
Expand All @@ -88,47 +49,38 @@ func buildFakeApp() (string, string) {

func buildFakeProxy() string {
dir := world.TempDirWithParent(suiteTempDir, "fake-proxy")
// Set directory permissions (no-op on Windows)
setExecutablePermissions(dir)
err := os.Chmod(dir, 0777)
Expect(err).NotTo(HaveOccurred())

// Build the fake proxy from the assets directory
fakeProxyPath := filepath.Join(".", "assets", "fake_proxy")

// Create temporary build output file
goos, goarch, binSuffix := getBuildConfig()
tempBinPath := filepath.Join(dir, "fake-proxy-temp"+binSuffix)

buildFlags := []string{"build", "-a", "-o", tempBinPath}
// Only add static linking flags for Linux builds
if goos == "linux" {
buildFlags = append(buildFlags, "-tags", "netgo", "-ldflags", "-extldflags=-static")
}
tempBinPath := filepath.Join(dir, "fake-proxy-temp")

cmd := exec.Command("go", buildFlags...)
cmd := exec.Command("go", "build", "-a", "-tags", "netgo", "-ldflags", "-extldflags=-static", "-o", tempBinPath)
cmd.Dir = fakeProxyPath // Set working directory to the source directory
cmd.Env = append(os.Environ(),
"CGO_ENABLED=0",
"GOOS="+goos,
"GOARCH="+goarch)
"GOOS=linux",
"GOARCH=amd64")

_, err := cmd.CombinedOutput()
_, err = cmd.CombinedOutput()
Expect(err).NotTo(HaveOccurred())

// Copy to envoy name (for compatibility with rep expectations)
envoyPath := filepath.Join(dir, "envoy"+binSuffix)
envoyPath := filepath.Join(dir, "envoy")
srcFile, err := os.Open(tempBinPath)
Expect(err).NotTo(HaveOccurred())
defer srcFile.Close()

newEnvoy, err := os.OpenFile(envoyPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
newEnvoy, err := os.OpenFile(envoyPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
Expect(err).NotTo(HaveOccurred())
defer newEnvoy.Close()

_, err = io.Copy(newEnvoy, srcFile)
Expect(err).NotTo(HaveOccurred())

// Set executable permissions (no-op on Windows)
err = setExecutablePermissions(envoyPath)
err = os.Chmod(envoyPath, 0755)
Expect(err).NotTo(HaveOccurred())

// Verify the fake-proxy binary was created correctly
Expand Down Expand Up @@ -165,12 +117,11 @@ var _ = Describe("Codependency", Serial, func() {
fakeAppContents, err := os.ReadFile(fakeAppPath)
Expect(err).NotTo(HaveOccurred())

_, _, binSuffix := getBuildConfig()
archive_helper.CreateZipArchive(
filepath.Join(fileServerStaticDir, "lrp.zip"),
[]archive_helper.ArchiveFile{
{
Name: "fake-app" + binSuffix,
Name: "fake-app",
Body: string(fakeAppContents),
Mode: 0755,
},
Expand Down Expand Up @@ -223,39 +174,32 @@ var _ = Describe("Codependency", Serial, func() {
func(processToExit string, exitCode int) {
fakeProxyDir = buildFakeProxy()

// Get cross-platform configuration
_, _, binSuffix := getBuildConfig()
tempDir := getTempDir()
monitorShell, monitorArgs := getShellCommand("exit 0")
mainShell, mainArgs := getShellCommand(fmt.Sprintf("PORT=8080 %s%cfake-app%s", tempDir, filepath.Separator, binSuffix))
sidecarShell, sidecarArgs := getShellCommand(fmt.Sprintf("PORT=8081 %s%cfake-app%s", tempDir, filepath.Separator, binSuffix))

// Construct DesiredLRP
lrp := helpers.DefaultLRPCreateRequest(componentMaker.Addresses(), processGuid, "log-guid", 1)
lrp.Setup = models.WrapAction(&models.DownloadAction{
User: "vcap",
From: fmt.Sprintf("http://%s/v1/static/%s", componentMaker.Addresses().FileServer, "lrp.zip"),
To: tempDir,
To: "/tmp",
})
lrp.Monitor = models.WrapAction(&models.RunAction{
User: "vcap",
Path: monitorShell,
Args: monitorArgs,
Path: "sh",
Args: []string{"-c", "exit 0"},
})

lrp.Action = models.WrapAction(&models.RunAction{
User: "vcap",
Path: mainShell,
Args: mainArgs,
Path: "sh",
Args: []string{"-c", "PORT=8080 /tmp/fake-app"},
})
lrp.Ports = []uint32{8080, 8081}

lrp.Sidecars = []*models.Sidecar{
{
Action: models.WrapAction(&models.RunAction{
User: "vcap",
Path: sidecarShell,
Args: sidecarArgs,
Path: "sh",
Args: []string{"-c", "PORT=8081 /tmp/fake-app"},
}),
MemoryMb: 128,
},
Expand Down
Loading