@@ -4,6 +4,9 @@ package apiserverapp
44import (
55 "context"
66 "fmt"
7+ "net"
8+ "net/url"
9+ "time"
710
811 metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
912 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -23,6 +26,7 @@ import (
2326 "k8s.io/kube-openapi/pkg/validation/spec"
2427
2528 aggregationv1alpha1 "github.com/coder/coder-k8s/api/aggregation/v1alpha1"
29+ "github.com/coder/coder-k8s/internal/aggregated/coder"
2630 "github.com/coder/coder-k8s/internal/aggregated/storage"
2731)
2832
@@ -32,6 +36,20 @@ const (
3236 serverName = "coder-k8s-aggregated-apiserver"
3337)
3438
39+ // Options configures aggregated-apiserver bootstrap behavior.
40+ type Options struct {
41+ // SecureServingPort used when Listener is nil. Default: DefaultSecureServingPort.
42+ SecureServingPort int
43+ // Listener allows tests to bind to 127.0.0.1:0.
44+ Listener net.Listener
45+ // CoderURL is an optional fallback URL when CoderControlPlane status has no URL.
46+ CoderURL string
47+ // CoderSessionToken is the admin session token.
48+ CoderSessionToken string
49+ // CoderRequestTimeout for SDK calls. Default 30s.
50+ CoderRequestTimeout time.Duration
51+ }
52+
3553// NewScheme builds the runtime scheme used by the aggregated API server.
3654func NewScheme () * runtime.Scheme {
3755 scheme := runtime .NewScheme ()
@@ -96,10 +114,17 @@ func NewRecommendedConfig(
96114}
97115
98116// NewAPIGroupInfo creates APIGroupInfo for the aggregation.coder.com API group.
99- func NewAPIGroupInfo (scheme * runtime.Scheme , codecs serializer.CodecFactory ) (* genericapiserver.APIGroupInfo , error ) {
117+ func NewAPIGroupInfo (
118+ scheme * runtime.Scheme ,
119+ codecs serializer.CodecFactory ,
120+ provider coder.ClientProvider ,
121+ ) (* genericapiserver.APIGroupInfo , error ) {
100122 if scheme == nil {
101123 return nil , fmt .Errorf ("assertion failed: scheme must not be nil" )
102124 }
125+ if provider == nil {
126+ return nil , fmt .Errorf ("assertion failed: coder client provider must not be nil" )
127+ }
103128
104129 parameterCodec := runtime .NewParameterCodec (scheme )
105130 apiGroupInfo := genericapiserver .NewDefaultAPIGroupInfo (
@@ -109,8 +134,8 @@ func NewAPIGroupInfo(scheme *runtime.Scheme, codecs serializer.CodecFactory) (*g
109134 codecs ,
110135 )
111136 apiGroupInfo .VersionedResourcesStorageMap [aggregationv1alpha1 .SchemeGroupVersion .Version ] = map [string ]rest.Storage {
112- "coderworkspaces" : storage .NewWorkspaceStorage (),
113- "codertemplates" : storage .NewTemplateStorage (),
137+ "coderworkspaces" : storage .NewWorkspaceStorage (provider ),
138+ "codertemplates" : storage .NewTemplateStorage (provider ),
114139 }
115140 return & apiGroupInfo , nil
116141}
@@ -146,9 +171,48 @@ func NewGenericAPIServer(recommendedConfig *genericapiserver.RecommendedConfig)
146171
147172// Run starts the aggregated API server application mode.
148173func Run (ctx context.Context ) error {
174+ return RunWithOptions (ctx , Options {})
175+ }
176+
177+ // RunWithOptions starts the aggregated API server application mode.
178+ func RunWithOptions (ctx context.Context , opts Options ) error {
149179 if ctx == nil {
150180 return fmt .Errorf ("assertion failed: context must not be nil" )
151181 }
182+ if opts .CoderURL == "" {
183+ return fmt .Errorf ("assertion failed: coder URL must not be empty" )
184+ }
185+ if opts .CoderSessionToken == "" {
186+ return fmt .Errorf ("assertion failed: coder session token must not be empty" )
187+ }
188+ if opts .CoderRequestTimeout < 0 {
189+ return fmt .Errorf ("assertion failed: coder request timeout must not be negative" )
190+ }
191+
192+ parsedCoderURL , err := url .Parse (opts .CoderURL )
193+ if err != nil {
194+ return fmt .Errorf ("parse coder URL %q: %w" , opts .CoderURL , err )
195+ }
196+ if parsedCoderURL == nil {
197+ return fmt .Errorf ("assertion failed: parsed coder URL must not be nil" )
198+ }
199+
200+ requestTimeout := opts .CoderRequestTimeout
201+ if requestTimeout == 0 {
202+ requestTimeout = 30 * time .Second
203+ }
204+
205+ provider , err := coder .NewStaticClientProvider (coder.Config {
206+ CoderURL : parsedCoderURL ,
207+ SessionToken : opts .CoderSessionToken ,
208+ RequestTimeout : requestTimeout ,
209+ })
210+ if err != nil {
211+ return fmt .Errorf ("build coder client provider: %w" , err )
212+ }
213+ if provider == nil {
214+ return fmt .Errorf ("assertion failed: coder client provider is nil after successful construction" )
215+ }
152216
153217 scheme := NewScheme ()
154218 if scheme == nil {
@@ -157,7 +221,18 @@ func Run(ctx context.Context) error {
157221
158222 codecs := serializer .NewCodecFactory (scheme )
159223 secureServingOptions := genericoptions .NewSecureServingOptions ()
160- secureServingOptions .BindPort = DefaultSecureServingPort
224+ secureServingPort := opts .SecureServingPort
225+ if secureServingPort == 0 {
226+ secureServingPort = DefaultSecureServingPort
227+ }
228+ if secureServingPort < 0 {
229+ return fmt .Errorf ("assertion failed: secure serving port must not be negative" )
230+ }
231+ secureServingOptions .BindPort = secureServingPort
232+ if opts .Listener != nil {
233+ secureServingOptions .Listener = opts .Listener
234+ secureServingOptions .BindPort = 0
235+ }
161236 secureServingOptions .ServerCert .CertDirectory = ""
162237 secureServingOptions .ServerCert .PairName = ""
163238
@@ -171,7 +246,7 @@ func Run(ctx context.Context) error {
171246 return err
172247 }
173248
174- apiGroupInfo , err := NewAPIGroupInfo (scheme , codecs )
249+ apiGroupInfo , err := NewAPIGroupInfo (scheme , codecs , provider )
175250 if err != nil {
176251 return fmt .Errorf ("build API group info: %w" , err )
177252 }
0 commit comments