diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml index f81f636..fd1e55e 100644 --- a/charts/templates/deployment.yaml +++ b/charts/templates/deployment.yaml @@ -74,16 +74,16 @@ spec: value: {{ .Values.auth.enableJwt | quote }} - name: ENABLE_AUTHZ value: {{ .Values.auth.enableAuthz | quote }} - {{- if .Values.adapters }} - {{- if .Values.adapters.cluster }} - - name: HYPERFLEET_CLUSTER_ADAPTERS - value: {{ .Values.adapters.cluster | toJson | quote }} - {{- end }} - {{- if .Values.adapters.nodepool }} - - name: HYPERFLEET_NODEPOOL_ADAPTERS - value: {{ .Values.adapters.nodepool | toJson | quote }} + {{- if not (and .Values.adapters (hasKey .Values.adapters "cluster")) }} + {{- fail "adapters.cluster is required - specify which adapters must be ready for clusters (e.g., [\"validation\",\"dns\",\"pullsecret\",\"hypershift\"])" }} {{- end }} + {{- if not (and .Values.adapters (hasKey .Values.adapters "nodepool")) }} + {{- fail "adapters.nodepool is required - specify which adapters must be ready for nodepools (e.g., [\"validation\",\"hypershift\"])" }} {{- end }} + - name: HYPERFLEET_CLUSTER_ADAPTERS + value: {{ .Values.adapters.cluster | default list | toJson | quote }} + - name: HYPERFLEET_NODEPOOL_ADAPTERS + value: {{ .Values.adapters.nodepool | default list | toJson | quote }} {{- if .Values.env }} {{- range .Values.env }} - name: {{ .name }} diff --git a/charts/values.yaml b/charts/values.yaml index cf8007f..0b1717c 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -124,16 +124,17 @@ auth: # Override with: --set auth.jwksUrl="https://your-idp/.../.well-known/jwks.json" jwksUrl: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/certs" -# Adapter configuration +# Adapter configuration (REQUIRED) # Configure which adapters must be ready for resources to be marked as "Ready" -# If not specified, defaults are used (see pkg/config/adapter.go) +# Both cluster and nodepool adapters MUST be specified - the application will not start without them +# Empty arrays [] are valid if you want no adapters required for the Ready state adapters: - # Required adapters for cluster "Ready" state - # Default: ["validation", "dns", "pullsecret", "hypershift"] - cluster: [] - # Required adapters for nodepool "Ready" state - # Default: ["validation", "hypershift"] - nodepool: [] + # Required adapters for cluster "Ready" state (REQUIRED) + # Example: ["validation", "dns", "pullsecret", "hypershift"] + # cluster: [] + # Required adapters for nodepool "Ready" state (REQUIRED) + # Example: ["validation", "hypershift"] + # nodepool: [] # ServiceMonitor for Prometheus Operator # Enables automatic metrics discovery in clusters with Prometheus Operator diff --git a/cmd/hyperfleet-api/environments/framework.go b/cmd/hyperfleet-api/environments/framework.go index 5394e1a..a0af8db 100755 --- a/cmd/hyperfleet-api/environments/framework.go +++ b/cmd/hyperfleet-api/environments/framework.go @@ -87,6 +87,12 @@ func (e *Env) Initialize() error { os.Exit(1) } + // Load adapter configuration from environment variables + if err := e.Config.LoadAdapters(); err != nil { + logger.WithError(ctx, err).Error("Failed to load adapter configuration") + os.Exit(1) + } + // each env will set db explicitly because the DB impl has a `once` init section if err := envImpl.OverrideDatabase(&e.Database); err != nil { logger.WithError(ctx, err).Error("Failed to configure Database") diff --git a/cmd/hyperfleet-api/environments/framework_test.go b/cmd/hyperfleet-api/environments/framework_test.go index 0beaffb..a1604a4 100755 --- a/cmd/hyperfleet-api/environments/framework_test.go +++ b/cmd/hyperfleet-api/environments/framework_test.go @@ -1,6 +1,7 @@ package environments import ( + "os" "os/exec" "reflect" "testing" @@ -23,6 +24,14 @@ func BenchmarkGetDynos(b *testing.B) { } func TestLoadServices(t *testing.T) { + // Set required adapter configuration for tests + if os.Getenv("HYPERFLEET_CLUSTER_ADAPTERS") == "" { + t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation","dns","pullsecret","hypershift"]`) + } + if os.Getenv("HYPERFLEET_NODEPOOL_ADAPTERS") == "" { + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation","hypershift"]`) + } + env := Environment() err := env.AddFlags(pflag.CommandLine) if err != nil { diff --git a/docs/deployment.md b/docs/deployment.md index c927b97..2fd1540 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -85,13 +85,13 @@ export OPENAPI_SCHEMA_PATH=/path/to/custom-schema.yaml - `LOG_LEVEL` - Logging level: `debug`, `info`, `warn`, `error` (default: `info`) - `LOG_FORMAT` - Log format: `json`, `text` (default: `json`) -**Adapter Requirements:** +**Adapter Requirements (REQUIRED):** Configure which adapters must be ready for resources to be marked as "Ready". +These environment variables are **required** - the application will not start without them. -**Default values** (if not configured): -- Cluster: `["validation","dns","pullsecret","hypershift"]` -- NodePool: `["validation","hypershift"]` +- `HYPERFLEET_CLUSTER_ADAPTERS` - JSON array of required cluster adapters (e.g., `["validation","dns","pullsecret","hypershift"]`) +- `HYPERFLEET_NODEPOOL_ADAPTERS` - JSON array of required nodepool adapters (e.g., `["validation","hypershift"]`) **Option 1: Using structured values (Helm only, recommended)** ```yaml @@ -107,22 +107,14 @@ adapters: - hypershift ``` -**Option 2: Using environment variables in Helm** -```yaml -# values.yaml -env: - - name: HYPERFLEET_CLUSTER_ADAPTERS - value: '["validation","dns","pullsecret","hypershift"]' - - name: HYPERFLEET_NODEPOOL_ADAPTERS - value: '["validation","hypershift"]' -``` - -**Option 3: Direct environment variable (non-Helm)** +**Option 2: Direct environment variable (non-Helm)** ```bash export HYPERFLEET_CLUSTER_ADAPTERS='["validation","dns","pullsecret","hypershift"]' export HYPERFLEET_NODEPOOL_ADAPTERS='["validation","hypershift"]' ``` +**Note:** Empty arrays (`[]`) are valid if you want no adapters to be required for the Ready state. + ## Kubernetes Deployment ### Using Helm Chart @@ -136,7 +128,9 @@ Deploy with built-in PostgreSQL for development and testing: ```bash helm install hyperfleet-api ./charts/ \ --namespace hyperfleet-system \ - --create-namespace + --create-namespace \ + --set adapters.cluster='{validation,dns,pullsecret,hypershift}' \ + --set adapters.nodepool='{validation,hypershift}' ``` This creates: @@ -168,7 +162,9 @@ helm install hyperfleet-api ./charts/ \ --namespace hyperfleet-system \ --set database.postgresql.enabled=false \ --set database.external.enabled=true \ - --set database.external.secretName=hyperfleet-db-external + --set database.external.secretName=hyperfleet-db-external \ + --set adapters.cluster='{validation,dns,pullsecret,hypershift}' \ + --set adapters.nodepool='{validation,hypershift}' ``` #### Custom Image Deployment @@ -180,7 +176,9 @@ helm install hyperfleet-api ./charts/ \ --namespace hyperfleet-system \ --set image.registry=quay.io \ --set image.repository=myuser/hyperfleet-api \ - --set image.tag=v1.0.0 + --set image.tag=v1.0.0 \ + --set adapters.cluster='{validation,dns,pullsecret,hypershift}' \ + --set adapters.nodepool='{validation,hypershift}' ``` #### Upgrade Deployment @@ -211,6 +209,8 @@ helm uninstall hyperfleet-api --namespace hyperfleet-system | `image.repository` | Image repository | `openshift-hyperfleet/hyperfleet-api` | | `image.tag` | Image tag | `latest` | | `image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `adapters.cluster` | Required cluster adapters (REQUIRED) | - | +| `adapters.nodepool` | Required nodepool adapters (REQUIRED) | - | | `auth.enableJwt` | Enable JWT authentication | `true` | | `database.postgresql.enabled` | Enable built-in PostgreSQL | `true` | | `database.external.enabled` | Use external database | `false` | @@ -248,13 +248,16 @@ database: enabled: true secretName: hyperfleet-db-external -# Optional: customize adapter requirements (YAML table format) +# Required: specify adapter requirements adapters: cluster: - validation - dns + - pullsecret + - hypershift nodepool: - validation + - hypershift replicaCount: 3 @@ -421,7 +424,9 @@ helm install hyperfleet-api ./charts/ \ --set image.tag=dev-abc123 \ --set auth.enableJwt=false \ --set database.postgresql.enabled=false \ - --set database.external.enabled=true + --set database.external.enabled=true \ + --set adapters.cluster='{validation,dns,pullsecret,hypershift}' \ + --set adapters.nodepool='{validation,hypershift}' # 6. Verify deployment kubectl get pods diff --git a/pkg/config/adapter.go b/pkg/config/adapter.go index 261371d..d067908 100644 --- a/pkg/config/adapter.go +++ b/pkg/config/adapter.go @@ -2,6 +2,7 @@ package config import ( "encoding/json" + "fmt" "log" "os" ) @@ -12,55 +13,49 @@ type AdapterRequirementsConfig struct { RequiredNodePoolAdapters []string } -// NewAdapterRequirementsConfig creates config from environment variables or defaults -func NewAdapterRequirementsConfig() *AdapterRequirementsConfig { - config := &AdapterRequirementsConfig{ - RequiredClusterAdapters: getDefaultClusterAdapters(), - RequiredNodePoolAdapters: getDefaultNodePoolAdapters(), +// NewAdapterRequirementsConfig creates config from environment variables. +// Returns an error if required environment variables are not set. +// Required env vars: HYPERFLEET_CLUSTER_ADAPTERS, HYPERFLEET_NODEPOOL_ADAPTERS +// Format: JSON array, e.g., '["validation","dns","pullsecret","hypershift"]' +func NewAdapterRequirementsConfig() (*AdapterRequirementsConfig, error) { + config := &AdapterRequirementsConfig{} + + if err := config.LoadFromEnv(); err != nil { + return nil, err } - config.LoadFromEnv() - return config + return config, nil } // LoadFromEnv loads adapter lists from HYPERFLEET_CLUSTER_ADAPTERS and // HYPERFLEET_NODEPOOL_ADAPTERS (JSON array format: '["adapter1","adapter2"]') -func (c *AdapterRequirementsConfig) LoadFromEnv() { - if clusterAdaptersStr := os.Getenv("HYPERFLEET_CLUSTER_ADAPTERS"); clusterAdaptersStr != "" { - var adapters []string - if err := json.Unmarshal([]byte(clusterAdaptersStr), &adapters); err == nil { - c.RequiredClusterAdapters = adapters - log.Printf("Loaded HYPERFLEET_CLUSTER_ADAPTERS from env: %v", adapters) - } else { - log.Printf("WARNING: Failed to parse HYPERFLEET_CLUSTER_ADAPTERS, using defaults: %v", err) - } +// Returns an error if the environment variables are not set or have invalid JSON. +func (c *AdapterRequirementsConfig) LoadFromEnv() error { + clusterAdaptersStr := os.Getenv("HYPERFLEET_CLUSTER_ADAPTERS") + if clusterAdaptersStr == "" { + return fmt.Errorf("HYPERFLEET_CLUSTER_ADAPTERS environment variable is required but not set") } - if nodepoolAdaptersStr := os.Getenv("HYPERFLEET_NODEPOOL_ADAPTERS"); nodepoolAdaptersStr != "" { - var adapters []string - if err := json.Unmarshal([]byte(nodepoolAdaptersStr), &adapters); err == nil { - c.RequiredNodePoolAdapters = adapters - log.Printf("Loaded HYPERFLEET_NODEPOOL_ADAPTERS from env: %v", adapters) - } else { - log.Printf("WARNING: Failed to parse HYPERFLEET_NODEPOOL_ADAPTERS, using defaults: %v", err) - } + var clusterAdapters []string + if err := json.Unmarshal([]byte(clusterAdaptersStr), &clusterAdapters); err != nil { + return fmt.Errorf("failed to parse HYPERFLEET_CLUSTER_ADAPTERS: %w "+ + "(expected JSON array, e.g., '[\"validation\",\"dns\"]')", err) } -} + c.RequiredClusterAdapters = clusterAdapters + log.Printf("Loaded HYPERFLEET_CLUSTER_ADAPTERS from env: %v", clusterAdapters) -// Default cluster adapters: validation, dns, pullsecret, hypershift -func getDefaultClusterAdapters() []string { - return []string{ - "validation", - "dns", - "pullsecret", - "hypershift", + nodepoolAdaptersStr := os.Getenv("HYPERFLEET_NODEPOOL_ADAPTERS") + if nodepoolAdaptersStr == "" { + return fmt.Errorf("HYPERFLEET_NODEPOOL_ADAPTERS environment variable is required but not set") } -} -// Default nodepool adapters: validation, hypershift -func getDefaultNodePoolAdapters() []string { - return []string{ - "validation", - "hypershift", + var nodepoolAdapters []string + if err := json.Unmarshal([]byte(nodepoolAdaptersStr), &nodepoolAdapters); err != nil { + return fmt.Errorf("failed to parse HYPERFLEET_NODEPOOL_ADAPTERS: %w "+ + "(expected JSON array, e.g., '[\"validation\",\"hypershift\"]')", err) } + c.RequiredNodePoolAdapters = nodepoolAdapters + log.Printf("Loaded HYPERFLEET_NODEPOOL_ADAPTERS from env: %v", nodepoolAdapters) + + return nil } diff --git a/pkg/config/adapter_test.go b/pkg/config/adapter_test.go index 7ee0e3a..db8bc6b 100644 --- a/pkg/config/adapter_test.go +++ b/pkg/config/adapter_test.go @@ -6,70 +6,61 @@ import ( . "github.com/onsi/gomega" ) -func TestNewAdapterRequirementsConfig_Defaults(t *testing.T) { +func TestNewAdapterRequirementsConfig_MissingClusterAdapters(t *testing.T) { RegisterTestingT(t) // Ensure env vars are not set t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", "") - t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", "") - - config := NewAdapterRequirementsConfig() + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation"]`) - // Verify default cluster adapters - Expect(config.RequiredClusterAdapters).To(Equal([]string{ - "validation", - "dns", - "pullsecret", - "hypershift", - })) + _, err := NewAdapterRequirementsConfig() - // Verify default nodepool adapters - Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ - "validation", - "hypershift", - })) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("HYPERFLEET_CLUSTER_ADAPTERS")) + Expect(err.Error()).To(ContainSubstring("required")) } -func TestLoadFromEnv_ClusterAdapters(t *testing.T) { +func TestNewAdapterRequirementsConfig_MissingNodePoolAdapters(t *testing.T) { RegisterTestingT(t) - t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation","dns"]`) + t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation"]`) t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", "") - config := NewAdapterRequirementsConfig() + _, err := NewAdapterRequirementsConfig() - // Verify cluster adapters from env - Expect(config.RequiredClusterAdapters).To(Equal([]string{ - "validation", - "dns", - })) - - // Verify nodepool adapters use defaults - Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ - "validation", - "hypershift", - })) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("HYPERFLEET_NODEPOOL_ADAPTERS")) + Expect(err.Error()).To(ContainSubstring("required")) } -func TestLoadFromEnv_NodePoolAdapters(t *testing.T) { +func TestNewAdapterRequirementsConfig_MissingBothAdapters(t *testing.T) { RegisterTestingT(t) - t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation"]`) t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", "") + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", "") - config := NewAdapterRequirementsConfig() + _, err := NewAdapterRequirementsConfig() - // Verify cluster adapters use defaults + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("HYPERFLEET_CLUSTER_ADAPTERS")) +} + +func TestLoadFromEnv_ClusterAdapters(t *testing.T) { + RegisterTestingT(t) + + t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation","dns"]`) + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation","hypershift"]`) + + config, err := NewAdapterRequirementsConfig() + + Expect(err).NotTo(HaveOccurred()) Expect(config.RequiredClusterAdapters).To(Equal([]string{ "validation", "dns", - "pullsecret", - "hypershift", })) - - // Verify nodepool adapters from env Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ "validation", + "hypershift", })) } @@ -79,9 +70,9 @@ func TestLoadFromEnv_BothAdapters(t *testing.T) { t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation","dns"]`) t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation"]`) - config := NewAdapterRequirementsConfig() + config, err := NewAdapterRequirementsConfig() - // Verify both are loaded from env + Expect(err).NotTo(HaveOccurred()) Expect(config.RequiredClusterAdapters).To(Equal([]string{ "validation", "dns", @@ -95,67 +86,71 @@ func TestLoadFromEnv_EmptyArray(t *testing.T) { RegisterTestingT(t) t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `[]`) + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `[]`) - config := NewAdapterRequirementsConfig() + config, err := NewAdapterRequirementsConfig() - // Verify empty array is loaded + Expect(err).NotTo(HaveOccurred()) Expect(config.RequiredClusterAdapters).To(Equal([]string{})) + Expect(config.RequiredNodePoolAdapters).To(Equal([]string{})) } func TestLoadFromEnv_InvalidJSON_ClusterAdapters(t *testing.T) { RegisterTestingT(t) t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `not-valid-json`) + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation"]`) - config := NewAdapterRequirementsConfig() + _, err := NewAdapterRequirementsConfig() - // Verify defaults are used when JSON is invalid - Expect(config.RequiredClusterAdapters).To(Equal([]string{ - "validation", - "dns", - "pullsecret", - "hypershift", - })) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to parse HYPERFLEET_CLUSTER_ADAPTERS")) } func TestLoadFromEnv_InvalidJSON_NodePoolAdapters(t *testing.T) { RegisterTestingT(t) + t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation"]`) t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `{invalid}`) - config := NewAdapterRequirementsConfig() + _, err := NewAdapterRequirementsConfig() - // Verify defaults are used when JSON is invalid - Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ - "validation", - "hypershift", - })) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to parse HYPERFLEET_NODEPOOL_ADAPTERS")) } func TestLoadFromEnv_SingleAdapter(t *testing.T) { RegisterTestingT(t) t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation"]`) + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["hypershift"]`) - config := NewAdapterRequirementsConfig() + config, err := NewAdapterRequirementsConfig() - // Verify single adapter is loaded + Expect(err).NotTo(HaveOccurred()) Expect(config.RequiredClusterAdapters).To(Equal([]string{ "validation", })) + Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ + "hypershift", + })) } func TestLoadFromEnv_CustomAdapterNames(t *testing.T) { RegisterTestingT(t) t.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["custom-adapter-1","custom-adapter-2","test"]`) + t.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["custom-np-adapter"]`) - config := NewAdapterRequirementsConfig() + config, err := NewAdapterRequirementsConfig() - // Verify custom adapters are loaded + Expect(err).NotTo(HaveOccurred()) Expect(config.RequiredClusterAdapters).To(Equal([]string{ "custom-adapter-1", "custom-adapter-2", "test", })) + Expect(config.RequiredNodePoolAdapters).To(Equal([]string{ + "custom-np-adapter", + })) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 7395706..326b7ba 100755 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -13,12 +13,13 @@ import ( ) type ApplicationConfig struct { - Server *ServerConfig `json:"server"` - Metrics *MetricsConfig `json:"metrics"` - Health *HealthConfig `json:"health"` - Database *DatabaseConfig `json:"database"` - OCM *OCMConfig `json:"ocm"` - Logging *LoggingConfig `json:"logging"` + Server *ServerConfig `json:"server"` + Metrics *MetricsConfig `json:"metrics"` + Health *HealthConfig `json:"health"` + Database *DatabaseConfig `json:"database"` + OCM *OCMConfig `json:"ocm"` + Logging *LoggingConfig `json:"logging"` + Adapters *AdapterRequirementsConfig `json:"adapters"` } func NewApplicationConfig() *ApplicationConfig { @@ -42,6 +43,17 @@ func (c *ApplicationConfig) AddFlags(flagset *pflag.FlagSet) { c.Logging.AddFlags(flagset) } +// LoadAdapters initializes the adapter configuration from environment variables. +// This should be called once during application startup. +func (c *ApplicationConfig) LoadAdapters() error { + adapters, err := NewAdapterRequirementsConfig() + if err != nil { + return err + } + c.Adapters = adapters + return nil +} + func (c *ApplicationConfig) ReadFiles() []string { readFiles := []struct { f func() error diff --git a/pkg/services/cluster_test.go b/pkg/services/cluster_test.go index 4c9f436..8d972d0 100644 --- a/pkg/services/cluster_test.go +++ b/pkg/services/cluster_test.go @@ -18,6 +18,14 @@ const ( testClusterID = "test-cluster-id" ) +// testAdapterConfig creates a test adapter config with default values +func testAdapterConfig() *config.AdapterRequirementsConfig { + return &config.AdapterRequirementsConfig{ + RequiredClusterAdapters: []string{"validation", "dns", "pullsecret", "hypershift"}, + RequiredNodePoolAdapters: []string{"validation", "hypershift"}, + } +} + // Mock implementations for testing ProcessAdapterStatus type mockClusterDao struct { @@ -162,7 +170,7 @@ func TestProcessAdapterStatus_UnknownCondition(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testAdapterConfig() service := NewClusterService(clusterDao, adapterStatusDao, config) ctx := context.Background() @@ -202,7 +210,7 @@ func TestProcessAdapterStatus_TrueCondition(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testAdapterConfig() service := NewClusterService(clusterDao, adapterStatusDao, config) ctx := context.Background() @@ -253,7 +261,7 @@ func TestProcessAdapterStatus_FalseCondition(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testAdapterConfig() service := NewClusterService(clusterDao, adapterStatusDao, config) ctx := context.Background() @@ -303,7 +311,7 @@ func TestProcessAdapterStatus_NoAvailableCondition(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testAdapterConfig() service := NewClusterService(clusterDao, adapterStatusDao, config) ctx := context.Background() @@ -382,7 +390,7 @@ func TestProcessAdapterStatus_MultipleConditions_AvailableUnknown(t *testing.T) clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testAdapterConfig() service := NewClusterService(clusterDao, adapterStatusDao, config) ctx := context.Background() @@ -431,7 +439,7 @@ func TestClusterAvailableReadyTransitions(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testAdapterConfig() // Keep this small so we can cover transitions succinctly. adapterConfig.RequiredClusterAdapters = []string{"validation", "dns"} @@ -581,7 +589,7 @@ func TestClusterStaleAdapterStatusUpdatePolicy(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testAdapterConfig() adapterConfig.RequiredClusterAdapters = []string{"validation", "dns"} service := NewClusterService(clusterDao, adapterStatusDao, adapterConfig) @@ -656,7 +664,7 @@ func TestClusterSyntheticTimestampsStableWithoutAdapterStatus(t *testing.T) { clusterDao := newMockClusterDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testAdapterConfig() adapterConfig.RequiredClusterAdapters = []string{"validation"} service := NewClusterService(clusterDao, adapterStatusDao, adapterConfig) diff --git a/pkg/services/node_pool_test.go b/pkg/services/node_pool_test.go index 6fd8b51..b72c3aa 100644 --- a/pkg/services/node_pool_test.go +++ b/pkg/services/node_pool_test.go @@ -18,6 +18,14 @@ const ( testNodePoolID = "test-nodepool-id" ) +// testNodePoolAdapterConfig creates a test adapter config with default values +func testNodePoolAdapterConfig() *config.AdapterRequirementsConfig { + return &config.AdapterRequirementsConfig{ + RequiredClusterAdapters: []string{"validation", "dns", "pullsecret", "hypershift"}, + RequiredNodePoolAdapters: []string{"validation", "hypershift"}, + } +} + // Mock implementations for testing NodePool ProcessAdapterStatus type mockNodePoolDao struct { @@ -79,7 +87,7 @@ func TestNodePoolProcessAdapterStatus_UnknownCondition(t *testing.T) { nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testNodePoolAdapterConfig() service := NewNodePoolService(nodePoolDao, adapterStatusDao, config) ctx := context.Background() @@ -119,7 +127,7 @@ func TestNodePoolProcessAdapterStatus_TrueCondition(t *testing.T) { nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testNodePoolAdapterConfig() service := NewNodePoolService(nodePoolDao, adapterStatusDao, config) ctx := context.Background() @@ -170,7 +178,7 @@ func TestNodePoolProcessAdapterStatus_MultipleConditions_AvailableUnknown(t *tes nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - config := config.NewAdapterRequirementsConfig() + config := testNodePoolAdapterConfig() service := NewNodePoolService(nodePoolDao, adapterStatusDao, config) ctx := context.Background() @@ -214,7 +222,7 @@ func TestNodePoolAvailableReadyTransitions(t *testing.T) { nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testNodePoolAdapterConfig() adapterConfig.RequiredNodePoolAdapters = []string{"validation", "hypershift"} service := NewNodePoolService(nodePoolDao, adapterStatusDao, adapterConfig) @@ -367,7 +375,7 @@ func TestNodePoolStaleAdapterStatusUpdatePolicy(t *testing.T) { nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testNodePoolAdapterConfig() adapterConfig.RequiredNodePoolAdapters = []string{"validation", "hypershift"} service := NewNodePoolService(nodePoolDao, adapterStatusDao, adapterConfig) @@ -442,7 +450,7 @@ func TestNodePoolSyntheticTimestampsStableWithoutAdapterStatus(t *testing.T) { nodePoolDao := newMockNodePoolDao() adapterStatusDao := newMockAdapterStatusDao() - adapterConfig := config.NewAdapterRequirementsConfig() + adapterConfig := testNodePoolAdapterConfig() adapterConfig.RequiredNodePoolAdapters = []string{"validation"} service := NewNodePoolService(nodePoolDao, adapterStatusDao, adapterConfig) diff --git a/plugins/clusters/plugin.go b/plugins/clusters/plugin.go index 944a1a4..d7eae5a 100644 --- a/plugins/clusters/plugin.go +++ b/plugins/clusters/plugin.go @@ -10,7 +10,6 @@ import ( "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/presenters" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/auth" - "github.com/openshift-hyperfleet/hyperfleet-api/pkg/config" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/dao" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/handlers" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/services" @@ -23,14 +22,11 @@ import ( type ServiceLocator func() services.ClusterService func NewServiceLocator(env *environments.Env) ServiceLocator { - // Initialize adapter requirements config from environment variables - adapterConfig := config.NewAdapterRequirementsConfig() - return func() services.ClusterService { return services.NewClusterService( dao.NewClusterDao(&env.Database.SessionFactory), dao.NewAdapterStatusDao(&env.Database.SessionFactory), - adapterConfig, + env.Config.Adapters, ) } } diff --git a/plugins/nodePools/plugin.go b/plugins/nodePools/plugin.go index 0b870f3..5215174 100644 --- a/plugins/nodePools/plugin.go +++ b/plugins/nodePools/plugin.go @@ -10,7 +10,6 @@ import ( "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/presenters" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/auth" - "github.com/openshift-hyperfleet/hyperfleet-api/pkg/config" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/dao" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/handlers" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/services" @@ -21,14 +20,11 @@ import ( type ServiceLocator func() services.NodePoolService func NewServiceLocator(env *environments.Env) ServiceLocator { - // Initialize adapter requirements config from environment variables - adapterConfig := config.NewAdapterRequirementsConfig() - return func() services.NodePoolService { return services.NewNodePoolService( dao.NewNodePoolDao(&env.Database.SessionFactory), dao.NewAdapterStatusDao(&env.Database.SessionFactory), - adapterConfig, + env.Config.Adapters, ) } } diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go index 5f22290..0348717 100755 --- a/test/integration/integration_test.go +++ b/test/integration/integration_test.go @@ -17,6 +17,15 @@ func TestMain(m *testing.M) { ctx := context.Background() logger.With(ctx, "go_version", runtime.Version()).Info("Starting integration test") + // Set adapter configuration for integration tests if not already set + // These are required by the NodePool plugin + if os.Getenv("HYPERFLEET_CLUSTER_ADAPTERS") == "" { + _ = os.Setenv("HYPERFLEET_CLUSTER_ADAPTERS", `["validation","dns","pullsecret","hypershift"]`) + } + if os.Getenv("HYPERFLEET_NODEPOOL_ADAPTERS") == "" { + _ = os.Setenv("HYPERFLEET_NODEPOOL_ADAPTERS", `["validation","hypershift"]`) + } + // Set OPENAPI_SCHEMA_PATH for integration tests if not already set // This enables schema validation middleware during tests if os.Getenv("OPENAPI_SCHEMA_PATH") == "" {