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
23 changes: 4 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ RUN microdnf install -y \
gzip \
unzip \
ca-certificates \
# Container tools
podman \
# Container tools (skopeo for OCI image operations, no daemon required)
skopeo \
# Python (required for gcloud SDK)
python3 \
# Clean up
Expand Down Expand Up @@ -155,10 +155,6 @@ RUN ARCH=${TARGETARCH:-amd64} && \
echo "${ROXCTL_SHA256} /usr/local/bin/roxctl" | sha256sum -c - && \
chmod +x /usr/local/bin/roxctl

# Install podman (required for extracting operator bundles)
# fuse-overlayfs provides better performance but vfs driver is more compatible
RUN microdnf install -y podman fuse-overlayfs \
&& microdnf clean all

# Install common kubectl credential plugins for cloud provider authentication
# This enables kubectl to work with GKE, EKS, AKS, and OpenShift clusters
Expand Down Expand Up @@ -199,19 +195,8 @@ RUN chmod +x /usr/local/bin/roxcurl
# This allows users to mount credentials directly at their standard paths:
# -v ~/.kube:/.kube:ro instead of -v ~/.kube:/home/roxie/.kube:ro
RUN useradd -r -u 1000 -d / -s /bin/bash roxie \
&& mkdir -p /.kube /.roxie /.local/share/containers /.config /.aws /.azure \
&& chown -R roxie:roxie /.kube /.roxie /.local /.config /.aws /.azure

# Configure podman for rootless operation inside container
# This is critical for roxie's operator bundle extraction functionality
# Using VFS storage driver for maximum compatibility in containerized environments
RUN mkdir -p /etc/containers && \
echo 'unqualified-search-registries = ["docker.io", "quay.io"]' > /etc/containers/registries.conf && \
echo '[storage]' > /etc/containers/storage.conf && \
echo 'driver = "vfs"' >> /etc/containers/storage.conf && \
echo 'runroot = "/tmp/containers/storage"' >> /etc/containers/storage.conf && \
echo 'graphroot = "/.local/share/containers/storage"' >> /etc/containers/storage.conf && \
chmod 644 /etc/containers/storage.conf /etc/containers/registries.conf
&& mkdir -p /.kube /.roxie /.config /.aws /.azure \
&& chown -R roxie:roxie /.kube /.roxie /.config /.aws /.azure

# Set working directory
WORKDIR /workspace
Expand Down
4 changes: 0 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ test-integration: build ## Run integration tests (requires kubectl context and c
echo "❌ No kubectl context found. Please configure kubectl first."; \
exit 1; \
fi
@if ! command -v podman >/dev/null 2>&1; then \
echo "❌ podman not found. Please install podman for integration tests."; \
exit 1; \
fi
$(GOTEST) -v -tags=integration -run=_Integration$$ -timeout=120m -parallel=1 ./...

.PHONY: test-all
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ the cluster to succeed.

