Skip to content

feat: Add high availability for multiple replicas #4555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 86 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
35b2fed
feat: HA tailnet coordinator
coadler Sep 22, 2022
68a812b
fixup! feat: HA tailnet coordinator
coadler Sep 23, 2022
774c5da
fixup! feat: HA tailnet coordinator
coadler Sep 23, 2022
bd82c5e
remove printlns
coadler Sep 23, 2022
02e079d
Merge branch 'main' into colin/pg-coordinate
coadler Oct 7, 2022
fbad8d0
close all connections on coordinator
coadler Oct 7, 2022
46803aa
impelement high availability feature
coadler Oct 7, 2022
d38391e
fixup! impelement high availability feature
coadler Oct 7, 2022
a0bcd64
fixup! impelement high availability feature
coadler Oct 7, 2022
1f33018
fixup! impelement high availability feature
coadler Oct 7, 2022
b6a5070
fixup! impelement high availability feature
coadler Oct 7, 2022
1883430
Add replicas
kylecarbs Oct 12, 2022
7dc968c
Add DERP meshing to arbitrary addresses
kylecarbs Oct 12, 2022
1dcf0d0
Move packages to highavailability folder
kylecarbs Oct 12, 2022
5c43d63
Merge branch 'main' into colin/pg-coordinate
kylecarbs Oct 12, 2022
4804269
Merge branch 'colin/pg-coordinate' into replica
kylecarbs Oct 12, 2022
289e139
Move coordinator to high availability package
kylecarbs Oct 12, 2022
585bc1d
Add flags for HA
kylecarbs Oct 12, 2022
fdb3557
Rename to replicasync
kylecarbs Oct 13, 2022
9124b00
Denest packages for replicas
kylecarbs Oct 13, 2022
d5555f6
Add test for multiple replicas
kylecarbs Oct 13, 2022
8dfc261
Fix coordination test
kylecarbs Oct 13, 2022
ff5968b
Add HA to the helm chart
kylecarbs Oct 13, 2022
557b390
Rename function pointer
kylecarbs Oct 13, 2022
186a5e2
Add warnings for HA
kylecarbs Oct 13, 2022
de5b13b
Add the ability to block endpoints
kylecarbs Oct 13, 2022
9a50ac4
Add flag to disable P2P connections
kylecarbs Oct 14, 2022
6fa941f
Wow, I made the tests pass
kylecarbs Oct 14, 2022
abff96b
Add replicas endpoint
kylecarbs Oct 14, 2022
d6ce216
Ensure close kills replica
kylecarbs Oct 14, 2022
c3786a5
Merge branch 'main' into replica
kylecarbs Oct 14, 2022
d7cc0ff
Update sql
kylecarbs Oct 14, 2022
9914840
Add database latency to high availability
kylecarbs Oct 15, 2022
c1aa3d2
Pipe TLS to DERP mesh
kylecarbs Oct 15, 2022
0cc4263
Fix DERP mesh with TLS
kylecarbs Oct 15, 2022
f9177e4
Add tests for TLS
kylecarbs Oct 15, 2022
ee59d88
Fix replica sync TLS
kylecarbs Oct 15, 2022
8641e58
Fix RootCA for replica meshing
kylecarbs Oct 15, 2022
3dfb796
Remove ID from replicasync
kylecarbs Oct 15, 2022
ec2c1f1
Fix getting certificates for meshing
kylecarbs Oct 15, 2022
590f0f8
Remove excessive locking
kylecarbs Oct 15, 2022
d8580d1
Fix linting
kylecarbs Oct 15, 2022
ae956fb
Store mesh key in the database
kylecarbs Oct 15, 2022
d703e2d
Fix replica key for tests
kylecarbs Oct 15, 2022
9bb021c
Fix types gen
kylecarbs Oct 15, 2022
76c9e2c
Fix unlocking unlocked
kylecarbs Oct 15, 2022
09e87b0
Fix race in tests
kylecarbs Oct 15, 2022
18c0464
Update enterprise/derpmesh/derpmesh.go
kylecarbs Oct 15, 2022
6f25b2d
Rename to syncReplicas
kylecarbs Oct 15, 2022
efb6ece
Merge branch 'replica' of github.com:coder/coder into replica
kylecarbs Oct 15, 2022
1e85039
Reuse http client
kylecarbs Oct 15, 2022
ae0aa5f
Delete old replicas on a CRON
kylecarbs Oct 15, 2022
332d435
Merge branch 'main' into replica
kylecarbs Oct 15, 2022
bd7fb13
Fix race condition in connection tests
kylecarbs Oct 15, 2022
bb5b347
Fix linting
kylecarbs Oct 15, 2022
76e0511
Fix nil type
kylecarbs Oct 15, 2022
1ff5f7d
Move pubsub to in-memory for twenty test
kylecarbs Oct 16, 2022
b732184
Add comment for configuration tweaking
kylecarbs Oct 16, 2022
38465ac
Fix leak with transport
kylecarbs Oct 16, 2022
72555e2
Fix close leak in derpmesh
kylecarbs Oct 16, 2022
e54072a
Fix race when creating server
kylecarbs Oct 16, 2022
27d5f40
Remove handler update
kylecarbs Oct 16, 2022
4d0b1d8
Skip test on Windows
kylecarbs Oct 16, 2022
129f5ba
Fix DERP mesh test
kylecarbs Oct 16, 2022
4e5d30e
Wrap HTTP handler replacement in mutex
kylecarbs Oct 16, 2022
0359a7e
Fix error message for relay
kylecarbs Oct 16, 2022
f364d1f
Fix API handler for normal tests
kylecarbs Oct 16, 2022
423a47e
Fix speedtest
kylecarbs Oct 16, 2022
c3a77fe
Fix replica resend
kylecarbs Oct 16, 2022
729f8a0
Fix derpmesh send
kylecarbs Oct 16, 2022
ae0bc5d
Ping async
kylecarbs Oct 16, 2022
d7d50db
Increase wait time of template version jobd
kylecarbs Oct 16, 2022
77d23dc
Fix race when closing replica sync
kylecarbs Oct 16, 2022
435bbbb
Add name to client
kylecarbs Oct 16, 2022
9b7c41a
Log the derpmap being used
kylecarbs Oct 17, 2022
9615402
Don't connect if DERP is empty
kylecarbs Oct 17, 2022
bcb97ac
Improve agent coordinator logging
kylecarbs Oct 17, 2022
e2f6a19
Fix lock in coordinator
kylecarbs Oct 17, 2022
c855c9b
Fix relay addr
kylecarbs Oct 17, 2022
a0e5cab
Fix race when updating durations
kylecarbs Oct 17, 2022
9878fc5
Fix client publish race
kylecarbs Oct 17, 2022
7a40bf8
Run pubsub loop in a queue
kylecarbs Oct 17, 2022
08b9681
Store agent nodes in order
kylecarbs Oct 17, 2022
79991a9
Fix coordinator locking
kylecarbs Oct 17, 2022
020171b
Check for closed pipe
kylecarbs Oct 17, 2022
6a57554
Merge branch 'main' into replica
kylecarbs Oct 17, 2022
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
Prev Previous commit
Next Next commit
Add flags for HA
  • Loading branch information
