Skip to content

Commit 9e641cf

Browse files
committed
feat: Default to Podman Socket
Update s2i's defaults to inspect the host system for Podman sockets. Podman socket provides support for the Docker v1.40 API, which meets s2i's needs. The logic looks for the podman socket in the following order: 1. Rootless podman socket 2. Root podman socket 3. Docker socket If none are found, the default falls back to the docker socket. As before, the `--url` and `DOCKER_HOST` environment variables can override the container engine socket location. Signed-off-by: Adam Kaplan <[email protected]>
1 parent c301811 commit 9e641cf

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,20 @@ ready-to-run images by injecting source code into a container image and letting
1010

1111
For a deep dive on S2I you can view [this presentation](https://www.youtube.com/watch?v=flI6zx9wH6M).
1212

13-
Want to try it right now? Download the [latest release](https://github.com/openshift/source-to-image/releases/latest) and run:
13+
### Try It!
14+
15+
First, make sure your machine has either [Docker](https://www.docker.com) or [Podman](https://podman.io)
16+
installed:
17+
18+
- s2i can work with Docker out of the box. However, the container image build will run in root
19+
containers by default, which may pose a security risk.
20+
- s2i can work with Podman by starting the Podman socket service. Passing `--user` to systemctl
21+
ensures podman runs in rootless mode:
22+
```sh
23+
systemctl enable --user --now podman.socket
24+
```
25+
26+
Next, download the [latest release](https://github.com/openshift/source-to-image/releases/latest) and run:
1427

1528
$ s2i build https://github.com/sclorg/django-ex centos/python-35-centos7 hello-python
1629
$ docker run -p 8080:8080 hello-python

pkg/docker/util.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ var (
3030
log = utillog.StderrLog
3131

3232
// DefaultEntrypoint is the default entry point used when starting containers
33-
DefaultEntrypoint = []string{"/usr/bin/env"}
33+
DefaultEntrypoint = []string{"/usr/bin/env"}
34+
DefaultPodmanSocket = filepath.Join("/run", "podman", "podman.sock")
3435
)
3536

3637
// AuthConfigurations maps a registry name to an AuthConfig, as used for
@@ -434,7 +435,7 @@ func GetDefaultDockerConfig() *api.DockerConfig {
434435
cfg := &api.DockerConfig{}
435436

436437
if cfg.Endpoint = os.Getenv("DOCKER_HOST"); cfg.Endpoint == "" {
437-
cfg.Endpoint = client.DefaultDockerHost
438+
cfg.Endpoint = GetDefaultContainerEngineHost()
438439
}
439440

440441
certPath := os.Getenv("DOCKER_CERT_PATH")
@@ -457,6 +458,36 @@ func GetDefaultDockerConfig() *api.DockerConfig {
457458
return cfg
458459
}
459460

461+
// GetDefaultContainerEngineHost returns a default container engine socket address. It checks the
462+
// following locations for a container engine socket:
463+
//
464+
// 1. Rootless podman socket: unix://$XDG_RUNTIME_DIR/podman/podman.sock
465+
// 2. "Rootful" podman socket: unix:///run/podman/podman.sock
466+
// 3. Docker socket: unix:///var/run/docker.sock
467+
func GetDefaultContainerEngineHost() string {
468+
podmanSockets := []string{}
469+
470+
// Podman socket provides a unix socket that is directly compatible with Docker
471+
// There are two modes the socket can be provided:
472+
//
473+
// 1) "rootless" - runs as nonroot, with socket at $XDG_RUNTIME_DIR/podman/podman.sock
474+
// 2) "rootful" - runs as root, with socket at /run/podman/podman.sock
475+
476+
if runtimeDir, ok := os.LookupEnv("XDG_RUNTIME_DIR"); ok {
477+
podmanSockets = append(podmanSockets, filepath.Join(runtimeDir, "podman", "podman.sock"))
478+
}
479+
podmanSockets = append(podmanSockets, DefaultPodmanSocket)
480+
481+
for _, socket := range podmanSockets {
482+
if _, err := os.Stat(socket); err == nil {
483+
return fmt.Sprintf("unix://%s", socket)
484+
}
485+
}
486+
487+
// Fall back to default docker socket.
488+
return client.DefaultDockerHost
489+
}
490+
460491
// GetAssembleUser finds an assemble user on the given image.
461492
// This functions receives the config to check if the AssembleUser was defined in command line
462493
// If the cmd is blank, it tries to fetch the value from the Builder Image defined Label (assemble-user)

pkg/docker/util_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package docker
22

33
import (
4+
"fmt"
45
"os"
6+
"path/filepath"
57
"testing"
68

9+
"github.com/docker/docker/client"
10+
711
"github.com/openshift/source-to-image/pkg/api"
812
"github.com/openshift/source-to-image/pkg/api/constants"
913
"github.com/openshift/source-to-image/pkg/util/user"
@@ -233,14 +237,17 @@ func TestGetDefaultDockerConfig(t *testing.T) {
233237
},
234238
}
235239
for _, tc := range tests {
240+
oldXdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
236241
oldHost := os.Getenv("DOCKER_HOST")
237242
oldCertPath := os.Getenv("DOCKER_CERT_PATH")
238243
oldTLSVerify := os.Getenv("DOCKER_TLS_VERIFY")
239244
oldTLS := os.Getenv("DOCKER_TLS")
245+
os.Setenv("XDG_RUNTIME_DIR", "")
240246
os.Setenv("DOCKER_HOST", tc.envHost)
241247
os.Setenv("DOCKER_CERT_PATH", tc.envCertPath)
242248
os.Setenv("DOCKER_TLS_VERIFY", tc.envTLSVerify)
243249
os.Setenv("DOCKER_TLS", tc.envTLS)
250+
defer os.Setenv("XDG_RUNTIME_DIR", oldXdgRuntimeDir)
244251
defer os.Setenv("DOCKER_HOST", oldHost)
245252
defer os.Setenv("DOCKER_CERT_PATH", oldCertPath)
246253
defer os.Setenv("DOCKER_TLS_VERIFY", oldTLSVerify)
@@ -262,6 +269,84 @@ func TestGetDefaultDockerConfig(t *testing.T) {
262269
}
263270
}
264271

272+
func TestGetDefaultContainerEngineHost(t *testing.T) {
273+
274+
tmpDir, err := os.MkdirTemp("", "s2i-container-engine-host-*")
275+
if err != nil {
276+
t.Fatalf("failed to create xdg temp dir: %v", err)
277+
}
278+
defer func() {
279+
if rmErr := os.RemoveAll(tmpDir); rmErr != nil {
280+
t.Errorf("failed to clean up xdg temp dir: %v", err)
281+
}
282+
}()
283+
284+
testCases := []struct {
285+
name string
286+
xdgRuntimeDir string
287+
createPodmanSocket bool
288+
expectedHost string
289+
}{
290+
{
291+
name: "rootless podman - socket exists",
292+
xdgRuntimeDir: tmpDir,
293+
createPodmanSocket: true,
294+
expectedHost: fmt.Sprintf("unix://%s", filepath.Join(tmpDir, "podman", "podman.sock")),
295+
},
296+
{
297+
name: "rootless podman - socket does not exist",
298+
xdgRuntimeDir: tmpDir,
299+
createPodmanSocket: false,
300+
expectedHost: client.DefaultDockerHost,
301+
},
302+
{
303+
name: "docker default",
304+
expectedHost: client.DefaultDockerHost,
305+
},
306+
}
307+
308+
for _, tc := range testCases {
309+
t.Run(tc.name, func(t *testing.T) {
310+
oldXdgDir := os.Getenv("XDG_RUNTIME_DIR")
311+
os.Setenv("XDG_RUNTIME_DIR", tc.xdgRuntimeDir)
312+
defer os.Setenv("XDG_RUNTIME_DIR", oldXdgDir)
313+
314+
socketCreated := false
315+
socketPath := filepath.Join(tc.xdgRuntimeDir, "podman", "podman.sock")
316+
if tc.createPodmanSocket {
317+
318+
if _, err := os.Stat(socketPath); err != nil {
319+
if err := os.MkdirAll(filepath.Dir(socketPath), 0750); err != nil {
320+
t.Fatalf("failed to create socket directory: %v", err)
321+
}
322+
file, err := os.Create(socketPath)
323+
if err != nil {
324+
t.Fatalf("failed to create default podman socket: %v", err)
325+
}
326+
defer func() {
327+
if closeErr := file.Close(); closeErr != nil {
328+
t.Errorf("failed to close file: %v", closeErr)
329+
}
330+
}()
331+
socketCreated = true
332+
}
333+
}
334+
335+
engineHost := GetDefaultContainerEngineHost()
336+
if tc.expectedHost != engineHost {
337+
t.Errorf("expected container host %s; got %s", tc.expectedHost, engineHost)
338+
}
339+
340+
if socketCreated {
341+
if err := os.RemoveAll(filepath.Dir(socketPath)); err != nil {
342+
t.Errorf("failed to clean up podman socket: %v", err)
343+
}
344+
}
345+
346+
})
347+
}
348+
}
349+
265350
func TestGetAssembleUser(t *testing.T) {
266351
testCases := []struct {
267352
name string

0 commit comments

Comments
 (0)