Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions coderd/audit/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package audit

import (
"context"
"os"

"github.com/coder/coder/v2/coderd/database"
)

// DevModeEnabled returns true if development mode for audit logs is enabled.
// Set CODER_DEV_AUDIT_LOGS=true to enable audit functionality without a license.
// This is intended for testing and evaluation purposes only.
func DevModeEnabled() bool {
return os.Getenv("CODER_DEV_AUDIT_LOGS") == "true"
}

// DevAuditor implements the Auditor interface for development/testing purposes.
// It provides the same functionality as MockAuditor, storing audit logs in memory.
type DevAuditor struct {
*MockAuditor
}

// NewDevAuditor creates a new DevAuditor for development mode.
// This auditor stores logs in memory and does not require a license.
func NewDevAuditor() *DevAuditor {
return &DevAuditor{
MockAuditor: NewMock(),
}
}

func (a *DevAuditor) Export(ctx context.Context, alog database.AuditLog) error {
return a.MockAuditor.Export(ctx, alog)
}

var _ Auditor = (*DevAuditor)(nil)
29 changes: 29 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import (
"github.com/coder/coder/v2/coderd/webpush"
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/coderd/workspacequota"
"github.com/coder/coder/v2/coderd/workspacestats"
"github.com/coder/coder/v2/coderd/wsbuilder"
"github.com/coder/coder/v2/codersdk"
Expand Down Expand Up @@ -672,6 +673,34 @@ func New(options *Options) *API {
api.PrebuildsClaimer.Store(&prebuilds.DefaultClaimer)
api.PrebuildsReconciler.Store(&prebuilds.DefaultReconciler)
}

// Enable dev mode for audit logs.
// Set CODER_DEV_AUDIT_LOGS=true to enable audit logging without a license.
if audit.DevModeEnabled() {
options.Logger.Warn(ctx, "audit logs development mode enabled - this is for testing only")
devAuditor := audit.Auditor(audit.NewDevAuditor())
api.Auditor.Store(&devAuditor)
}

// Enable dev mode for connection logs.
// Set CODER_DEV_CONNECTION_LOGS=true to enable connection logging without a license.
if connectionlog.DevModeEnabled() {
options.Logger.Warn(ctx, "connection logs development mode enabled - this is for testing only")
devConnectionLogger := connectionlog.ConnectionLogger(connectionlog.NewDevConnectionLogger())
api.ConnectionLogger.Store(&devConnectionLogger)
}

// Enable dev mode for quotas (Template RBAC).
// Set CODER_DEV_QUOTAS=true to enable quota functionality without a license.
if workspacequota.DevModeEnabled() {
options.Logger.Warn(ctx, "quotas development mode enabled - this is for testing only")
devQuotaCommitter := proto.QuotaCommitter(workspacequota.NewDevCommitter(
options.Logger.Named("quota_committer"),
options.Database,
))
api.QuotaCommitter.Store(&devQuotaCommitter)
}

buildInfo := codersdk.BuildInfoResponse{
ExternalURL: buildinfo.ExternalURL(),
Version: buildinfo.Version(),
Expand Down
35 changes: 35 additions & 0 deletions coderd/connectionlog/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package connectionlog

import (
"context"
"os"
"sync"

"github.com/coder/coder/v2/coderd/database"
)

// DevModeEnabled returns true if development mode for connection logs is enabled.
// Set CODER_DEV_CONNECTION_LOGS=true to enable connection logging without a license.
// This is intended for testing and evaluation purposes only.
func DevModeEnabled() bool {
return os.Getenv("CODER_DEV_CONNECTION_LOGS") == "true"
}

// DevConnectionLogger implements the ConnectionLogger interface for development/testing.
// It stores connection logs in memory and does not require a license.
type DevConnectionLogger struct {
*FakeConnectionLogger
}

// NewDevConnectionLogger creates a new DevConnectionLogger for development mode.
func NewDevConnectionLogger() *DevConnectionLogger {
return &DevConnectionLogger{
FakeConnectionLogger: NewFake(),
}
}

func (l *DevConnectionLogger) Upsert(ctx context.Context, clog database.UpsertConnectionLogParams) error {
return l.FakeConnectionLogger.Upsert(ctx, clog)
}

var _ ConnectionLogger = (*DevConnectionLogger)(nil)
126 changes: 126 additions & 0 deletions coderd/workspacequota/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package workspacequota

import (
"context"
"database/sql"
"errors"
"os"

"github.com/google/uuid"

"cdr.dev/slog/v3"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/provisionerd/proto"
)

// DevModeEnabled returns true if development mode for quotas is enabled.
// Set CODER_DEV_QUOTAS=true to enable quota functionality without a license.
// This is intended for testing and evaluation purposes only.
func DevModeEnabled() bool {
return os.Getenv("CODER_DEV_QUOTAS") == "true"
}

// DevCommitter implements the proto.QuotaCommitter interface for development/testing.
// It provides the same quota enforcement functionality as the enterprise committer
// without requiring a license.
type DevCommitter struct {
Log slog.Logger
Database database.Store
}

// NewDevCommitter creates a new DevCommitter for development mode.
func NewDevCommitter(log slog.Logger, db database.Store) *DevCommitter {
return &DevCommitter{
Log: log,
Database: db,
}
}

func (c *DevCommitter) CommitQuota(
ctx context.Context, request *proto.CommitQuotaRequest,
) (*proto.CommitQuotaResponse, error) {
jobID, err := uuid.Parse(request.JobId)
if err != nil {
return nil, err
}

nextBuild, err := c.Database.GetWorkspaceBuildByJobID(ctx, jobID)
if err != nil {
return nil, err
}

workspace, err := c.Database.GetWorkspaceByID(ctx, nextBuild.WorkspaceID)
if err != nil {
return nil, err
}

var (
consumed int64
budget int64
permit bool
)
err = c.Database.InTx(func(s database.Store) error {
var err error
consumed, err = s.GetQuotaConsumedForUser(ctx, database.GetQuotaConsumedForUserParams{
OwnerID: workspace.OwnerID,
OrganizationID: workspace.OrganizationID,
})
if err != nil {
return err
}

budget, err = s.GetQuotaAllowanceForUser(ctx, database.GetQuotaAllowanceForUserParams{
UserID: workspace.OwnerID,
OrganizationID: workspace.OrganizationID,
})
if err != nil {
return err
}

// If the new build will reduce overall quota consumption, then we
// allow it even if the user is over quota.
netIncrease := true
prevBuild, err := s.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{
WorkspaceID: workspace.ID,
BuildNumber: nextBuild.BuildNumber - 1,
})
if err == nil {
netIncrease = request.DailyCost >= prevBuild.DailyCost
c.Log.Debug(
ctx, "previous build cost",
slog.F("prev_cost", prevBuild.DailyCost),
slog.F("next_cost", request.DailyCost),
slog.F("net_increase", netIncrease),
)
} else if !errors.Is(err, sql.ErrNoRows) {
return err
}

newConsumed := int64(request.DailyCost) + consumed
if newConsumed > budget && netIncrease {
c.Log.Debug(
ctx, "over quota, rejecting",
slog.F("prev_consumed", consumed),
slog.F("next_consumed", newConsumed),
slog.F("budget", budget),
)
permit = false
return nil
}

permit = true
return nil
}, nil)
if err != nil {
return nil, err
}

return &proto.CommitQuotaResponse{
Ok: permit,
CreditsConsumed: consumed,
Budget: budget,
DailyCostIncrease: request.DailyCost,
}, nil
}

var _ proto.QuotaCommitter = (*DevCommitter)(nil)
67 changes: 67 additions & 0 deletions deploy/values-dev-premium.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Helm values for deploying Coder with all premium features in development mode
# Use this for testing premium features without a license
#
# Usage:
# helm install coder coder-v2/coder \
# --namespace coder \
# --values values-dev-premium.yaml

coder:
image:
tag: "latest"
pullPolicy: Always

# Enable all premium features in development mode
env:
# Prebuilds - Pre-provision workspaces for faster startup
- name: CODER_DEV_PREBUILDS
value: "true"

# Audit Logs - Track all user operations and changes
- name: CODER_DEV_AUDIT_LOGS
value: "true"

# Connection Logs - Monitor workspace connections (SSH, IDE, apps)
- name: CODER_DEV_CONNECTION_LOGS
value: "true"

# Quotas - Credit-based workspace budgeting and spend control
- name: CODER_DEV_QUOTAS
value: "true"

# Disable telemetry for dev/testing
- name: CODER_TELEMETRY_ENABLE
value: "false"

# High Availability - Run multiple replicas for HA testing
# HA is automatically enabled when multiple instances connect to same DB
replicaCount: 2

# Resources for HA deployment
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"

# PostgreSQL configuration for HA and quota testing
postgres:
enabled: true
primary:
persistence:
enabled: true
size: 10Gi

# Enable replication for HA testing (optional)
replication:
enabled: false
slaveReplicas: 1

# Optional: Enable ingress for external access
# ingress:
# enable: true
# host: "coder-dev.example.com"
# tls:
# enable: true