kylecarbs committed Oct 12, 2022
commit 585bc1dfc81996b9967475de7f4249c93b24aa46
5 changes: 5 additions & 0 deletions cli/config/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ func (r Root) Session() File {
return File(filepath.Join(string(r), "session"))
}

// ReplicaID is a unique identifier for the Coder server.
func (r Root) ReplicaID() File {
return File(filepath.Join(string(r), "replica_id"))
}

func (r Root) URL() File {
return File(filepath.Join(string(r), "url"))
}
Expand Down
15 changes: 15 additions & 0 deletions cli/deployment/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func Flags() *codersdk.DeploymentFlags {
Description: "Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.",
Default: []string{"stun.l.google.com:19302"},
},
DerpServerRelayAddress: &codersdk.StringFlag{
Name: "DERP Server Relay Address",
Flag: "derp-server-relay-address",
EnvVar: "CODER_DERP_SERVER_RELAY_ADDRESS",
Description: "An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.",
Enterprise: true,
},
DerpConfigURL: &codersdk.StringFlag{
Name: "DERP Config URL",
Flag: "derp-config-url",
Expand Down Expand Up @@ -123,6 +130,14 @@ func Flags() *codersdk.DeploymentFlags {
Description: "The bind address to serve pprof.",
Default: "127.0.0.1:6060",
},
HighAvailability: &codersdk.BoolFlag{
Name: "High Availability",
Flag: "high-availability",
EnvVar: "CODER_HIGH_AVAILABILITY",
Description: "Specifies whether high availability is enabled.",
Default: true,
Enterprise: true,
},
CacheDir: &codersdk.StringFlag{
Name: "Cache Directory",
Flag: "cache-dir",
Expand Down
2 changes: 1 addition & 1 deletion cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func Core() []*cobra.Command {
}

func AGPL() []*cobra.Command {
all := append(Core(), Server(deployment.Flags(), func(_ context.Context, o *coderd.Options) (*coderd.API, error) {
all := append(Core(), Server(deployment.Flags(), func(_ context.Context, _ config.Root, o *coderd.Options) (*coderd.API, error) {
return coderd.New(o), nil
}))
return all
Expand Down
4 changes: 2 additions & 2 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ import (
)

// nolint:gocyclo
func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *coderd.Options) (*coderd.API, error)) *cobra.Command {
func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, config.Root, *coderd.Options) (*coderd.API, error)) *cobra.Command {
root := &cobra.Command{
Use: "server",
Short: "Start a Coder server",
Expand Down Expand Up @@ -463,7 +463,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code
), dflags.PromAddress.Value, "prometheus")()
}

coderAPI, err := newAPI(ctx, options)
coderAPI, err := newAPI(ctx, config, options)
if err != nil {
return err
}
Expand Down
8 changes: 5 additions & 3 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type Options struct {
AutoImportTemplates []AutoImportTemplate

TailnetCoordinator tailnet.Coordinator
DERPServer *derp.Server
DERPMap *tailcfg.DERPMap

MetricsCacheRefreshInterval time.Duration
Expand Down Expand Up @@ -121,6 +122,9 @@ func New(options *Options) *API {
if options.TailnetCoordinator == nil {
options.TailnetCoordinator = tailnet.NewCoordinator()
}
if options.DERPServer == nil {
options.DERPServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger))
}
if options.Auditor == nil {
options.Auditor = audit.NewNop()
}
Expand Down Expand Up @@ -160,7 +164,6 @@ func New(options *Options) *API {
api.WorkspaceQuotaEnforcer.Store(&options.WorkspaceQuotaEnforcer)
api.workspaceAgentCache = wsconncache.New(api.dialWorkspaceAgentTailnet, 0)
api.TailnetCoordinator.Store(&options.TailnetCoordinator)
api.derpServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger))
oauthConfigs := &httpmw.OAuth2Configs{
Github: options.GithubOAuth2Config,
OIDC: options.OIDCConfig,
Expand Down Expand Up @@ -228,7 +231,7 @@ func New(options *Options) *API {
r.Route("/%40{user}/{workspace_and_agent}/apps/{workspaceapp}", apps)
r.Route("/@{user}/{workspace_and_agent}/apps/{workspaceapp}", apps)
r.Route("/derp", func(r chi.Router) {
r.Get("/", derphttp.Handler(api.derpServer).ServeHTTP)
r.Get("/", derphttp.Handler(api.DERPServer).ServeHTTP)
// This is used when UDP is blocked, and latency must be checked via HTTP(s).
r.Get("/latency-check", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -540,7 +543,6 @@ type API struct {
// RootHandler serves "/"
RootHandler chi.Router

derpServer *derp.Server
metricsCache *metricscache.Cache
siteHandler http.Handler
websocketWaitMutex sync.Mutex
Expand Down
17 changes: 12 additions & 5 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ type Options struct {
MetricsCacheRefreshInterval time.Duration
AgentStatsRefreshInterval time.Duration
DeploymentFlags *codersdk.DeploymentFlags

// Overriding the database is heavily discouraged.
// It should only be used in cases where multiple Coder
// test instances are running against the same database.
Database database.Store
Pubsub database.Pubsub
}

// New constructs a codersdk client connected to an in-memory API instance.
Expand Down Expand Up @@ -135,13 +141,14 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
close(options.AutobuildStats)
})
}

db, pubsub := dbtestutil.NewDB(t)
if options.Database == nil {
options.Database, options.Pubsub = dbtestutil.NewDB(t)
}

ctx, cancelFunc := context.WithCancel(context.Background())
lifecycleExecutor := executor.New(
ctx,
db,
options.Database,
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
options.AutobuildTicker,
).WithStatsChannel(options.AutobuildStats)
Expand Down Expand Up @@ -181,8 +188,8 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
AppHostname: options.AppHostname,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
CacheDir: t.TempDir(),
Database: db,
Pubsub: pubsub,
Database: options.Database,
Pubsub: options.Pubsub,

Auditor: options.Auditor,
AWSCertificates: options.AWSCertificates,
Expand Down
2 changes: 2 additions & 0 deletions codersdk/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type DeploymentFlags struct {
DerpServerRegionCode *StringFlag `json:"derp_server_region_code" typescript:",notnull"`
DerpServerRegionName *StringFlag `json:"derp_server_region_name" typescript:",notnull"`
DerpServerSTUNAddresses *StringArrayFlag `json:"derp_server_stun_address" typescript:",notnull"`
DerpServerRelayAddress *StringFlag `json:"derp_server_relay_address" typescript:",notnull"`
DerpConfigURL *StringFlag `json:"derp_config_url" typescript:",notnull"`
DerpConfigPath *StringFlag `json:"derp_config_path" typescript:",notnull"`
PromEnabled *BoolFlag `json:"prom_enabled" typescript:",notnull"`
Expand Down Expand Up @@ -59,6 +60,7 @@ type DeploymentFlags struct {
Verbose *BoolFlag `json:"verbose" typescript:",notnull"`
AuditLogging *BoolFlag `json:"audit_logging" typescript:",notnull"`
BrowserOnly *BoolFlag `json:"browser_only" typescript:",notnull"`
HighAvailability *BoolFlag `json:"high_availability" typescript:",notnull"`
SCIMAuthHeader *StringFlag `json:"scim_auth_header" typescript:",notnull"`
UserWorkspaceQuota *IntFlag `json:"user_workspace_quota" typescript:",notnull"`
}
Expand Down
22 changes: 22 additions & 0 deletions codersdk/replicas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package codersdk

import (
"time"

"github.com/google/uuid"
)

type Replica struct {
// ID is the unique identifier for the replica.
ID uuid.UUID `json:"id"`
// Hostname is the hostname of the replica.
Hostname string `json:"hostname"`
// CreatedAt is when the replica was first seen.
CreatedAt time.Time `json:"created_at"`
// Active determines whether the replica is online.
Active bool `json:"active"`
// RelayAddress is the accessible address to relay DERP connections.
RelayAddress string `json:"relay_address"`
// Error is the error.
Error string `json:"error"`
}
25 changes: 22 additions & 3 deletions enterprise/cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package cli
import (
"context"

"github.com/google/uuid"
"github.com/spf13/cobra"

"cdr.dev/slog"

"github.com/coder/coder/cli/config"
"github.com/coder/coder/cli/deployment"
"github.com/coder/coder/enterprise/coderd"

Expand All @@ -14,14 +18,29 @@ import (

func server() *cobra.Command {
dflags := deployment.Flags()
cmd := agpl.Server(dflags, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, error) {
cmd := agpl.Server(dflags, func(ctx context.Context, cfg config.Root, options *agplcoderd.Options) (*agplcoderd.API, error) {
replicaIDRaw, err := cfg.ReplicaID().Read()
if err != nil {
replicaIDRaw = uuid.NewString()
}
replicaID, err := uuid.Parse(replicaIDRaw)
if err != nil {
options.Logger.Warn(ctx, "failed to parse replica id", slog.Error(err), slog.F("replica_id", replicaIDRaw))
replicaID = uuid.New()
}
o := &coderd.Options{
AuditLogging: dflags.AuditLogging.Value,
BrowserOnly: dflags.BrowserOnly.Value,
SCIMAPIKey: []byte(dflags.SCIMAuthHeader.Value),
UserWorkspaceQuota: dflags.UserWorkspaceQuota.Value,
RBACEnabled: true,
Options: options,
RBAC: true,
HighAvailability: dflags.HighAvailability.Value,

ReplicaID: replicaID,
DERPServerRelayAddress: dflags.DerpServerRelayAddress.Value,
DERPServerRegionID: dflags.DerpServerRegionID.Value,

Options: options,
}
api, err := coderd.New(ctx, o)
if err != nil {
Expand Down
64 changes: 56 additions & 8 deletions enterprise/coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/cenkalti/backoff/v4"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"

"cdr.dev/slog"
"github.com/coder/coder/coderd"
Expand All @@ -24,6 +25,8 @@ import (
"github.com/coder/coder/enterprise/audit/backends"
"github.com/coder/coder/enterprise/coderd/license"
"github.com/coder/coder/enterprise/highavailability"
"github.com/coder/coder/enterprise/highavailability/derpmesh"
"github.com/coder/coder/enterprise/highavailability/replica"
"github.com/coder/coder/tailnet"
)

Expand All @@ -43,6 +46,7 @@ func New(ctx context.Context, options *Options) (*API, error) {
Options: options,
cancelEntitlementsLoop: cancelFunc,
}

oauthConfigs := &httpmw.OAuth2Configs{
Github: options.GithubOAuth2Config,
OIDC: options.OIDCConfig,
Expand Down Expand Up @@ -113,7 +117,27 @@ func New(ctx context.Context, options *Options) (*API, error) {
})
}

err := api.updateEntitlements(ctx)
// If high availability is disabled and multiple replicas appear, show an error.
// If high availability is enabled and the built-in DERP is but the DERP relay isn't set, show an error.
// We need to block meshing if high availability is disabled, because the meshing code would just work.
// SetAddresses([]string{})

api.AGPL.RootHandler.Route("/replicas", func(r chi.Router) {

})

var err error
api.replica, err = replica.New(ctx, options.Logger, options.Database, options.Pubsub, replica.Options{
ID: options.ReplicaID,
RelayAddress: options.DERPServerRelayAddress,
RegionID: int32(options.DERPServerRegionID),
})
if err != nil {
return nil, xerrors.Errorf("initialize replica: %w", err)
}
api.derpMesh = derpmesh.New(options.Logger, api.DERPServer)

err = api.updateEntitlements(ctx)
if err != nil {
return nil, xerrors.Errorf("update entitlements: %w", err)
}
Expand All @@ -125,12 +149,18 @@ func New(ctx context.Context, options *Options) (*API, error) {
type Options struct {
*coderd.Options

RBACEnabled bool
RBAC bool
AuditLogging bool
// Whether to block non-browser connections.
BrowserOnly bool
SCIMAPIKey []byte
UserWorkspaceQuota int
HighAvailability bool

// Used for high availability.
DERPServerRelayAddress string
DERPServerRegionID int
ReplicaID uuid.UUID

EntitlementsUpdateInterval time.Duration
Keys map[string]ed25519.PublicKey
Expand All @@ -140,13 +170,20 @@ type API struct {
AGPL *coderd.API
*Options

// Detects multiple Coder replicas running at the same time.
replica *replica.Server
// Meshes DERP connections from multiple replicas.
derpMesh *derpmesh.Mesh

cancelEntitlementsLoop func()
entitlementsMu sync.RWMutex
entitlements codersdk.Entitlements
}

func (api *API) Close() error {
api.cancelEntitlementsLoop()
_ = api.replica.Close()
_ = api.derpMesh.Close()
return api.AGPL.Close()
}

Expand All @@ -155,11 +192,12 @@ func (api *API) updateEntitlements(ctx context.Context) error {
defer api.entitlementsMu.Unlock()

entitlements, err := license.Entitlements(ctx, api.Database, api.Logger, api.Keys, map[string]bool{
codersdk.FeatureAuditLog: api.AuditLogging,
codersdk.FeatureBrowserOnly: api.BrowserOnly,
codersdk.FeatureSCIM: len(api.SCIMAPIKey) != 0,
codersdk.FeatureWorkspaceQuota: api.UserWorkspaceQuota != 0,
codersdk.FeatureTemplateRBAC: api.RBACEnabled,
codersdk.FeatureAuditLog: api.AuditLogging,
codersdk.FeatureBrowserOnly: api.BrowserOnly,
codersdk.FeatureSCIM: len(api.SCIMAPIKey) != 0,
codersdk.FeatureWorkspaceQuota: api.UserWorkspaceQuota != 0,
codersdk.FeatureHighAvailability: api.HighAvailability,
codersdk.FeatureTemplateRBAC: api.RBAC,
})
if err != nil {
return err
Expand Down Expand Up @@ -210,13 +248,23 @@ func (api *API) updateEntitlements(ctx context.Context) error {
if enabled {
haCoordinator, err := highavailability.NewCoordinator(api.Logger, api.Pubsub)
if err != nil {
api.Logger.Error(ctx, "unable to setup HA tailnet coordinator", slog.Error(err))
api.Logger.Error(ctx, "unable to set up high availability coordinator", slog.Error(err))
// If we try to setup the HA coordinator and it fails, nothing
// is actually changing.
changed = false
} else {
coordinator = haCoordinator
}

api.replica.SetCallback(func() {
addresses := make([]string, 0)
for _, replica := range api.replica.Regional() {
addresses = append(addresses, replica.RelayAddress)
}
api.derpMesh.SetAddresses(addresses)
})
} else {
api.derpMesh.SetAddresses([]string{})
}

// Recheck changed in case the HA coordinator failed to set up.
Expand Down
7 changes: 6 additions & 1 deletion enterprise/coderd/coderdenttest/coderdenttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -62,10 +63,14 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, io.Closer, *c
}
srv, cancelFunc, oop := coderdtest.NewOptions(t, options.Options)
coderAPI, err := coderd.New(context.Background(), &coderd.Options{
RBACEnabled: true,
RBAC: true,
AuditLogging: options.AuditLogging,
BrowserOnly: options.BrowserOnly,
SCIMAPIKey: options.SCIMAPIKey,
DERPServerRelayAddress: oop.AccessURL.String(),
DERPServerRegionID: 1,
HighAvailability: true,
ReplicaID: uuid.New(),
UserWorkspaceQuota: options.UserWorkspaceQuota,
Options: oop,
EntitlementsUpdateInterval: options.EntitlementsUpdateInterval,
Expand Down
Loading