Skip to content

Commit a51cde9

Browse files
committed
Merge branch 'main' into 6724-ssh-metrics
2 parents 8cd07f2 + 1bd3ed9 commit a51cde9

File tree

91 files changed

+1148
-766
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1148
-766
lines changed

coderd/apidoc/docs.go

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/userauth.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,11 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
921921
Username: params.Username,
922922
OrganizationID: organizationID,
923923
},
924-
LoginType: params.LoginType,
924+
// All of the userauth tests depend on this being able to create
925+
// the first organization. It shouldn't be possible in normal
926+
// operation.
927+
CreateOrganization: len(organizations) == 0,
928+
LoginType: params.LoginType,
925929
})
926930
if err != nil {
927931
return xerrors.Errorf("create user: %w", err)

coderd/users.go

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
128128
// Create an org for the first user.
129129
OrganizationID: uuid.Nil,
130130
},
131-
LoginType: database.LoginTypePassword,
131+
CreateOrganization: true,
132+
LoginType: database.LoginTypePassword,
132133
})
133134
if err != nil {
134135
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
@@ -313,19 +314,41 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
313314
return
314315
}
315316

316-
_, err = api.Database.GetOrganizationByID(ctx, req.OrganizationID)
317-
if httpapi.Is404Error(err) {
318-
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
319-
Message: fmt.Sprintf("Organization does not exist with the provided id %q.", req.OrganizationID),
320-
})
321-
return
322-
}
323-
if err != nil {
324-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
325-
Message: "Internal error fetching organization.",
326-
Detail: err.Error(),
327-
})
328-
return
317+
if req.OrganizationID != uuid.Nil {
318+
// If an organization was provided, make sure it exists.
319+
_, err := api.Database.GetOrganizationByID(ctx, req.OrganizationID)
320+
if err != nil {
321+
if httpapi.Is404Error(err) {
322+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
323+
Message: fmt.Sprintf("Organization does not exist with the provided id %q.", req.OrganizationID),
324+
})
325+
return
326+
}
327+
328+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
329+
Message: "Internal error fetching organization.",
330+
Detail: err.Error(),
331+
})
332+
return
333+
}
334+
} else {
335+
// If no organization is provided, add the user to the first
336+
// organization.
337+
organizations, err := api.Database.GetOrganizations(ctx)
338+
if err != nil {
339+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
340+
Message: "Internal error fetching orgs.",
341+
Detail: err.Error(),
342+
})
343+
return
344+
}
345+
346+
if len(organizations) > 0 {
347+
// Add the user to the first organization. Once multi-organization
348+
// support is added, we should enable a configuration map of user
349+
// email to organization.
350+
req.OrganizationID = organizations[0].ID
351+
}
329352
}
330353

331354
err = userpassword.Validate(req.Password)
@@ -955,7 +978,8 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques
955978

956979
type CreateUserRequest struct {
957980
codersdk.CreateUserRequest
958-
LoginType database.LoginType
981+
CreateOrganization bool
982+
LoginType database.LoginType
959983
}
960984

961985
func (api *API) CreateUser(ctx context.Context, store database.Store, req CreateUserRequest) (database.User, uuid.UUID, error) {
@@ -964,6 +988,10 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
964988
orgRoles := make([]string, 0)
965989
// If no organization is provided, create a new one for the user.
966990
if req.OrganizationID == uuid.Nil {
991+
if !req.CreateOrganization {
992+
return xerrors.Errorf("organization ID must be provided")
993+
}
994+
967995
organization, err := tx.InsertOrganization(ctx, database.InsertOrganizationParams{
968996
ID: uuid.New(),
969997
Name: req.Username,

coderd/users_test.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/google/uuid"
13+
"github.com/stretchr/testify/assert"
1314
"github.com/stretchr/testify/require"
1415
"golang.org/x/sync/errgroup"
1516

@@ -478,21 +479,49 @@ func TestPostUsers(t *testing.T) {
478479
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
479480
})
480481

482+
t.Run("CreateWithoutOrg", func(t *testing.T) {
483+
t.Parallel()
484+
auditor := audit.NewMock()
485+
client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor})
486+
numLogs := len(auditor.AuditLogs())
487+
488+
firstUser := coderdtest.CreateFirstUser(t, client)
489+
numLogs++ // add an audit log for user create
490+
numLogs++ // add an audit log for login
491+
492+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
493+
defer cancel()
494+
495+
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
496+
Email: "another@user.org",
497+
Username: "someone-else",
498+
Password: "SomeSecurePassword!",
499+
})
500+
require.NoError(t, err)
501+
502+
require.Len(t, auditor.AuditLogs(), numLogs)
503+
require.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[numLogs-1].Action)
504+
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-2].Action)
505+
506+
require.Len(t, user.OrganizationIDs, 1)
507+
assert.Equal(t, firstUser.OrganizationID, user.OrganizationIDs[0])
508+
})
509+
481510
t.Run("Create", func(t *testing.T) {
482511
t.Parallel()
483512
auditor := audit.NewMock()
484513
client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor})
485514
numLogs := len(auditor.AuditLogs())
486515

