Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 386b48a823 | |||
| 6f6fc1fc66 | |||
| 7fce42be14 | |||
| 8701fa4784 | |||
| 1c2dc95f0e | |||
| 70baa088c8 | |||
| a7c35f4a81 | |||
| a98781b603 | |||
| 53a659fadd | |||
| 5fba46c628 | |||
| 5e75a51a5a |
@@ -506,6 +506,7 @@ type DeploymentValues struct {
|
||||
Prebuilds PrebuildsConfig `json:"workspace_prebuilds,omitempty" typescript:",notnull"`
|
||||
HideAITasks serpent.Bool `json:"hide_ai_tasks,omitempty" typescript:",notnull"`
|
||||
AI AIConfig `json:"ai,omitempty"`
|
||||
LicenseFile serpent.String `json:"license_file,omitempty" typescript:",notnull"`
|
||||
|
||||
Config serpent.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"`
|
||||
WriteConfig serpent.Bool `json:"write_config,omitempty" typescript:",notnull"`
|
||||
@@ -3373,6 +3374,15 @@ Write out the current server config as YAML to stdout.`,
|
||||
// used externally.
|
||||
Hidden: true,
|
||||
},
|
||||
{
|
||||
Name: "License File",
|
||||
Description: "Path to a license file to automatically import on server startup. The license will only be imported if no licenses exist yet. This is useful for automated deployments.",
|
||||
Flag: "license-file",
|
||||
Env: "CODER_LICENSE_FILE",
|
||||
Value: &c.LicenseFile,
|
||||
Default: "",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationEnterpriseKey, "true"),
|
||||
},
|
||||
}
|
||||
|
||||
return opts
|
||||
|
||||
@@ -131,6 +131,17 @@ func (r *RootCmd) Server(_ func()) *serpent.Command {
|
||||
}
|
||||
closers.Add(api)
|
||||
|
||||
// Import license from file if configured and no licenses exist yet.
|
||||
// This is useful for automated deployments where you want to provision
|
||||
// a license without manual intervention.
|
||||
if licenseFile := options.DeploymentValues.LicenseFile.Value(); licenseFile != "" {
|
||||
err = coderd.ImportLicenseFromFile(ctx, options.Database, o.LicenseKeys, licenseFile, api.AGPL.DeploymentID, options.Logger)
|
||||
if err != nil {
|
||||
_ = closers.Close()
|
||||
return nil, nil, xerrors.Errorf("import license from file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the enterprise usage publisher routine. This won't do anything
|
||||
// unless the deployment is licensed and one of the licenses has usage
|
||||
// publishing enabled.
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/coderd"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/license"
|
||||
)
|
||||
|
||||
func TestImportLicenseFromFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Create a temporary license file
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import the license
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 1)
|
||||
require.Equal(t, licenseJWT, licenses[0].JWT)
|
||||
})
|
||||
|
||||
t.Run("Idempotent", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Create a temporary license file
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import the license once
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to import again - should skip
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify only one license exists
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 1)
|
||||
})
|
||||
|
||||
t.Run("EmptyFilePath", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Import with empty file path
|
||||
err := coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, "", deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify no license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 0)
|
||||
})
|
||||
|
||||
t.Run("FileDoesNotExist", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Import from non-existent file
|
||||
err := coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, "/nonexistent/license.jwt", deploymentID, logger)
|
||||
require.NoError(t, err) // Should not error, just skip
|
||||
|
||||
// Verify no license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 0)
|
||||
})
|
||||
|
||||
t.Run("EmptyFile", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Create an empty license file
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(""), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import from empty file
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err) // Should not error, just skip
|
||||
|
||||
// Verify no license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 0)
|
||||
})
|
||||
|
||||
t.Run("WhitespaceOnlyFile", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Create a file with only whitespace
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(" \n\t \n"), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import from whitespace-only file
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err) // Should not error, just skip
|
||||
|
||||
// Verify no license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 0)
|
||||
})
|
||||
|
||||
t.Run("InvalidLicenseJWT", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Create a file with invalid JWT
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte("invalid-jwt-token"), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import should fail
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "parse license")
|
||||
})
|
||||
|
||||
t.Run("DeploymentIDRestriction", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
|
||||
// Generate license for a specific deployment ID
|
||||
restrictedDeploymentID := uuid.NewString()
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
DeploymentIDs: []string{restrictedDeploymentID},
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to import with a different deployment ID
|
||||
differentDeploymentID := uuid.NewString()
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, differentDeploymentID, logger)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "license is locked to deployments")
|
||||
require.Contains(t, err.Error(), restrictedDeploymentID)
|
||||
require.Contains(t, err.Error(), differentDeploymentID)
|
||||
})
|
||||
|
||||
t.Run("DeploymentIDMatch", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
|
||||
// Generate license for a specific deployment ID
|
||||
deploymentID := uuid.NewString()
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
DeploymentIDs: []string{deploymentID},
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import with matching deployment ID should succeed
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 1)
|
||||
})
|
||||
|
||||
t.Run("ExpiredLicense", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Generate an expired license
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
ExpiresAt: time.Now().Add(-24 * time.Hour), // Expired 1 day ago
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err := os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import should fail for expired licenses
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "parse license")
|
||||
|
||||
// Verify no license was imported
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 0)
|
||||
})
|
||||
|
||||
t.Run("LicenseWithUUID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Generate license (UUID is auto-generated in the license claims)
|
||||
licenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
|
||||
// Parse the license to get its UUID
|
||||
claims, err := license.ParseClaims(licenseJWT, coderdenttest.Keys)
|
||||
require.NoError(t, err)
|
||||
expectedUUID, err := uuid.Parse(claims.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err = os.WriteFile(licenseFile, []byte(licenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import the license
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the license UUID matches
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 1)
|
||||
require.Equal(t, expectedUUID, licenses[0].UUID)
|
||||
})
|
||||
|
||||
t.Run("SkipsWhenLicenseExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
ctx := context.Background()
|
||||
logger := slogtest.Make(t, nil)
|
||||
deploymentID := uuid.NewString()
|
||||
|
||||
// Insert a license directly into the database
|
||||
existingLicense := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAuditLog: 1,
|
||||
},
|
||||
})
|
||||
_, err := db.InsertLicense(ctx, database.InsertLicenseParams{
|
||||
JWT: existingLicense,
|
||||
Exp: time.Now().Add(24 * time.Hour),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a different license file
|
||||
newLicenseJWT := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureMultipleOrganizations: 1,
|
||||
},
|
||||
})
|
||||
tmpDir := t.TempDir()
|
||||
licenseFile := filepath.Join(tmpDir, "license.jwt")
|
||||
err = os.WriteFile(licenseFile, []byte(newLicenseJWT), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to import - should skip because license already exists
|
||||
err = coderd.ImportLicenseFromFile(ctx, db, coderdenttest.Keys, licenseFile, deploymentID, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify only the original license exists (not the new one)
|
||||
licenses, err := db.GetLicenses(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, licenses, 1)
|
||||
require.Equal(t, existingLicense, licenses[0].JWT)
|
||||
})
|
||||
}
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -24,6 +26,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd"
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
@@ -381,3 +384,91 @@ func decodeClaims(l database.License) (jwt.MapClaims, error) {
|
||||
err = d.Decode(&c)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// ImportLicenseFromFile reads a license from a file and adds it to the database.
|
||||
// This should only be called during server startup, and only if no licenses exist yet.
|
||||
// It returns nil if the file doesn't exist or if licenses already exist.
|
||||
func ImportLicenseFromFile(ctx context.Context, db database.Store, licenseKeys map[string]ed25519.PublicKey, filePath string, deploymentID string, logger slog.Logger) error {
|
||||
if filePath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if any licenses already exist
|
||||
// Use a subject with specific license permissions for this system startup operation
|
||||
// nolint:gocritic // This is a system operation during startup before any users exist
|
||||
systemCtx := dbauthz.As(ctx, rbac.Subject{
|
||||
ID: uuid.Nil.String(),
|
||||
Roles: rbac.Roles([]rbac.Role{
|
||||
{
|
||||
Identifier: rbac.RoleIdentifier{Name: "license-import"},
|
||||
DisplayName: "License Import",
|
||||
Site: rbac.Permissions(map[string][]policy.Action{
|
||||
rbac.ResourceLicense.Type: {policy.ActionCreate, policy.ActionRead},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
Scope: rbac.ScopeAll,
|
||||
})
|
||||
licenses, err := db.GetLicenses(systemCtx)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return xerrors.Errorf("check existing licenses: %w", err)
|
||||
}
|
||||
if len(licenses) > 0 {
|
||||
logger.Debug(ctx, "licenses already exist, skipping file import", slog.F("count", len(licenses)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read the license file
|
||||
licenseData, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
logger.Debug(ctx, "license file does not exist, skipping import", slog.F("path", filePath))
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("read license file: %w", err)
|
||||
}
|
||||
|
||||
licenseStr := strings.TrimSpace(string(licenseData))
|
||||
if licenseStr == "" {
|
||||
logger.Warn(ctx, "license file is empty, skipping import", slog.F("path", filePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse and validate the license
|
||||
claims, err := license.ParseClaimsIgnoreNbf(licenseStr, licenseKeys)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parse license: %w", err)
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(claims.ID)
|
||||
if err != nil {
|
||||
// If no uuid is in the license, we generate a random uuid.
|
||||
id = uuid.New()
|
||||
}
|
||||
|
||||
// Check deployment ID restriction
|
||||
if len(claims.DeploymentIDs) > 0 && !slices.Contains(claims.DeploymentIDs, deploymentID) {
|
||||
return xerrors.Errorf("license is locked to deployments %q but this deployment is %q", claims.DeploymentIDs, deploymentID)
|
||||
}
|
||||
|
||||
// Insert the license into the database
|
||||
// Use the same system context with specific license permissions
|
||||
// nolint:gocritic // This is a system operation during startup before any users exist
|
||||
dl, err := db.InsertLicense(systemCtx, database.InsertLicenseParams{
|
||||
UploadedAt: dbtime.Now(),
|
||||
JWT: licenseStr,
|
||||
Exp: claims.ExpiresAt.Time,
|
||||
UUID: id,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert license: %w", err)
|
||||
}
|
||||
|
||||
logger.Info(ctx, "successfully imported license from file",
|
||||
slog.F("path", filePath),
|
||||
slog.F("license_id", dl.ID),
|
||||
slog.F("license_uuid_id", dl.UUID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@ env:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.provisionerDaemon.pskSecretName | quote }}
|
||||
key: psk
|
||||
{{- end }}
|
||||
{{- if .Values.coder.license.secretName }}
|
||||
- name: CODER_LICENSE_FILE
|
||||
value: "{{ .Values.coder.license.mountPath }}/{{ .Values.coder.license.secretKey }}"
|
||||
{{- end }}
|
||||
# Set the default access URL so a `helm apply` works by default.
|
||||
# See: https://github.com/coder/coder/issues/5024
|
||||
|
||||
@@ -251,6 +251,19 @@ coder:
|
||||
# exec:
|
||||
# command: ["/bin/sh","-c","echo preStart"]
|
||||
|
||||
# coder.license -- License configuration for automated license import.
|
||||
# The license will only be imported on first startup when no licenses exist yet.
|
||||
license:
|
||||
# coder.license.secretName -- Name of a Kubernetes secret containing the license.
|
||||
# The secret must be in the same namespace and contain a key with the license JWT.
|
||||
secretName: ""
|
||||
# coder.license.secretKey -- Key in the secret that contains the license JWT.
|
||||
# Defaults to "license.jwt" if not specified.
|
||||
secretKey: "license.jwt"
|
||||
# coder.license.mountPath -- Path where the license will be mounted inside the container.
|
||||
# This path will be passed to CODER_LICENSE_FILE environment variable.
|
||||
mountPath: "/tmp/coder-license"
|
||||
|
||||
# coder.resources -- The resources to request for Coder. The below values are
|
||||
# defaults and can be overridden.
|
||||
resources:
|
||||
|
||||
@@ -104,6 +104,13 @@ Coder volume definitions.
|
||||
secret:
|
||||
secretName: {{ $secret.name | quote }}
|
||||
{{ end -}}
|
||||
{{- with .Values.coder.license }}
|
||||
{{- if .secretName }}
|
||||
- name: "license"
|
||||
secret:
|
||||
secretName: {{ .secretName | quote }}
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
{{ if gt (len .Values.coder.volumes) 0 -}}
|
||||
{{ toYaml .Values.coder.volumes }}
|
||||
{{ end -}}
|
||||
@@ -138,6 +145,13 @@ Coder volume mounts.
|
||||
subPath: {{ $secret.key | quote }}
|
||||
readOnly: true
|
||||
{{ end -}}
|
||||
{{- with .Values.coder.license }}
|
||||
{{- if .secretName }}
|
||||
- name: "license"
|
||||
mountPath: {{ .mountPath | quote }}
|
||||
readOnly: true
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
{{ if gt (len .Values.coder.volumeMounts) 0 -}}
|
||||
{{ toYaml .Values.coder.volumeMounts }}
|
||||
{{ end -}}
|
||||
|
||||
Generated
+1
@@ -1786,6 +1786,7 @@ export interface DeploymentValues {
|
||||
readonly workspace_prebuilds?: PrebuildsConfig;
|
||||
readonly hide_ai_tasks?: boolean;
|
||||
readonly ai?: AIConfig;
|
||||
readonly license_file?: string;
|
||||
readonly config?: string;
|
||||
readonly write_config?: boolean;
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user