Prerequisites:
- `kubectl` configured to point at your target cluster
- `podman` is set up and available
- `skopeo` is available (for OCI image operations)
- The `roxctl` CLI
- The `roxie` branch forked and cloned to your local machine

Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ require (
github.com/spf13/pflag v1.0.9 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.35.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
Expand Down
2 changes: 1 addition & 1 deletion internal/deployer/deploy_via_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ func (d *Deployer) verifyHelmChartImages(ctx context.Context, chartDir, valuesFi
d.logger.Dim(fmt.Sprintf(" - %s", img))
}

if !d.imageCache.VerifyImagesPullable(imageRefs...) {
if !d.imageCache.VerifyImagesPullable(ctx, imageRefs...) {
return errors.New("one or more images not found or not pullable")
}

Expand Down
14 changes: 1 addition & 13 deletions internal/deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ func (d *Deployer) waitForNamespaceDeletion(namespace string) error {

// checkRequiredTools verifies that required CLI tools are available
func checkRequiredTools() error {
requiredTools := []string{"kubectl", "roxctl"}
requiredTools := []string{"kubectl", "roxctl", "skopeo"}

var missing []string
for _, tool := range requiredTools {
Expand All @@ -806,18 +806,6 @@ func checkRequiredTools() error {
}
}

// Check for container tool (podman or docker)
containerTool := ""
if _, err := exec.LookPath("podman"); err == nil {
containerTool = "podman"
} else if _, err := exec.LookPath("docker"); err == nil {
containerTool = "docker"
}

if containerTool == "" {
missing = append(missing, "podman or docker")
}

if len(missing) > 0 {
return fmt.Errorf("required tools not found in PATH: %s\nPlease install these tools and ensure they are available in your PATH", strings.Join(missing, ", "))
}
Expand Down
46 changes: 5 additions & 41 deletions internal/deployer/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ import (
"fmt"
"math/big"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"gopkg.in/yaml.v3"

"github.com/stackrox/roxie/internal/env"
"github.com/stackrox/roxie/internal/helpers"
"github.com/stackrox/roxie/internal/skopeohelper"
)

const (
Expand Down Expand Up @@ -70,46 +69,11 @@ func (d *Deployer) downloadAndExtractOperatorBundle(ctx context.Context, bundleI
return "", fmt.Errorf("failed to create temp dir: %w", err)
}

d.logger.Dim(fmt.Sprintf("Created temporary directory: %s", bundleDir))

containerTool := helpers.GetContainerTool()
d.logger.Dim(fmt.Sprintf("Using %s to extract bundle", containerTool))

// Check if image exists locally
inspectCmd := exec.CommandContext(ctx, containerTool, "inspect", bundleImage)
if err := inspectCmd.Run(); err != nil {
// Image doesn't exist locally, pull it
d.logger.Info("Pulling operator bundle image...")
// Force amd64 platform for pulling the operator bundle image.
// This is
// 1. fine because bundle images only contain platform-agnostic YAML files and
// 2. required to pull the image after the recent changes to the operator bundle image build process.
pullCmd := exec.CommandContext(ctx, containerTool, "pull", "--platform", "linux/amd64", bundleImage)
if output, err := pullCmd.CombinedOutput(); err != nil {
os.RemoveAll(bundleDir)
d.logger.Dim("Command output:")
d.logger.Dim(string(output))
return "", fmt.Errorf("failed to pull bundle image: %w", err)
}
} else {
d.logger.Dim("Bundle image already available locally, skipping pull")
}

containerID := fmt.Sprintf("stackrox-bundle-extract-%d", time.Now().Unix())

createCmd := exec.CommandContext(ctx, containerTool, "create", "--name", containerID, bundleImage)
if err := createCmd.Run(); err != nil {
os.RemoveAll(bundleDir)
return "", fmt.Errorf("failed to create container: %w", err)
}

defer func() {
rmCmd := exec.Command(containerTool, "rm", containerID)
rmCmd.Run()
}()
d.logger.Dimf("Created temporary directory: %s", bundleDir)
d.logger.Info("Pulling and extracting operator bundle image...")

cpCmd := exec.CommandContext(ctx, containerTool, "cp", fmt.Sprintf("%s:/manifests/.", containerID), bundleDir)
if err := cpCmd.Run(); err != nil {
// Force amd64 platform - the bundle images only contain platform-agnostic YAML files.
if err := skopeohelper.ExtractManifestsFromImage(ctx, d.logger, bundleImage, bundleDir); err != nil {
os.RemoveAll(bundleDir)
return "", fmt.Errorf("failed to copy bundle contents: %w", err)
}
Expand Down
22 changes: 8 additions & 14 deletions internal/imagecache/imagecache.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package imagecache

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"

"github.com/stackrox/roxie/internal/helpers"
"github.com/stackrox/roxie/internal/logger"
"github.com/stackrox/roxie/internal/skopeohelper"
)

// ImageCache manages cache of verified pullable Docker images
Expand Down Expand Up @@ -127,31 +128,24 @@ func (ic *ImageCache) AddToCache(imageRef string) {
}

// VerifyImagePullable verifies if image is pullable using cache when possible
func (ic *ImageCache) VerifyImagePullable(imageRef string) bool {
func (ic *ImageCache) VerifyImagePullable(ctx context.Context, imageRef string) bool {
if ic.IsCached(imageRef) {
return true
}

_, stderr, err := helpers.RunCommandWithOutput(
"Verifying image pullability",
"podman",
[]string{"manifest", "inspect", imageRef},
)

// Use skopeo inspect to verify image accessibility.
err := skopeohelper.InspectImage(ctx, ic.logger, imageRef)
if err == nil {
ic.AddToCache(imageRef)
return true
}

if stderr != "" {
fmt.Fprintln(os.Stderr, stderr)
}

fmt.Fprintf(os.Stderr, "Failed to verify image %s: %v\n", imageRef, err)
return false
}

// VerifyImagesPullable verifies that Docker images are pullable using cached results
func (ic *ImageCache) VerifyImagesPullable(images ...string) bool {
func (ic *ImageCache) VerifyImagesPullable(ctx context.Context, images ...string) bool {
if len(images) == 0 {
return true
}
Expand Down Expand Up @@ -207,7 +201,7 @@ func (ic *ImageCache) VerifyImagesPullable(images ...string) bool {
sem <- struct{}{} // Acquire semaphore
defer func() { <-sem }() // Release semaphore

success := ic.VerifyImagePullable(image)
success := ic.VerifyImagePullable(ctx, image)
if success {
results <- result{img: image, success: true}
} else {
Expand Down
Loading
Loading