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
45 changes: 30 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,38 @@ make fmt
```
hyperfleet-adapter/
├── cmd/
│ └── adapter/ # Main application entry point
│ └── adapter/ # Main application entry point
├── pkg/
│ ├── errors/ # Error handling utilities
│ └── logger/ # Structured logging with context support
│ ├── constants/ # Shared constants (annotations, labels)
│ ├── errors/ # Error handling utilities
│ ├── health/ # Health and metrics servers
│ ├── logger/ # Structured logging with context support
│ ├── otel/ # OpenTelemetry tracing utilities
│ ├── utils/ # General utility functions
│ └── version/ # Version information
├── internal/
│ ├── broker_consumer/ # Message broker consumer implementations
│ ├── config_loader/ # Configuration loading logic
│ ├── criteria/ # Precondition and CEL evaluation
│ ├── executor/ # Event execution engine
│ ├── hyperfleet_api/ # HyperFleet API client
│ └── k8s_client/ # Kubernetes client wrapper
├── test/ # Integration tests
├── charts/ # Helm chart for Kubernetes deployment
├── Dockerfile # Multi-stage Docker build
├── Makefile # Build and test automation
├── go.mod # Go module dependencies
└── README.md # This file
│ ├── config_loader/ # Configuration loading and validation
│ ├── criteria/ # Precondition and CEL evaluation
│ ├── executor/ # Event execution engine (phases pipeline)
│ ├── hyperfleet_api/ # HyperFleet API client
│ ├── k8s_client/ # Kubernetes client wrapper
│ ├── maestro_client/ # Maestro/OCM ManifestWork client
│ ├── manifest/ # Manifest utilities (generation, rendering)
│ └── transport_client/ # TransportClient interface (unified apply)
├── test/
│ └── integration/ # Integration tests
│ ├── config-loader/ # Config loader integration tests
│ ├── executor/ # Executor integration tests
│ ├── k8s_client/ # K8s client integration tests
│ ├── maestro_client/ # Maestro client integration tests
│ └── testutil/ # Test utilities
├── charts/ # Helm chart for Kubernetes deployment
├── configs/ # Configuration templates and examples
├── scripts/ # Build and test scripts
├── Dockerfile # Multi-stage Docker build
├── Makefile # Build and test automation
├── go.mod # Go module dependencies
└── README.md # This file
```

