Skip to content

Commit 76de0c7

Browse files
committed
feat: unexpose coderdtest.NewWithAPI
1 parent 9c8079b commit 76de0c7

13 files changed

+307
-203
lines changed

coderd/coderd.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,11 @@ func New(options *Options) *API {
282282

283283
r.Post("/authorization", api.checkPermissions)
284284

285-
r.Post("/keys", api.postAPIKey)
285+
r.Route("/keys", func(r chi.Router) {
286+
r.Post("/", api.postAPIKey)
287+
r.Get("/{keyid}", api.apiKey)
288+
})
289+
286290
r.Route("/organizations", func(r chi.Router) {
287291
r.Get("/", api.organizationsByUser)
288292
r.Get("/{organizationname}", api.organizationByUserAndName)

coderd/coderd_test.go

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ package coderd_test
22

33
import (
44
"context"
5+
"crypto/x509"
6+
"database/sql"
57
"io"
8+
"net"
69
"net/http"
10+
"net/http/httptest"
11+
"net/url"
12+
"os"
713
"strconv"
814
"strings"
915
"testing"
@@ -14,10 +20,23 @@ import (
1420
"github.com/stretchr/testify/require"
1521
"go.uber.org/goleak"
1622
"golang.org/x/xerrors"
23+
"google.golang.org/api/idtoken"
24+
"google.golang.org/api/option"
25+
26+
"cdr.dev/slog"
27+
"cdr.dev/slog/sloggers/slogtest"
1728

1829
"github.com/coder/coder/buildinfo"
30+
"github.com/coder/coder/coderd"
31+
"github.com/coder/coder/coderd/autobuild/executor"
1932
"github.com/coder/coder/coderd/coderdtest"
33+
"github.com/coder/coder/coderd/database"
34+
"github.com/coder/coder/coderd/database/databasefake"
35+
"github.com/coder/coder/coderd/database/postgres"
36+
"github.com/coder/coder/coderd/gitsshkey"
2037
"github.com/coder/coder/coderd/rbac"
38+
"github.com/coder/coder/coderd/telemetry"
39+
"github.com/coder/coder/coderd/turnconn"
2140
"github.com/coder/coder/codersdk"
2241
"github.com/coder/coder/provisioner/echo"
2342
"github.com/coder/coder/provisionersdk/proto"
@@ -39,13 +58,96 @@ func TestBuildInfo(t *testing.T) {
3958
// TestAuthorizeAllEndpoints will check `authorize` is called on every endpoint registered.
4059
func TestAuthorizeAllEndpoints(t *testing.T) {
4160
t.Parallel()
42-
ctx := context.Background()
61+
var (
62+
ctx = context.Background()
63+
authorizer = &fakeAuthorizer{}
64+
)
4365

44-
authorizer := &fakeAuthorizer{}
45-
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
46-
Authorizer: authorizer,
47-
IncludeProvisionerD: true,
48-
})
66+
// This function was taken from coderdtest.newWithAPI. It is intentionally
67+
// copied to avoid exposing the API to other tests in coderd. Tests should
68+
// not need a reference to coderd.API...this test is an exception.
69+
newClient := func(authorizer rbac.Authorizer) (*codersdk.Client, *coderd.API) {
70+
// This can be hotswapped for a live database instance.
71+
db := databasefake.New()
72+
pubsub := database.NewPubsubInMemory()
73+
if os.Getenv("DB") != "" {
74+
connectionURL, closePg, err := postgres.Open()
75+
require.NoError(t, err)
76+
t.Cleanup(closePg)
77+
sqlDB, err := sql.Open("postgres", connectionURL)
78+
require.NoError(t, err)
79+
t.Cleanup(func() {
80+
_ = sqlDB.Close()
81+
})
82+
err = database.MigrateUp(sqlDB)
83+
require.NoError(t, err)
84+
db = database.New(sqlDB)
85+
86+
pubsub, err = database.NewPubsub(context.Background(), sqlDB, connectionURL)
87+
require.NoError(t, err)
88+
t.Cleanup(func() {
89+
_ = pubsub.Close()
90+
})
91+
}
92+
93+
tickerCh := make(chan time.Time)
94+
t.Cleanup(func() { close(tickerCh) })
95+
96+
ctx, cancelFunc := context.WithCancel(context.Background())
97+
lifecycleExecutor := executor.New(
98+
ctx,
99+
db,
100+
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
101+
tickerCh,
102+
).WithStatsChannel(nil)
103+
lifecycleExecutor.Run()
104+
105+
srv := httptest.NewUnstartedServer(nil)
106+
srv.Config.BaseContext = func(_ net.Listener) context.Context {
107+
return ctx
108+
}
109+
srv.Start()
110+
serverURL, err := url.Parse(srv.URL)
111+
require.NoError(t, err)
112+
113+
turnServer, err := turnconn.New(nil)
114+
require.NoError(t, err)
115+
116+
validator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
117+
require.NoError(t, err)
118+
119+
// We set the handler after server creation for the access URL.
120+
coderAPI := coderd.New(&coderd.Options{
121+
AgentConnectionUpdateFrequency: 150 * time.Millisecond,
122+
AccessURL: serverURL,
123+
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
124+
Database: db,
125+
Pubsub: pubsub,
126+
127+
AWSCertificates: nil,
128+
AzureCertificates: x509.VerifyOptions{},
129+
GithubOAuth2Config: nil,
130+
GoogleTokenValidator: validator,
131+
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
132+
TURNServer: turnServer,
133+
APIRateLimit: 0,
134+
Authorizer: authorizer,
135+
Telemetry: telemetry.NewNoop(),
136+
})
137+
srv.Config.Handler = coderAPI.Handler
138+
139+
_ = coderdtest.NewProvisionerDaemon(t, coderAPI)
140+
t.Cleanup(func() {
141+
cancelFunc()
142+
_ = turnServer.Close()
143+
srv.Close()
144+
_ = coderAPI.Close()
145+
})
146+
147+
return codersdk.New(serverURL), coderAPI
148+
}
149+
150+
client, api := newClient(authorizer)
49151
admin := coderdtest.CreateFirstUser(t, client)
50152
// The provisioner will call to coderd and register itself. This is async,
51153
// so we wait for it to occur.

coderd/gitsshkey_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ func TestGitSSHKey(t *testing.T) {
7979
func TestAgentGitSSHKey(t *testing.T) {
8080
t.Parallel()
8181

82-
client, coderAPI := coderdtest.NewWithAPI(t, nil)
82+
client := coderdtest.New(t, &coderdtest.Options{
83+
IncludeProvisionerD: true,
84+
})
8385
user := coderdtest.CreateFirstUser(t, client)
84-
daemonCloser := coderdtest.NewProvisionerDaemon(t, coderAPI)
8586
authToken := uuid.NewString()
8687
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
8788
Parse: echo.ParseComplete,
@@ -107,7 +108,6 @@ func TestAgentGitSSHKey(t *testing.T) {
107108
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
108109
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
109110
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
110-
daemonCloser.Close()
111111

112112
agentClient := codersdk.New(client.URL)
113113
agentClient.SessionToken = authToken

coderd/templateversions_test.go

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package coderd_test
22

33
import (
44
"context"
5-
"database/sql"
65
"net/http"
76
"testing"
87
"time"
@@ -12,7 +11,6 @@ import (
1211
"github.com/stretchr/testify/require"
1312

1413
"github.com/coder/coder/coderd/coderdtest"
15-
"github.com/coder/coder/coderd/database"
1614
"github.com/coder/coder/codersdk"
1715
"github.com/coder/coder/provisioner/echo"
1816
"github.com/coder/coder/provisionersdk/proto"
@@ -555,21 +553,34 @@ func TestTemplateVersionDryRun(t *testing.T) {
555553

556554
t.Run("OK", func(t *testing.T) {
557555
t.Parallel()
558-
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerD: true})
556+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
559557
user := coderdtest.CreateFirstUser(t, client)
558+
560559
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
561560
Parse: echo.ParseComplete,
562-
Provision: []*proto.Provision_Response{{
563-
Type: &proto.Provision_Response_Log{
564-
Log: &proto.Log{},
561+
Provision: []*proto.Provision_Response{
562+
{
563+
Type: &proto.Provision_Response_Log{
564+
Log: &proto.Log{},
565+
}},
566+
{
567+
Type: &proto.Provision_Response_Complete{
568+
Complete: &proto.Provision_Complete{},
569+
},
565570
},
566-
}},
571+
},
567572
})
568-
forceCompleteTemplateVersionJob(t, api.Database, client, version)
573+
574+
version = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
569575

570576
// Create the dry-run
571577
job, err := client.CreateTemplateVersionDryRun(context.Background(), version.ID, codersdk.CreateTemplateVersionDryRunRequest{
572-
ParameterValues: []codersdk.CreateParameterRequest{},
578+
ParameterValues: []codersdk.CreateParameterRequest{
579+
{
580+
Name: echo.ParameterExecKey,
581+
SourceValue: "tail -f /dev/null",
582+
},
583+
},
573584
})
574585
require.NoError(t, err)
575586

@@ -606,13 +617,7 @@ func TestTemplateVersionDryRun(t *testing.T) {
606617
})
607618
require.NoError(t, err)
608619

609-
require.Eventually(t, func() bool {
610-
job, err := client.TemplateVersionDryRun(context.Background(), version.ID, job.ID)
611-
assert.NoError(t, err)
612-
613-
t.Logf("Status: %s", job.Status)
614-
return job.Status == codersdk.ProvisionerJobSucceeded
615-
}, 5*time.Second, 25*time.Millisecond)
620+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
616621

617622
err = client.CancelTemplateVersionDryRun(context.Background(), version.ID, job.ID)
618623
var apiErr *codersdk.Error
@@ -622,21 +627,33 @@ func TestTemplateVersionDryRun(t *testing.T) {
622627

623628
t.Run("AlreadyCanceled", func(t *testing.T) {
624629
t.Parallel()
625-
client, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerD: true})
630+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
626631
user := coderdtest.CreateFirstUser(t, client)
627632
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
628633
Parse: echo.ParseComplete,
629-
Provision: []*proto.Provision_Response{{
630-
Type: &proto.Provision_Response_Log{
631-
Log: &proto.Log{},
634+
Provision: []*proto.Provision_Response{
635+
{
636+
Type: &proto.Provision_Response_Log{
637+
Log: &proto.Log{},
638+
}},
639+
{
640+
Type: &proto.Provision_Response_Complete{
641+
Complete: &proto.Provision_Complete{},
642+
},
632643
},
633-
}},
644+
},
634645
})
635-
forceCompleteTemplateVersionJob(t, api.Database, client, version)
646+
647+
version = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
636648

637649
// Create the dry-run
638650
job, err := client.CreateTemplateVersionDryRun(context.Background(), version.ID, codersdk.CreateTemplateVersionDryRunRequest{
639-
ParameterValues: []codersdk.CreateParameterRequest{},
651+
ParameterValues: []codersdk.CreateParameterRequest{
652+
{
653+
Name: echo.ParameterExecKey,
654+
SourceValue: "tail -f /dev/null",
655+
},
656+
},
640657
})
641658
require.NoError(t, err)
642659

@@ -753,23 +770,3 @@ func TestPaginatedTemplateVersions(t *testing.T) {
753770
})
754771
}
755772
}
756-
757-
func forceCompleteTemplateVersionJob(t *testing.T, db database.Store, client *codersdk.Client, version codersdk.TemplateVersion) {
758-
t.Helper()
759-
760-
// HACK: we need the template version job to be finished so the dry-run job
761-
// can be created. We do this by canceling the job and then marking it as
762-
// successful.
763-
err := client.CancelTemplateVersion(context.Background(), version.ID)
764-
require.NoError(t, err)
765-
err = db.UpdateProvisionerJobWithCompleteByID(context.Background(), database.UpdateProvisionerJobWithCompleteByIDParams{
766-
ID: version.Job.ID,
767-
UpdatedAt: time.Now(),
768-
CompletedAt: sql.NullTime{
769-
Time: time.Now(),
770-
Valid: true,
771-
},
772-
Error: sql.NullString{},
773-
})
774-
require.NoError(t, err)
775-
}

coderd/users.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,34 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
737737
httpapi.Write(rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: sessionToken})
738738
}
739739

740+
func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) {
741+
var (
742+
ctx = r.Context()
743+
user = httpmw.UserParam(r)
744+
)
745+
746+
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceAPIKey.WithOwner(user.ID.String())) {
747+
httpapi.ResourceNotFound(rw)
748+
return
749+
}
750+
751+
keyID := chi.URLParam(r, "keyid")
752+
key, err := api.Database.GetAPIKeyByID(ctx, keyID)
753+
if errors.Is(err, sql.ErrNoRows) {
754+
httpapi.ResourceNotFound(rw)
755+
return
756+
}
757+
if err != nil {
758+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
759+
Message: "Internal error fetching API key.",
760+
Detail: err.Error(),
761+
})
762+
return
763+
}
764+
765+
httpapi.Write(rw, http.StatusOK, convertAPIKey(key))
766+
}
767+
740768
// Clear the user's session cookie.
741769
func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) {
742770
// Get a blank token cookie.
@@ -971,3 +999,16 @@ func findUser(id uuid.UUID, users []database.User) *database.User {
971999
}
9721000
return nil
9731001
}
1002+
1003+
func convertAPIKey(k database.APIKey) codersdk.APIKey {
1004+
return codersdk.APIKey{
1005+
ID: k.ID,
1006+
UserID: k.UserID,
1007+
LastUsed: k.LastUsed,
1008+
ExpiresAt: k.ExpiresAt,
1009+
CreatedAt: k.CreatedAt,
1010+
UpdatedAt: k.UpdatedAt,
1011+
LoginType: codersdk.LoginType(k.LoginType),
1012+
LifetimeSeconds: k.LifetimeSeconds,
1013+
}
1014+
}

0 commit comments

Comments
 (0)