package server import ( "context" "errors" "net/http" "github.com/rancher/steve/pkg/server/resources/schemas" "github.com/rancher/dynamiclistener/server" "github.com/rancher/steve/pkg/accesscontrol" "github.com/rancher/steve/pkg/client" "github.com/rancher/steve/pkg/clustercache" schemacontroller "github.com/rancher/steve/pkg/controllers/schema" "github.com/rancher/steve/pkg/dashboard" "github.com/rancher/steve/pkg/schema" "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/steve/pkg/server/handler" "github.com/rancher/steve/pkg/server/resources" "github.com/rancher/steve/pkg/server/resources/common" ) var ErrConfigRequired = errors.New("rest config is required") func setDefaults(server *Server) error { if server.RestConfig == nil { return ErrConfigRequired } if server.Controllers == nil { var err error server.Controllers, err = NewController(server.RestConfig) if err != nil { return err } } if server.Next == nil { server.Next = http.NotFoundHandler() } if server.BaseSchemas == nil { server.BaseSchemas = types.EmptyAPISchemas() } return nil } func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collection, error) { err := setDefaults(server) if err != nil { return nil, nil, err } cf := server.ClientFactory if cf == nil { cf, err = client.NewFactory(server.RestConfig, server.AuthMiddleware != nil) if err != nil { return nil, nil, err } server.ClientFactory = cf } asl := server.AccessSetLookup if asl == nil { asl = accesscontrol.NewAccessStore(ctx, true, server.RBAC) } ccache := clustercache.NewClusterCache(ctx, cf.DynamicClient()) server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, ccache, cf) server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl, server.K8s.Discovery())...) cols, err := common.NewDynamicColumns(server.RestConfig) if err != nil { return nil, nil, err } sf := schema.NewCollection(ctx, server.BaseSchemas, asl) schemas.SetupWatcher(ctx, server.BaseSchemas, asl, sf) sync := schemacontroller.Register(ctx, cols, server.K8s.Discovery(), server.CRD.CustomResourceDefinition(), server.API.APIService(), server.K8s.AuthorizationV1().SelfSubjectAccessReviews(), ccache, sf) handler, err := handler.New(server.RestConfig, sf, server.AuthMiddleware, server.Next, server.Router) if err != nil { return nil, nil, err } server.PostStartHooks = append(server.PostStartHooks, func() error { return sync() }) if server.DashboardURL != nil && server.DashboardURL() != "" { handler = dashboard.Route(handler, server.DashboardURL) } return handler, sf, nil } func (c *Server) Handler(ctx context.Context) (http.Handler, error) { handler, sf, err := setup(ctx, c) if err != nil { return nil, err } c.Next = handler for _, hook := range c.StartHooks { if err := hook(ctx, c); err != nil { return nil, err } } for i := range c.SchemaTemplates { sf.AddTemplate(&c.SchemaTemplates[i]) } if err := c.Controllers.Start(ctx); err != nil { return nil, err } for _, hook := range c.PostStartHooks { if err := hook(); err != nil { return nil, err } } return c.Next, nil } func (c *Server) ListenAndServe(ctx context.Context, httpsPort, httpPort int, opts *server.ListenOpts) error { handler, err := c.Handler(ctx) if err != nil { return err } if opts == nil { opts = &server.ListenOpts{} } if opts.Storage == nil && opts.Secrets == nil { opts.Secrets = c.Core.Secret() } if err := server.ListenAndServe(ctx, httpsPort, httpPort, handler, opts); err != nil { return err } if err := c.Controllers.Start(ctx); err != nil { return err } <-ctx.Done() return ctx.Err() }