### Available Make Targets
Expand Down
2 changes: 1 addition & 1 deletion cmd/adapter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func runServe() error {
exec, err := executor.NewBuilder().
WithConfig(config).
WithAPIClient(apiClient).
WithK8sClient(k8sClient).
WithTransportClient(k8sClient).
WithLogger(log).
Build()
if err != nil {
Expand Down
29 changes: 3 additions & 26 deletions internal/config_loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package config_loader
import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/openshift-hyperfleet/hyperfleet-adapter/pkg/utils"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -250,29 +249,7 @@ func loadYAMLFile(baseDir, refPath string) (map[string]interface{}, error) {

// resolvePath resolves a relative path against the base directory and validates
// that the resolved path does not escape the base directory.
// This delegates to utils.ResolveSecurePath.
func resolvePath(baseDir, refPath string) (string, error) {
baseAbs, err := filepath.Abs(baseDir)
if err != nil {
return "", fmt.Errorf("failed to resolve base directory: %w", err)
}
baseClean := filepath.Clean(baseAbs)

var targetPath string
if filepath.IsAbs(refPath) {
targetPath = filepath.Clean(refPath)
} else {
targetPath = filepath.Clean(filepath.Join(baseClean, refPath))
}

// Check if target path is within base directory
rel, err := filepath.Rel(baseClean, targetPath)
if err != nil {
return "", fmt.Errorf("path %q escapes base directory", refPath)
}

if strings.HasPrefix(rel, "..") {
return "", fmt.Errorf("path %q escapes base directory", refPath)
}

return targetPath, nil
return utils.ResolveSecurePath(baseDir, refPath)
}
12 changes: 6 additions & 6 deletions internal/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/cloudevents/sdk-go/v2/event"
"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/config_loader"
"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/hyperfleet_api"
"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/k8s_client"
"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/transport_client"
"github.com/openshift-hyperfleet/hyperfleet-adapter/pkg/logger"
pkgotel "github.com/openshift-hyperfleet/hyperfleet-adapter/pkg/otel"
"go.opentelemetry.io/otel"
Expand Down Expand Up @@ -44,7 +44,7 @@ func validateExecutorConfig(config *ExecutorConfig) error {
requiredFields := []string{
"APIClient",
"Logger",
"K8sClient"}
"TransportClient"}

for _, field := range requiredFields {
if reflect.ValueOf(config).Elem().FieldByName(field).IsNil() {
Expand Down Expand Up @@ -208,7 +208,7 @@ func (e *Executor) Execute(ctx context.Context, data interface{}) *ExecutionResu
// executeParamExtraction extracts parameters from the event and environment
func (e *Executor) executeParamExtraction(execCtx *ExecutionContext) error {
// Extract configured parameters
if err := extractConfigParams(e.config.Config, execCtx, e.config.K8sClient); err != nil {
if err := extractConfigParams(e.config.Config, execCtx); err != nil {
return err
}

Expand Down Expand Up @@ -334,9 +334,9 @@ func (b *ExecutorBuilder) WithAPIClient(client hyperfleet_api.Client) *ExecutorB
return b
}

// WithK8sClient sets the Kubernetes client
func (b *ExecutorBuilder) WithK8sClient(client k8s_client.K8sClient) *ExecutorBuilder {
b.config.K8sClient = client
// WithTransportClient sets the transport client for resource application
func (b *ExecutorBuilder) WithTransportClient(client transport_client.TransportClient) *ExecutorBuilder {
b.config.TransportClient = client
return b
}

Expand Down
22 changes: 11 additions & 11 deletions internal/executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ func TestNewExecutor(t *testing.T) {
{
name: "valid config",
config: &ExecutorConfig{
Config: &config_loader.Config{},
APIClient: newMockAPIClient(),
K8sClient: k8s_client.NewMockK8sClient(),
Logger: logger.NewTestLogger(),
Config: &config_loader.Config{},
APIClient: newMockAPIClient(),
TransportClient: k8s_client.NewMockK8sClient(),
Logger: logger.NewTestLogger(),
},
expectError: false,
},
Expand Down Expand Up @@ -89,7 +89,7 @@ func TestExecutorBuilder(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(newMockAPIClient()).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down Expand Up @@ -257,7 +257,7 @@ func TestExecute_ParamExtraction(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(newMockAPIClient()).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down Expand Up @@ -362,7 +362,7 @@ func TestParamExtractor(t *testing.T) {
}

// Extract params using pure function
err := extractConfigParams(config, execCtx, nil)
err := extractConfigParams(config, execCtx)

if tt.expectError {
assert.Error(t, err)
Expand Down Expand Up @@ -508,7 +508,7 @@ func TestSequentialExecution_Preconditions(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(newMockAPIClient()).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down Expand Up @@ -610,7 +610,7 @@ func TestSequentialExecution_Resources(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(newMockAPIClient()).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down Expand Up @@ -681,7 +681,7 @@ func TestSequentialExecution_PostActions(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(mockClient).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down Expand Up @@ -751,7 +751,7 @@ func TestSequentialExecution_SkipReasonCapture(t *testing.T) {
exec, err := NewBuilder().
WithConfig(config).
WithAPIClient(newMockAPIClient()).
WithK8sClient(k8s_client.NewMockK8sClient()).
WithTransportClient(k8s_client.NewMockK8sClient()).
WithLogger(logger.NewTestLogger()).
Build()

Expand Down
42 changes: 3 additions & 39 deletions internal/executor/param_extractor.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package executor

import (
"context"
"fmt"
"math"
"os"
"strconv"
"strings"

"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/config_loader"
"github.com/openshift-hyperfleet/hyperfleet-adapter/internal/k8s_client"
)

// ParamConfig interface allows extractConfigParams to work with both AdapterConfig and Config
Expand All @@ -20,9 +18,9 @@ type ParamConfig interface {

// extractConfigParams extracts all configured parameters and populates execCtx.Params
// This is a pure function that directly modifies execCtx for simplicity
func extractConfigParams(config ParamConfig, execCtx *ExecutionContext, k8sClient k8s_client.K8sClient) error {
func extractConfigParams(config ParamConfig, execCtx *ExecutionContext) error {
for _, param := range config.GetParams() {
value, err := extractParam(execCtx.Ctx, param, execCtx.EventData, k8sClient)
value, err := extractParam(param, execCtx.EventData)
if err != nil {
if param.Required {
return NewExecutorError(PhaseParamExtraction, param.Name,
Expand Down Expand Up @@ -70,7 +68,7 @@ func extractConfigParams(config ParamConfig, execCtx *ExecutionContext, k8sClien
}

// extractParam extracts a single parameter based on its source
func extractParam(ctx context.Context, param config_loader.Parameter, eventData map[string]interface{}, k8sClient k8s_client.K8sClient) (interface{}, error) {
func extractParam(param config_loader.Parameter, eventData map[string]interface{}) (interface{}, error) {
source := param.Source

// Handle different source types
Expand All @@ -79,10 +77,6 @@ func extractParam(ctx context.Context, param config_loader.Parameter, eventData
return extractFromEnv(source[4:])
case strings.HasPrefix(source, "event."):
return extractFromEvent(source[6:], eventData)
case strings.HasPrefix(source, "secret."):
return extractFromSecret(ctx, source[7:], k8sClient)
case strings.HasPrefix(source, "configmap."):
return extractFromConfigMap(ctx, source[10:], k8sClient)
case source == "":
// No source specified, return default or nil
return param.Default, nil
Expand Down Expand Up @@ -128,36 +122,6 @@ func extractFromEvent(path string, eventData map[string]interface{}) (interface{
return current, nil
}

// extractFromSecret extracts a value from a Kubernetes Secret
// Format: secret.<namespace>.<secret-name>.<key> (namespace is required)
func extractFromSecret(ctx context.Context, path string, k8sClient k8s_client.K8sClient) (interface{}, error) {
if k8sClient == nil {
return nil, fmt.Errorf("kubernetes client not configured, cannot extract from secret")
}

value, err := k8sClient.ExtractFromSecret(ctx, path)
if err != nil {
return nil, err
}

return value, nil
}

// extractFromConfigMap extracts a value from a Kubernetes ConfigMap
// Format: configmap.<namespace>.<configmap-name>.<key> (namespace is required)
func extractFromConfigMap(ctx context.Context, path string, k8sClient k8s_client.K8sClient) (interface{}, error) {
if k8sClient == nil {
return nil, fmt.Errorf("kubernetes client not configured, cannot extract from configmap")
}

value, err := k8sClient.ExtractFromConfigMap(ctx, path)
if err != nil {
return nil, err
}

return value, nil
}

// addMetadataParams adds adapter and event metadata to execCtx.Params
func addMetadataParams(config ParamConfig, execCtx *ExecutionContext) {
metadata := config.GetMetadata()
Expand Down
Loading