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
8 changes: 4 additions & 4 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,13 @@ func runDeploy(cmd *cobra.Command, args []string) error {
return fmt.Errorf("applying config patches from command line argument: %w", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()

if deploySettings.Roxie.Version != "" {
log.Dimf("Using main image tag %s", deploySettings.Roxie.Version)
} else {
mainImageTag, err := helpers.LookupMainImageTag(log)
mainImageTag, err := helpers.LookupMainImageTag(ctx, log)
if err != nil {
return fmt.Errorf("looking up main image tag: %w", err)
}
Expand Down Expand Up @@ -333,9 +336,6 @@ func runDeploy(cmd *cobra.Command, args []string) error {
return nil
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()

// If we are deploying to a local cluster and the images exist locally, then we transfer them
// to the local cluster.
if deploySettings.Roxie.ClusterType.IsLocal() && !deploySettings.Roxie.KonfluxImagesEnabled() {
Expand Down
9 changes: 9 additions & 0 deletions internal/constants/registries.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package constants

const (
DefaultRegistry = "quay.io/rhacs-eng"
GitHubStackroxRepo = "stackrox/stackrox"

EnvRegistryUsername = "REGISTRY_USERNAME"
EnvRegistryPassword = "REGISTRY_PASSWORD"
)
8 changes: 5 additions & 3 deletions internal/deployer/acs_images.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package deployer

import "fmt"
import (
"fmt"

const (
imageRegistry = "quay.io/rhacs-eng"
"github.com/stackrox/roxie/internal/constants"
)

func imagesForConfig(config Config) []string {
Expand All @@ -13,6 +13,7 @@ func imagesForConfig(config Config) []string {
prefix = "release-"
}

imageRegistry := constants.DefaultRegistry
images = append(images, fmt.Sprintf("%s/%s%s:%s", imageRegistry, prefix, "main", config.Roxie.Version))
images = append(images, fmt.Sprintf("%s/%s%s:%s", imageRegistry, prefix, "central-db", config.Roxie.Version))
images = append(images, fmt.Sprintf("%s/%s%s:%s", imageRegistry, prefix, "scanner-v4-db", config.Roxie.Version))
Expand All @@ -27,6 +28,7 @@ func imagesForConfig(config Config) []string {
}

func OperatorBundleImage(config Config) string {
imageRegistry := constants.DefaultRegistry
if config.Roxie.KonfluxImagesEnabled() {
return fmt.Sprintf("%s/release-operator-bundle:v%s", imageRegistry, config.Operator.Version)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/deployer/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"gopkg.in/yaml.v3"

"github.com/stackrox/roxie/internal/constants"
"github.com/stackrox/roxie/internal/k8s"
"github.com/stackrox/roxie/internal/ocihelper"
)
Expand Down Expand Up @@ -203,7 +204,7 @@ func (d *Deployer) applyImageContentSourcePolicy(ctx context.Context) error {
// Define repository digest mirrors as Go data structures
rewrite := func(from, to string) map[string]interface{} {
source := fmt.Sprintf("registry.redhat.io/advanced-cluster-security/%s", from)
mirror := fmt.Sprintf("quay.io/rhacs-eng/%s", to)
mirror := fmt.Sprintf("%s/%s", constants.DefaultRegistry, to)
if d.verbose {
d.logger.Dimf("Image rewriting rule: %s -> %s", source, mirror)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/deployer/operator_olm.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"time"

"github.com/stackrox/roxie/internal/constants"
"github.com/stackrox/roxie/internal/k8s"
"gopkg.in/yaml.v3"
)
Expand All @@ -17,7 +18,7 @@ const (
subscriptionName = "stackrox-operator-subscription"
operatorGroupName = "all-namespaces-operator-group"
operatorChannel = "latest"
operatorIndexImage = "quay.io/rhacs-eng/stackrox-operator-index"
operatorIndexImage = constants.DefaultRegistry + "/stackrox-operator-index"
namespacedSubscriptionName = operatorNamespace + "/" + subscriptionName
)

Expand Down
9 changes: 5 additions & 4 deletions internal/dockerauth/dockerauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os/exec"
"path/filepath"

"github.com/stackrox/roxie/internal/constants"
"github.com/stackrox/roxie/internal/logger"
)

Expand Down Expand Up @@ -61,14 +62,14 @@ func (d *DockerAuth) GetAndVerifyCredentials() (*Credentials, error) {
var username, password string

// Try environment variables first.
username = os.Getenv("REGISTRY_USERNAME")
password = os.Getenv("REGISTRY_PASSWORD")
username = os.Getenv(constants.EnvRegistryUsername)
password = os.Getenv(constants.EnvRegistryPassword)

if username != "" && password == "" {
return nil, errors.New("REGISTRY_USERNAME set but REGISTRY_PASSWORD is empty")
return nil, fmt.Errorf("%s set but %s is empty", constants.EnvRegistryUsername, constants.EnvRegistryPassword)
}
if username == "" && password != "" {
return nil, errors.New("REGISTRY_PASSWORD set but REGISTRY_USERNAME is empty")
return nil, fmt.Errorf("%s set but %s is empty", constants.EnvRegistryPassword, constants.EnvRegistryUsername)
}

if username == "" {
Expand Down
49 changes: 41 additions & 8 deletions internal/helpers/tag.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package helpers

import (
"context"
"errors"
"fmt"
"net/http"
"os"
"strings"

"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/stackrox/roxie/internal/constants"
"github.com/stackrox/roxie/internal/env"
"github.com/stackrox/roxie/internal/logger"
"github.com/stackrox/roxie/internal/ocihelper"
"github.com/stackrox/roxie/internal/stackroxversions"
)

const (
// TODO(#91): Is the plan to keep bumping this on new ACS releases?
defaultMainImageTag = "4.9.2"
)

func LookupMainImageTag(log *logger.Logger) (string, error) {
func LookupMainImageTag(ctx context.Context, log *logger.Logger) (string, error) {
log.Info("Looking up main image tag")
if tag := os.Getenv("MAIN_IMAGE_TAG"); tag != "" {
log.Dimf("Using MAIN_IMAGE_TAG from environment: %s", tag)
Expand All @@ -29,11 +32,41 @@ func LookupMainImageTag(log *logger.Logger) (string, error) {
return tag, nil
}

log.Warningf("No MAIN_IMAGE_TAG found in the environment, using default main image tag %s for deployment", defaultMainImageTag)
log.Warningf("No MAIN_IMAGE_TAG found in the environment, looking up latest release tag on registry")
log.Warning("To use a different tag, set the MAIN_IMAGE_TAG environment variable")
log.Warning("Alternatively, execute roxie from within the stackrox repository, in which case the currently checked out stackrox tag will be used")

return defaultMainImageTag, nil
latestTag, err := LookupLatestTag(ctx, log)
if err != nil {
return "", fmt.Errorf("looking up latest release tag: %w", err)
}

return latestTag, nil
}

// Computes the latest image tag for a pullable, released main image.
func LookupLatestTag(ctx context.Context, log *logger.Logger) (string, error) {
const atMost = 5

tags, err := stackroxversions.LookupLatestReleaseTagsViaGitHub(ctx, atMost)
if err != nil {
return "", fmt.Errorf("looking up latest release tags: %w", err)
}

// Verify we have a pullable main image.
for _, tag := range tags {
mainImage := fmt.Sprintf("%s/main:%s", constants.DefaultRegistry, tag)
if err := ocihelper.VerifyImageExistence(ctx, log, mainImage); err != nil {
var te *transport.Error
if errors.As(err, &te) && te.StatusCode == http.StatusNotFound {
continue
}
return "", fmt.Errorf("verifying image %s: %w", mainImage, err)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return tag, nil
}

return "", fmt.Errorf("failed to verify main image existence for tags %s", strings.Join(tags, ", "))
}

func ConvertMainTagToOperatorTag(mainTag string) string {
Expand Down
54 changes: 54 additions & 0 deletions internal/helpers/tag_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//go:build integration

package helpers

import (
"context"
"errors"
"fmt"
"net/http"
"testing"
"time"

"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/stackrox/roxie/internal/constants"
"github.com/stackrox/roxie/internal/logger"
"github.com/stackrox/roxie/internal/ocihelper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestLookupLatestTag_Integration(t *testing.T) {
log := logger.New()
ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute)
defer cancel()

tag, err := LookupLatestTag(ctx, log)
require.NoError(t, err)
require.NotEmpty(t, tag)

imageRef := fmt.Sprintf("%s/main:%s", constants.DefaultRegistry, tag)
ref, err := name.ParseReference(imageRef)
require.NoError(t, err)

_, err = remote.Head(ref, remote.WithContext(ctx), remote.WithAuthFromKeychain(ocihelper.Keychain))
require.NoError(t, err, "image %s is not pullable", imageRef)

t.Logf("Latest pullable tag: %s (%s)", tag, imageRef)
}

func TestVerifyImageExistence_NotFound_Integration(t *testing.T) {
log := logger.New()
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()

madeUpImage := fmt.Sprintf("%s/main:99.99.99", constants.DefaultRegistry)
err := ocihelper.VerifyImageExistence(ctx, log, madeUpImage)
require.Error(t, err)

var te *transport.Error
require.True(t, errors.As(err, &te), "expected transport.Error, got %T", err)
assert.Equal(t, http.StatusNotFound, te.StatusCode)
}
27 changes: 27 additions & 0 deletions internal/ocihelper/keychain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ocihelper

import (
"os"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/stackrox/roxie/internal/constants"
)

// Keychain resolves registry credentials by checking REGISTRY_USERNAME/REGISTRY_PASSWORD
// environment variables first, then falling back to the default Docker keychain
// (~/.docker/config.json, credential helpers, etc.).
var Keychain = authn.NewMultiKeychain(&envKeychain{}, authn.DefaultKeychain)

type envKeychain struct{}

func (e *envKeychain) Resolve(target authn.Resource) (authn.Authenticator, error) {
username := os.Getenv(constants.EnvRegistryUsername)
password := os.Getenv(constants.EnvRegistryPassword)
if username == "" || password == "" {
return authn.Anonymous, nil
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
return authn.FromConfig(authn.AuthConfig{
Username: username,
Password: password,
}), nil
}
8 changes: 4 additions & 4 deletions internal/ocihelper/ocihelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"context"
"fmt"
"io"
"net/http"
"os"
"path/filepath"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
Expand All @@ -28,10 +28,10 @@ func VerifyImageExistence(ctx context.Context, log *logger.Logger, imageRef stri
return fmt.Errorf("invalid image reference: %w", err)
}

// Use HEAD request to verify image exists without downloading
_, err = remote.Head(ref,
remote.WithContext(ctx),
remote.WithAuthFromKeychain(authn.DefaultKeychain))
remote.WithAuthFromKeychain(Keychain),
remote.WithRetryStatusCodes(http.StatusTooManyRequests, http.StatusServiceUnavailable))
Comment thread
mclasmeier marked this conversation as resolved.
if err != nil {
return fmt.Errorf("image inspection failed: %w", err)
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func assureImageExistsLocally(ctx context.Context, log *logger.Logger, imageRef,

img, err = remote.Image(ref,
remote.WithContext(ctx),
remote.WithAuthFromKeychain(authn.DefaultKeychain),
remote.WithAuthFromKeychain(Keychain),
remote.WithPlatform(platform))
if err != nil {
return nil, fmt.Errorf("failed to fetch image: %w", err)
Expand Down
Loading
Loading