487-
user := coderdtest.CreateFirstUser(t, client)
516+
firstUser := coderdtest.CreateFirstUser(t, client)
488517
numLogs++ // add an audit log for user create
489518
numLogs++ // add an audit log for login
490519

491520
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
492521
defer cancel()
493522

494-
_, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
495-
OrganizationID: user.OrganizationID,
523+
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
524+
OrganizationID: firstUser.OrganizationID,
496525
Email: "another@user.org",
497526
Username: "someone-else",
498527
Password: "SomeSecurePassword!",
@@ -502,6 +531,9 @@ func TestPostUsers(t *testing.T) {
502531
require.Len(t, auditor.AuditLogs(), numLogs)
503532
require.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[numLogs-1].Action)
504533
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-2].Action)
534+
535+
require.Len(t, user.OrganizationIDs, 1)
536+
assert.Equal(t, firstUser.OrganizationID, user.OrganizationIDs[0])
505537
})
506538

507539
t.Run("LastSeenAt", func(t *testing.T) {

codersdk/users.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ type CreateUserRequest struct {
6969
Email string `json:"email" validate:"required,email" format:"email"`
7070
Username string `json:"username" validate:"required,username"`
7171
Password string `json:"password" validate:"required"`
72-
OrganizationID uuid.UUID `json:"organization_id" validate:"required" format:"uuid"`
72+
OrganizationID uuid.UUID `json:"organization_id" validate:"" format:"uuid"`
7373
}
7474

7575
type UpdateUserProfileRequest struct {

docs/admin/configure.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ coder server postgres-builtin-url
6565
psql "postgres://coder@localhost:49627/coder?sslmode=disable&password=feU...yI1"
6666
```
6767

68+
### Migrating from the built-in database to an external database
69+
70+
To migrate from the built-in database to an external database, follow these steps:
71+
72+
1. Stop your Coder deployment.
73+
2. Run `coder server postgres-builtin-serve` in a background terminal.
74+
3. Run `coder server postgres-builtin-url` and copy its output command.
75+
4. Run `pg_dump <built-in-connection-string> > coder.sql` to dump the internal database to a file.
76+
5. Restore that content to an external database with `psql <external-connection-string> < coder.sql`.
77+
6. Start your Coder deployment with `CODER_PG_CONNECTION_URL=<external-connection-string>`.
78+
6879
## System packages
6980

7081
If you've installed Coder via a [system package](../install/packages.md) Coder, you can

docs/api/schemas.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
15931593
| Name | Type | Required | Restrictions | Description |
15941594
| ----------------- | ------ | -------- | ------------ | ----------- |
15951595
| `email` | string | true | | |
1596-
| `organization_id` | string | true | | |
1596+
| `organization_id` | string | false | | |
15971597
| `password` | string | true | | |
15981598
| `username` | string | true | | |
15991599

dogfood/Dockerfile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ ARG CLOUD_SQL_PROXY_VERSION=2.2.0 \
226226

227227
# cloud_sql_proxy, for connecting to cloudsql instances
228228
# the upstream go.mod prevents this from being installed with go install
229-
RUN curl --silent --show-error --location --output /usr/local/bin/cloud_sql_proxy "https://storage.googleapis.com/cloudsql-proxy/v${CLOUD_SQL_PROXY_VERSION}/cloud_sql_proxy.linux.amd64" && \
229+
RUN curl --silent --show-error --location --output /usr/local/bin/cloud_sql_proxy "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v${CLOUD_SQL_PROXY_VERSION}/cloud-sql-proxy.linux.amd64" && \
230230
chmod a=rx /usr/local/bin/cloud_sql_proxy && \
231231
# dive for scanning image layer utilization metrics in CI
232232
curl --silent --show-error --location "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.tar.gz" | \
@@ -246,8 +246,7 @@ RUN curl --silent --show-error --location --output /usr/local/bin/cloud_sql_prox
246246
tar --extract --gzip --directory=/usr/local/bin --file=- --strip-components=1 linux-amd64/helm && \
247247
# kube-linter for linting Kubernetes objects, including those
248248
# that Helm generates from our charts
249-
curl --silent --show-error --location "https://github.com/stackrox/kube-linter/releases/download/${KUBE_LINTER_VERSION}/kube-linter-linux.tar.gz" | \
250-
tar --extract --gzip --directory=/usr/local/bin --file=- kube-linter && \
249+
curl --silent --show-error --location "https://github.com/stackrox/kube-linter/releases/download/${KUBE_LINTER_VERSION}/kube-linter-linux" --output /usr/local/bin/kube-linter && \
251250
# kubens and kubectx for managing Kubernetes namespaces and contexts
252251
curl --silent --show-error --location "https://github.com/ahmetb/kubectx/releases/download/v${KUBECTX_VERSION}/kubectx_v${KUBECTX_VERSION}_linux_x86_64.tar.gz" | \
253252
tar --extract --gzip --directory=/usr/local/bin --file=- kubectx && \
-852 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)