@@ -22,13 +22,23 @@ import (
2222const defaultClusterName = "openframe-dev"
2323
2424// Service provides bootstrap functionality
25- type Service struct {}
25+ type Service struct {
26+ clusterService cluster.ServiceInterface
27+ }
2628
2729// NewService creates a new bootstrap service
2830func NewService () * Service {
2931 return & Service {}
3032}
3133
34+ // NewServiceWithDependencies creates a new bootstrap service with an
35+ // injected cluster service, following the constructor-injection pattern
36+ // used by ClusterService. When clusterService is nil, it is lazily
37+ // constructed with a real command executor at the point of use.
38+ func NewServiceWithDependencies (clusterService cluster.ServiceInterface ) * Service {
39+ return & Service {clusterService : clusterService }
40+ }
41+
3242// Execute handles the bootstrap command execution
3343func (s * Service ) Execute (cmd * cobra.Command , args []string ) error {
3444 // Get verbose flag - first check local flag, then root command
@@ -166,7 +176,7 @@ func (s *Service) createClusterSuppressed(ctx context.Context, clusterName strin
166176
167177// installChart installs charts on the created cluster
168178func (s * Service ) installChart (ctx context.Context , clusterName string , nonInteractive , verbose bool , kubeConfig * rest.Config ) error {
169- return chartServices .InstallChartsWithConfigContext (ctx , bootstrapInstallRequest (clusterName , nonInteractive , verbose , kubeConfig ))
179+ return chartServices .InstallChartsWithConfigContext (ctx , s . bootstrapInstallRequest (clusterName , nonInteractive , verbose , kubeConfig ))
170180}
171181
172182// bootstrapInstallRequest builds the chart-install request for the cluster the
@@ -176,7 +186,14 @@ func (s *Service) installChart(ctx context.Context, clusterName string, nonInter
176186// "install OpenFrame chart on ”?" and every helm call ran WITHOUT
177187// --kube-context, silently targeting the kubeconfig's current context instead
178188// of the cluster the native client was pointed at.
179- func bootstrapInstallRequest (clusterName string , nonInteractive , verbose bool , kubeConfig * rest.Config ) utilTypes.InstallationRequest {
189+ func (s * Service ) bootstrapInstallRequest (clusterName string , nonInteractive , verbose bool , kubeConfig * rest.Config ) utilTypes.InstallationRequest {
190+ // Prefer the injected cluster service (constructor injection); fall back
191+ // to constructing one with a real command executor if the Service was
192+ // created via the zero-dependency NewService() constructor.
193+ clusterAccess := s .clusterService
194+ if clusterAccess == nil {
195+ clusterAccess = cluster .NewClusterService (executor .NewRealCommandExecutor (false , verbose ))
196+ }
180197 return utilTypes.InstallationRequest {
181198 Args : []string {clusterName },
182199 Force : false ,
@@ -196,6 +213,7 @@ func bootstrapInstallRequest(clusterName string, nonInteractive, verbose bool, k
196213 KubeContext : "k3d-" + clusterName ,
197214 // Inject cluster access from the orchestrator (composition root) so the
198215 // app subsystem stays isolated from cluster-creation code (req 18/19).
199- ClusterAccess : cluster . NewClusterService ( executor . NewRealCommandExecutor ( false , verbose )) ,
216+ ClusterAccess : clusterAccess ,
200217 }
201218}
219+
0 commit comments