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
3 changes: 3 additions & 0 deletions pkg/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const (

// DefaultTokenRequestAudience is the default audience for TokenRequest tokens
DefaultTokenRequestAudience = "hyperfleet-api"

// DefaultHyperfleetAPIBaseURL is the in-cluster base URL for the HyperFleet API.
DefaultHyperfleetAPIBaseURL = "http://hyperfleet-gateway:8000"
)

// Default required adapters for resource types
Expand Down
43 changes: 14 additions & 29 deletions pkg/helper/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

pubsubadmin "cloud.google.com/go/pubsub/v2/apiv1"
pubsubpb "cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/config"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/logger"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -169,18 +170,14 @@ func (h *Helper) DeployAdapter(ctx context.Context, opts AdapterDeploymentOption
extraEnv = append(extraEnv, "ADAPTER_GOOGLEPUBSUB_CREATE_SUBSCRIPTION_IF_MISSING=true")
}

// Resolve the in-cluster HyperFleet API URL for adapters running inside Kubernetes.
// The external LoadBalancer IP (HYPERFLEET_API_URL) is not routable from within GKE pods.
// We look up the hyperfleet-api service across all namespaces and construct the FQDN so
// that adapters deployed to the test namespace can reach the API regardless of where it runs.
if os.Getenv("ADAPTER_HYPERFLEET_API_URL") == "" && h.K8sClient != nil {
if internalURL, err := h.resolveInternalAPIURL(ctx); err == nil && internalURL != "" {
extraEnv = append(extraEnv, "ADAPTER_HYPERFLEET_API_URL="+internalURL)
logger.Info("resolved in-cluster HyperFleet API URL for adapters", "url", internalURL)
} else {
logger.Info("could not resolve in-cluster API URL, falling back to HYPERFLEET_API_URL",
"error", err)
}
// Resolve the in-cluster API URL for adapters. This is intentionally separate from
// HYPERFLEET_API_URL, which points wherever the e2e test process itself reaches the API
// (e.g. a port-forward or external LB address) and is not routable from in-cluster pods.
// Adapters reach the API via the in-cluster hyperfleet-gateway Service, which lives in the
// same namespace adapters are deployed into.
apiURL := os.Getenv("ADAPTER_HYPERFLEET_API_URL")
if apiURL == "" {
apiURL = config.DefaultHyperfleetAPIBaseURL
}

// Expand environment variables in values.yaml in-place using envsubst
Expand Down Expand Up @@ -223,7 +220,11 @@ func (h *Helper) DeployAdapter(ctx context.Context, opts AdapterDeploymentOption
"-f", valuesFilePath,
}

// Append conditional --set flags
// Override chart's default hyperfleetApi.baseUrl with the resolved API URL via --set.
helmArgs = append(helmArgs, "--set", "adapterConfig.hyperfleetApi.baseUrl="+apiURL)
Comment thread
mliptak0 marked this conversation as resolved.

// Append conditional --set flags (opts.SetValues is applied last, so tests can still override
// the base URL, e.g. to simulate an unreachable API)
helmArgs = append(helmArgs, h.adapterHelmSetArgs(releaseName, opts)...)

logger.Info("executing Helm command", "args", helmArgs)
Expand Down Expand Up @@ -289,22 +290,6 @@ func (h *Helper) adapterHelmSetArgs(releaseName string, opts AdapterDeploymentOp
return args
}

// resolveInternalAPIURL looks up the hyperfleet-api Kubernetes service in the configured
// namespace and returns an in-cluster FQDN URL that adapters deployed in any namespace can use.
// This is needed because the external LoadBalancer IP is not routable from within GKE pods.
func (h *Helper) resolveInternalAPIURL(ctx context.Context) (string, error) {
ns := h.Cfg.Namespace
svc, err := h.K8sClient.CoreV1().Services(ns).Get(ctx, "hyperfleet-api", metav1.GetOptions{})
if err != nil {
return "", fmt.Errorf("failed to get hyperfleet-api service in namespace %q: %w", ns, err)
}
if len(svc.Spec.Ports) == 0 {
return "", fmt.Errorf("hyperfleet-api service has no ports")
}
port := svc.Spec.Ports[0].Port
return fmt.Sprintf("http://hyperfleet-api.%s.svc.cluster.local:%d", ns, port), nil
}

// UninstallAdapter uninstalls an adapter using Helm uninstall
// This is a common function that can be reused across test cases
func (h *Helper) UninstallAdapter(ctx context.Context, releaseName, namespace string) error {
Expand Down
88 changes: 0 additions & 88 deletions pkg/helper/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ import (
"strings"
"testing"

k8sclient "github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/client/kubernetes"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/config"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
)

// hashSuffixPattern matches the deterministic hash appended on truncation:
Expand Down Expand Up @@ -114,89 +109,6 @@ func TestGenerateAdapterReleaseName_Deterministic(t *testing.T) {
}
}

func newHelperWithService(ns string, svc *corev1.Service) *Helper {
var objs []k8sruntime.Object
if svc != nil {
objs = append(objs, svc)
}
return &Helper{
Cfg: &config.Config{Namespace: ns},
K8sClient: &k8sclient.Client{Interface: fake.NewClientset(objs...)},
}
}

func TestResolveInternalAPIURL(t *testing.T) {
const ns = "hyperfleet-system"

svcWithPort := func(port int32) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "hyperfleet-api", Namespace: ns},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{Port: port}},
},
}
}

tests := []struct {
name string
svc *corev1.Service
wantURL string
wantErrMsg string
}{
{
name: "service found with port",
svc: svcWithPort(8000),
wantURL: fmt.Sprintf("http://hyperfleet-api.%s.svc.cluster.local:8000", ns),
},
{
name: "service not found",
svc: nil,
wantErrMsg: `failed to get hyperfleet-api service in namespace "hyperfleet-system"`,
},
{
name: "service found but no ports",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "hyperfleet-api", Namespace: ns},
Spec: corev1.ServiceSpec{},
},
wantErrMsg: "hyperfleet-api service has no ports",
},
{
// A hyperfleet-api service in a different namespace must not be found
// when h.Cfg.Namespace is set — Get is scoped to the configured namespace.
name: "service in wrong namespace is not found",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "hyperfleet-api", Namespace: "other-ns"},
Spec: corev1.ServiceSpec{Ports: []corev1.ServicePort{{Port: 8000}}},
},
wantErrMsg: `failed to get hyperfleet-api service in namespace "hyperfleet-system"`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := newHelperWithService(ns, tt.svc)
got, err := h.resolveInternalAPIURL(context.Background())

if tt.wantErrMsg != "" {
if err == nil {
t.Fatalf("expected error containing %q, got nil", tt.wantErrMsg)
}
if !strings.Contains(err.Error(), tt.wantErrMsg) {
t.Errorf("error %q does not contain %q", err.Error(), tt.wantErrMsg)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tt.wantURL {
t.Errorf("got %q, want %q", got, tt.wantURL)
}
})
}
}

// TestGenerateAdapterReleaseName_LongNameCollision asserts that two distinct
// long names sharing a long common prefix produce distinct release names.
// The hash suffix is what guarantees uniqueness once the base is truncated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down
2 changes: 1 addition & 1 deletion testdata/adapter-configs/cl-m-bad-api/adapter-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down
2 changes: 1 addition & 1 deletion testdata/adapter-configs/cl-m-wrong-ds/adapter-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ log:

clients:
hyperfleet_api:
base_url: http://hyperfleet-api:8000
base_url: http://hyperfleet-gateway:8000
version: v1
timeout: 2s
retry_attempts: 3
Expand Down