mirror of https://github.com/coder/coder.git
205 lines
6.4 KiB
Go
205 lines
6.4 KiB
Go
package coderdenttest
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coder/coder/v2/coderd/database/dbmem"
|
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
|
|
|
"github.com/golang-jwt/jwt/v4"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/enterprise/coderd"
|
|
"github.com/coder/coder/v2/enterprise/coderd/license"
|
|
"github.com/coder/coder/v2/enterprise/dbcrypt"
|
|
)
|
|
|
|
const (
|
|
testKeyID = "enterprise-test"
|
|
)
|
|
|
|
var (
|
|
testPrivateKey ed25519.PrivateKey
|
|
testPublicKey ed25519.PublicKey
|
|
|
|
Keys = map[string]ed25519.PublicKey{}
|
|
)
|
|
|
|
func init() {
|
|
var err error
|
|
testPublicKey, testPrivateKey, err = ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
Keys[testKeyID] = testPublicKey
|
|
}
|
|
|
|
type Options struct {
|
|
*coderdtest.Options
|
|
AuditLogging bool
|
|
BrowserOnly bool
|
|
EntitlementsUpdateInterval time.Duration
|
|
SCIMAPIKey []byte
|
|
UserWorkspaceQuota int
|
|
ProxyHealthInterval time.Duration
|
|
LicenseOptions *LicenseOptions
|
|
NoDefaultQuietHoursSchedule bool
|
|
DontAddLicense bool
|
|
DontAddFirstUser bool
|
|
ReplicaSyncUpdateInterval time.Duration
|
|
ExternalTokenEncryption []dbcrypt.Cipher
|
|
ProvisionerDaemonPSK string
|
|
}
|
|
|
|
// New constructs a codersdk client connected to an in-memory Enterprise API instance.
|
|
func New(t *testing.T, options *Options) (*codersdk.Client, codersdk.CreateFirstUserResponse) {
|
|
client, _, _, user := NewWithAPI(t, options)
|
|
return client, user
|
|
}
|
|
|
|
func NewWithAPI(t *testing.T, options *Options) (
|
|
*codersdk.Client, io.Closer, *coderd.API, codersdk.CreateFirstUserResponse,
|
|
) {
|
|
t.Helper()
|
|
|
|
if options == nil {
|
|
options = &Options{}
|
|
}
|
|
if options.Options == nil {
|
|
options.Options = &coderdtest.Options{}
|
|
}
|
|
require.False(t, options.DontAddFirstUser && !options.DontAddLicense, "DontAddFirstUser requires DontAddLicense")
|
|
setHandler, cancelFunc, serverURL, oop := coderdtest.NewOptions(t, options.Options)
|
|
if !options.NoDefaultQuietHoursSchedule && oop.DeploymentValues.UserQuietHoursSchedule.DefaultSchedule.Value() == "" {
|
|
err := oop.DeploymentValues.UserQuietHoursSchedule.DefaultSchedule.Set("0 0 * * *")
|
|
require.NoError(t, err)
|
|
}
|
|
coderAPI, err := coderd.New(context.Background(), &coderd.Options{
|
|
RBAC: true,
|
|
AuditLogging: options.AuditLogging,
|
|
BrowserOnly: options.BrowserOnly,
|
|
SCIMAPIKey: options.SCIMAPIKey,
|
|
DERPServerRelayAddress: oop.AccessURL.String(),
|
|
DERPServerRegionID: oop.BaseDERPMap.RegionIDs()[0],
|
|
ReplicaSyncUpdateInterval: options.ReplicaSyncUpdateInterval,
|
|
Options: oop,
|
|
EntitlementsUpdateInterval: options.EntitlementsUpdateInterval,
|
|
LicenseKeys: Keys,
|
|
ProxyHealthInterval: options.ProxyHealthInterval,
|
|
DefaultQuietHoursSchedule: oop.DeploymentValues.UserQuietHoursSchedule.DefaultSchedule.Value(),
|
|
ProvisionerDaemonPSK: options.ProvisionerDaemonPSK,
|
|
ExternalTokenEncryption: options.ExternalTokenEncryption,
|
|
})
|
|
require.NoError(t, err)
|
|
setHandler(coderAPI.AGPL.RootHandler)
|
|
var provisionerCloser io.Closer = nopcloser{}
|
|
if options.IncludeProvisionerDaemon {
|
|
provisionerCloser = coderdtest.NewProvisionerDaemon(t, coderAPI.AGPL)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
cancelFunc()
|
|
_ = provisionerCloser.Close()
|
|
_ = coderAPI.Close()
|
|
})
|
|
client := codersdk.New(serverURL)
|
|
client.HTTPClient = &http.Client{
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
//nolint:gosec
|
|
InsecureSkipVerify: true,
|
|
},
|
|
},
|
|
}
|
|
var user codersdk.CreateFirstUserResponse
|
|
if !options.DontAddFirstUser {
|
|
user = coderdtest.CreateFirstUser(t, client)
|
|
if !options.DontAddLicense {
|
|
lo := LicenseOptions{}
|
|
if options.LicenseOptions != nil {
|
|
lo = *options.LicenseOptions
|
|
// The pgCoord is not supported by the fake DB & in-memory Pubsub. It only works on a real postgres.
|
|
if lo.AllFeatures || (lo.Features != nil && lo.Features[codersdk.FeatureHighAvailability] != 0) {
|
|
// we check for the in-memory test types so that the real types don't have to exported
|
|
_, ok := coderAPI.Pubsub.(*pubsub.MemoryPubsub)
|
|
require.False(t, ok, "FeatureHighAvailability is incompatible with MemoryPubsub")
|
|
_, ok = coderAPI.Database.(*dbmem.FakeQuerier)
|
|
require.False(t, ok, "FeatureHighAvailability is incompatible with dbmem")
|
|
}
|
|
}
|
|
_ = AddLicense(t, client, lo)
|
|
}
|
|
}
|
|
return client, provisionerCloser, coderAPI, user
|
|
}
|
|
|
|
type LicenseOptions struct {
|
|
AccountType string
|
|
AccountID string
|
|
Trial bool
|
|
AllFeatures bool
|
|
GraceAt time.Time
|
|
ExpiresAt time.Time
|
|
Features license.Features
|
|
}
|
|
|
|
// AddFullLicense generates a license with all features enabled.
|
|
func AddFullLicense(t *testing.T, client *codersdk.Client) codersdk.License {
|
|
return AddLicense(t, client, LicenseOptions{AllFeatures: true})
|
|
}
|
|
|
|
// AddLicense generates a new license with the options provided and inserts it.
|
|
func AddLicense(t *testing.T, client *codersdk.Client, options LicenseOptions) codersdk.License {
|
|
l, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{
|
|
License: GenerateLicense(t, options),
|
|
})
|
|
require.NoError(t, err)
|
|
return l
|
|
}
|
|
|
|
// GenerateLicense returns a signed JWT using the test key.
|
|
func GenerateLicense(t *testing.T, options LicenseOptions) string {
|
|
if options.ExpiresAt.IsZero() {
|
|
options.ExpiresAt = time.Now().Add(time.Hour)
|
|
}
|
|
if options.GraceAt.IsZero() {
|
|
options.GraceAt = time.Now().Add(time.Hour)
|
|
}
|
|
|
|
c := &license.Claims{
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ID: uuid.NewString(),
|
|
Issuer: "test@testing.test",
|
|
ExpiresAt: jwt.NewNumericDate(options.ExpiresAt),
|
|
NotBefore: jwt.NewNumericDate(time.Now().Add(-time.Minute)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now().Add(-time.Minute)),
|
|
},
|
|
LicenseExpires: jwt.NewNumericDate(options.GraceAt),
|
|
AccountType: options.AccountType,
|
|
AccountID: options.AccountID,
|
|
Trial: options.Trial,
|
|
Version: license.CurrentVersion,
|
|
AllFeatures: options.AllFeatures,
|
|
Features: options.Features,
|
|
}
|
|
tok := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c)
|
|
tok.Header[license.HeaderKeyID] = testKeyID
|
|
signedTok, err := tok.SignedString(testPrivateKey)
|
|
require.NoError(t, err)
|
|
return signedTok
|
|
}
|
|
|
|
type nopcloser struct{}
|
|
|
|
func (nopcloser) Close() error { return nil }
|