chore: update v1 schema (#643)

This commit is contained in:
Colin Adler 2022-04-01 14:42:36 -05:00 committed by GitHub
parent cbb82ce017
commit 2b1a0ee126
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 757 additions and 624 deletions

View File

@ -187,6 +187,7 @@ linters-settings:
- t
- id
- wg
- Me
# Optional list of variable declarations that should be ignored completely. (defaults to empty list)
# Entries must be in the form of "<variable name> <type>" or "<variable name> *<type>" for
# variables, or "const <name>" for constants.

View File

@ -12,6 +12,7 @@ build: site/out bin
# Runs migrations to output a dump of the database.
coderd/database/dump.sql: $(wildcard coderd/database/migrations/*.sql)
go run coderd/database/dump/main.go
.PHONY: coderd/database/dump.sql
# Generates Go code for querying the database.
coderd/database/generate: fmt/sql coderd/database/dump.sql coderd/database/query.sql

View File

@ -52,7 +52,7 @@ func configSSH() *cobra.Command {
sshConfigContent = sshConfigContent[:startIndex-1] + sshConfigContent[endIndex+len(sshEndToken):]
}
workspaces, err := client.WorkspacesByUser(cmd.Context(), "")
workspaces, err := client.WorkspacesByUser(cmd.Context(), codersdk.Me)
if err != nil {
return err
}

View File

@ -8,6 +8,7 @@ import (
"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/pty/ptytest"
)
@ -21,7 +22,7 @@ func TestConfigSSH(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
tempFile, err := os.CreateTemp(t.TempDir(), "")
require.NoError(t, err)

View File

@ -119,10 +119,10 @@ func login() *cobra.Command {
}
_, err = client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{
Email: email,
Username: username,
Organization: username,
Password: password,
Email: email,
Username: username,
OrganizationName: username,
Password: password,
})
if err != nil {
return xerrors.Errorf("create initial user: %w", err)
@ -167,7 +167,7 @@ func login() *cobra.Command {
Secret: true,
Validate: func(token string) error {
client.SessionToken = token
_, err := client.User(cmd.Context(), "me")
_, err := client.User(cmd.Context(), codersdk.Me)
if err != nil {
return xerrors.New("That's not a valid token!")
}
@ -180,7 +180,7 @@ func login() *cobra.Command {
// Login to get user data - verify it is OK before persisting
client.SessionToken = sessionToken
resp, err := client.User(cmd.Context(), "me")
resp, err := client.User(cmd.Context(), codersdk.Me)
if err != nil {
return xerrors.Errorf("get user: %w", err)
}

View File

@ -3,6 +3,7 @@ package cli
import (
"context"
"github.com/google/uuid"
"github.com/spf13/cobra"
"golang.org/x/xerrors"
@ -20,42 +21,45 @@ func parameters() *cobra.Command {
return cmd
}
func parseScopeAndID(ctx context.Context, client *codersdk.Client, organization codersdk.Organization, rawScope string, name string) (codersdk.ParameterScope, string, error) {
func parseScopeAndID(ctx context.Context, client *codersdk.Client, organization codersdk.Organization, rawScope string, name string) (codersdk.ParameterScope, uuid.UUID, error) {
scope, err := parseParameterScope(rawScope)
if err != nil {
return scope, "", err
return scope, uuid.Nil, err
}
var scopeID string
var scopeID uuid.UUID
switch scope {
case codersdk.ParameterOrganization:
if name == "" {
scopeID = organization.ID
} else {
org, err := client.OrganizationByName(ctx, "", name)
org, err := client.OrganizationByName(ctx, codersdk.Me, name)
if err != nil {
return scope, "", err
return scope, uuid.Nil, err
}
scopeID = org.ID
}
case codersdk.ParameterProject:
project, err := client.ProjectByName(ctx, organization.ID, name)
if err != nil {
return scope, "", err
return scope, uuid.Nil, err
}
scopeID = project.ID.String()
scopeID = project.ID
case codersdk.ParameterUser:
user, err := client.User(ctx, name)
uid, _ := uuid.Parse(name)
user, err := client.User(ctx, uid)
if err != nil {
return scope, "", err
return scope, uuid.Nil, err
}
scopeID = user.ID
case codersdk.ParameterWorkspace:
workspace, err := client.WorkspaceByName(ctx, "", name)
workspace, err := client.WorkspaceByName(ctx, codersdk.Me, name)
if err != nil {
return scope, "", err
return scope, uuid.Nil, err
}
scopeID = workspace.ID.String()
scopeID = workspace.ID
}
return scope, scopeID, nil
}

View File

@ -107,7 +107,7 @@ func createClient(cmd *cobra.Command) (*codersdk.Client, error) {
// currentOrganization returns the currently active organization for the authenticated user.
func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (codersdk.Organization, error) {
orgs, err := client.OrganizationsByUser(cmd.Context(), "me")
orgs, err := client.OrganizationsByUser(cmd.Context(), codersdk.Me)
if err != nil {
return codersdk.Organization{}, nil
}

View File

@ -32,26 +32,32 @@ func ssh() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}
if workspace.LatestBuild.Transition != database.WorkspaceTransitionStart {
return xerrors.New("workspace must be in start transition to ssh")
}
if workspace.LatestBuild.Job.CompletedAt == nil {
err = cliui.WorkspaceBuild(cmd.Context(), cmd.ErrOrStderr(), client, workspace.LatestBuild.ID, workspace.CreatedAt)
if err != nil {
return err
}
}
if workspace.LatestBuild.Transition == database.WorkspaceTransitionDelete {
return xerrors.New("workspace is deleting...")
}
resources, err := client.WorkspaceResourcesByBuild(cmd.Context(), workspace.LatestBuild.ID)
if err != nil {
return err
}
resourceByAddress := make(map[string]codersdk.WorkspaceResource)
for _, resource := range resources {
if resource.Agent == nil {
@ -59,6 +65,7 @@ func ssh() *cobra.Command {
}
resourceByAddress[resource.Address] = resource
}
var resourceAddress string
if len(args) >= 2 {
resourceAddress = args[1]
@ -73,6 +80,7 @@ func ssh() *cobra.Command {
break
}
}
resource, exists := resourceByAddress[resourceAddress]
if !exists {
resourceKeys := make([]string, 0)
@ -100,6 +108,7 @@ func ssh() *cobra.Command {
return err
}
defer conn.Close()
if stdio {
rawSSH, err := conn.SSH()
if err != nil {
@ -135,13 +144,16 @@ func ssh() *cobra.Command {
if err != nil {
return err
}
sshSession.Stdin = cmd.InOrStdin()
sshSession.Stdout = cmd.OutOrStdout()
sshSession.Stderr = cmd.OutOrStdout()
err = sshSession.Shell()
if err != nil {
return err
}
err = sshSession.Wait()
if err != nil {
return err

View File

@ -53,7 +53,7 @@ func TestSSH(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
go func() {
// Run this async so the SSH command has to wait for
// the build and agent to connect!
@ -111,7 +111,7 @@ func TestSSH(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
go func() {
// Run this async so the SSH command has to wait for
// the build and agent to connect!

View File

@ -255,7 +255,7 @@ func start() *cobra.Command {
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "\n\n"+cliui.Styles.Bold.Render("Interrupt caught. Gracefully exiting..."))
if dev {
workspaces, err := client.WorkspacesByUser(cmd.Context(), "")
workspaces, err := client.WorkspacesByUser(cmd.Context(), codersdk.Me)
if err != nil {
return xerrors.Errorf("get workspaces: %w", err)
}
@ -343,10 +343,10 @@ func start() *cobra.Command {
func createFirstUser(cmd *cobra.Command, client *codersdk.Client, cfg config.Root) error {
_, err := client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{
Email: "admin@coder.com",
Username: "developer",
Password: "password",
Organization: "acme-corp",
Email: "admin@coder.com",
Username: "developer",
Password: "password",
OrganizationName: "acme-corp",
})
if err != nil {
return xerrors.Errorf("create first user: %w", err)

View File

@ -59,10 +59,10 @@ func TestStart(t *testing.T) {
return true
}, 15*time.Second, 25*time.Millisecond)
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "some@one.com",
Username: "example",
Password: "password",
Organization: "example",
Email: "some@one.com",
Username: "example",
Password: "password",
OrganizationName: "example",
})
require.NoError(t, err)
cancelFunc()
@ -90,7 +90,7 @@ func TestStart(t *testing.T) {
require.NoError(t, err)
client := codersdk.New(parsed)
client.SessionToken = token
_, err = client.User(ctx, "")
_, err = client.User(ctx, codersdk.Me)
require.NoError(t, err)
})
t.Run("TLSBadVersion", func(t *testing.T) {
@ -182,7 +182,7 @@ func TestStart(t *testing.T) {
require.NoError(t, err)
client := codersdk.New(parsed)
client.SessionToken = token
orgs, err := client.OrganizationsByUser(ctx, "")
orgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
require.NoError(t, err)
coderdtest.NewProvisionerDaemon(t, client)
@ -190,7 +190,7 @@ func TestStart(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, orgs[0].ID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, orgs[0].ID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
require.NoError(t, err)

View File

@ -8,6 +8,7 @@ import (
"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
)
@ -43,7 +44,7 @@ func TestWorkspaceAgent(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
cmd, _ := clitest.New(t, "workspaces", "agent", "--auth", "aws-instance-identity", "--url", client.URL.String())
@ -97,7 +98,7 @@ func TestWorkspaceAgent(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
cmd, _ := clitest.New(t, "workspaces", "agent", "--auth", "google-instance-identity", "--url", client.URL.String())

View File

@ -74,7 +74,7 @@ func workspaceCreate() *cobra.Command {
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Prompt.String()+"Creating with the "+cliui.Styles.Field.Render(project.Name)+" project...")
workspaceName := args[0]
_, err = client.WorkspaceByName(cmd.Context(), "", workspaceName)
_, err = client.WorkspaceByName(cmd.Context(), codersdk.Me, workspaceName)
if err == nil {
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
}
@ -137,7 +137,7 @@ func workspaceCreate() *cobra.Command {
}
before := time.Now()
workspace, err := client.CreateWorkspace(cmd.Context(), "", codersdk.CreateWorkspaceRequest{
workspace, err := client.CreateWorkspace(cmd.Context(), codersdk.Me, codersdk.CreateWorkspaceRequest{
ProjectID: project.ID,
Name: workspaceName,
ParameterValues: parameters,

View File

@ -21,7 +21,7 @@ func workspaceDelete() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}

View File

@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra"
"github.com/coder/coder/cli/cliui"
"github.com/coder/coder/codersdk"
)
func workspaceList() *cobra.Command {
@ -21,7 +22,7 @@ func workspaceList() *cobra.Command {
return err
}
start := time.Now()
workspaces, err := client.WorkspacesByUser(cmd.Context(), "")
workspaces, err := client.WorkspacesByUser(cmd.Context(), codersdk.Me)
if err != nil {
return err
}

View File

@ -4,6 +4,8 @@ import (
"strings"
"github.com/spf13/cobra"
"github.com/coder/coder/codersdk"
)
func workspaces() *cobra.Command {
@ -29,7 +31,7 @@ func validArgsWorkspaceName(cmd *cobra.Command, _ []string, toComplete string) (
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
workspaces, err := client.WorkspacesByUser(cmd.Context(), "")
workspaces, err := client.WorkspacesByUser(cmd.Context(), codersdk.Me)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
"github.com/spf13/cobra"
"github.com/coder/coder/codersdk"
)
func workspaceShow() *cobra.Command {
@ -14,7 +16,7 @@ func workspaceShow() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}

View File

@ -20,7 +20,7 @@ func workspaceStart() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}

View File

@ -20,7 +20,7 @@ func workspaceStop() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}

View File

@ -17,7 +17,7 @@ func workspaceUpdate() *cobra.Command {
if err != nil {
return err
}
workspace, err := client.WorkspaceByName(cmd.Context(), "", args[0])
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
if err != nil {
return err
}

View File

@ -99,10 +99,10 @@ func parse(cmd *cobra.Command, parameters []codersdk.CreateParameterRequest) err
defer daemonClose.Close()
created, err := client.CreateFirstUser(cmd.Context(), codersdk.CreateFirstUserRequest{
Email: "templater@coder.com",
Username: "templater",
Organization: "templater",
Password: "insecure",
Email: "templater@coder.com",
Username: "templater",
OrganizationName: "templater",
Password: "insecure",
})
if err != nil {
return err

View File

@ -155,10 +155,10 @@ func NewProvisionerDaemon(t *testing.T, client *codersdk.Client) io.Closer {
// with the passed in codersdk client.
func CreateFirstUser(t *testing.T, client *codersdk.Client) codersdk.CreateFirstUserResponse {
req := codersdk.CreateFirstUserRequest{
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
Organization: "testorg",
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
OrganizationName: "testorg",
}
resp, err := client.CreateFirstUser(context.Background(), req)
require.NoError(t, err)
@ -173,12 +173,12 @@ func CreateFirstUser(t *testing.T, client *codersdk.Client) codersdk.CreateFirst
}
// CreateAnotherUser creates and authenticates a new user.
func CreateAnotherUser(t *testing.T, client *codersdk.Client, organization string) *codersdk.Client {
func CreateAnotherUser(t *testing.T, client *codersdk.Client, organizationID uuid.UUID) *codersdk.Client {
req := codersdk.CreateUserRequest{
Email: namesgenerator.GetRandomName(1) + "@coder.com",
Username: randomUsername(),
Password: "testpass",
OrganizationID: organization,
OrganizationID: organizationID,
}
_, err := client.CreateUser(context.Background(), req)
require.NoError(t, err)
@ -197,12 +197,12 @@ func CreateAnotherUser(t *testing.T, client *codersdk.Client, organization strin
// CreateProjectVersion creates a project import provisioner job
// with the responses provided. It uses the "echo" provisioner for compatibility
// with testing.
func CreateProjectVersion(t *testing.T, client *codersdk.Client, organization string, res *echo.Responses) codersdk.ProjectVersion {
func CreateProjectVersion(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, res *echo.Responses) codersdk.ProjectVersion {
data, err := echo.Tar(res)
require.NoError(t, err)
file, err := client.Upload(context.Background(), codersdk.ContentTypeTar, data)
require.NoError(t, err)
projectVersion, err := client.CreateProjectVersion(context.Background(), organization, codersdk.CreateProjectVersionRequest{
projectVersion, err := client.CreateProjectVersion(context.Background(), organizationID, codersdk.CreateProjectVersionRequest{
StorageSource: file.Hash,
StorageMethod: database.ProvisionerStorageMethodFile,
Provisioner: database.ProvisionerTypeEcho,
@ -213,7 +213,7 @@ func CreateProjectVersion(t *testing.T, client *codersdk.Client, organization st
// CreateProject creates a project with the "echo" provisioner for
// compatibility with testing. The name assigned is randomly generated.
func CreateProject(t *testing.T, client *codersdk.Client, organization string, version uuid.UUID) codersdk.Project {
func CreateProject(t *testing.T, client *codersdk.Client, organization uuid.UUID, version uuid.UUID) codersdk.Project {
project, err := client.CreateProject(context.Background(), organization, codersdk.CreateProjectRequest{
Name: randomUsername(),
VersionID: version,
@ -268,7 +268,7 @@ func AwaitWorkspaceAgents(t *testing.T, client *codersdk.Client, build uuid.UUID
// CreateWorkspace creates a workspace for the user and project provided.
// A random name is generated for it.
func CreateWorkspace(t *testing.T, client *codersdk.Client, user string, projectID uuid.UUID) codersdk.Workspace {
func CreateWorkspace(t *testing.T, client *codersdk.Client, user uuid.UUID, projectID uuid.UUID) codersdk.Workspace {
workspace, err := client.CreateWorkspace(context.Background(), user, codersdk.CreateWorkspaceRequest{
ProjectID: projectID,
Name: randomUsername(),

View File

@ -6,6 +6,7 @@ import (
"go.uber.org/goleak"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/codersdk"
)
func TestMain(m *testing.M) {
@ -20,7 +21,7 @@ func TestNew(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
_, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false)

View File

@ -143,7 +143,7 @@ func (q *fakeQuerier) GetUserByEmailOrUsername(_ context.Context, arg database.G
return database.User{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetUserByID(_ context.Context, id string) (database.User, error) {
func (q *fakeQuerier) GetUserByID(_ context.Context, id uuid.UUID) (database.User, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
@ -217,34 +217,33 @@ func (q *fakeQuerier) GetWorkspaceOwnerCountsByProjectIDs(_ context.Context, pro
q.mutex.RLock()
defer q.mutex.RUnlock()
counts := map[string]map[string]struct{}{}
counts := map[uuid.UUID]map[uuid.UUID]struct{}{}
for _, projectID := range projectIDs {
found := false
for _, workspace := range q.workspace {
if workspace.ProjectID.String() != projectID.String() {
if workspace.ProjectID != projectID {
continue
}
if workspace.Deleted {
continue
}
countByOwnerID, ok := counts[projectID.String()]
countByOwnerID, ok := counts[projectID]
if !ok {
countByOwnerID = map[string]struct{}{}
countByOwnerID = map[uuid.UUID]struct{}{}
}
countByOwnerID[workspace.OwnerID] = struct{}{}
counts[projectID.String()] = countByOwnerID
counts[projectID] = countByOwnerID
found = true
break
}
if !found {
counts[projectID.String()] = map[string]struct{}{}
counts[projectID] = map[uuid.UUID]struct{}{}
}
}
res := make([]database.GetWorkspaceOwnerCountsByProjectIDsRow, 0)
for key, value := range counts {
uid := uuid.MustParse(key)
res = append(res, database.GetWorkspaceOwnerCountsByProjectIDsRow{
ProjectID: uid,
ProjectID: key,
Count: int64(len(value)),
})
}
@ -364,7 +363,7 @@ func (q *fakeQuerier) GetWorkspacesByUserID(_ context.Context, req database.GetW
return workspaces, nil
}
func (q *fakeQuerier) GetOrganizationByID(_ context.Context, id string) (database.Organization, error) {
func (q *fakeQuerier) GetOrganizationByID(_ context.Context, id uuid.UUID) (database.Organization, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
@ -388,7 +387,7 @@ func (q *fakeQuerier) GetOrganizationByName(_ context.Context, name string) (dat
return database.Organization{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetOrganizationsByUserID(_ context.Context, userID string) ([]database.Organization, error) {
func (q *fakeQuerier) GetOrganizationsByUserID(_ context.Context, userID uuid.UUID) ([]database.Organization, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
@ -483,7 +482,7 @@ func (q *fakeQuerier) GetProjectVersionByProjectIDAndName(_ context.Context, arg
defer q.mutex.RUnlock()
for _, projectVersion := range q.projectVersion {
if projectVersion.ProjectID.UUID.String() != arg.ProjectID.UUID.String() {
if projectVersion.ProjectID != arg.ProjectID {
continue
}
if !strings.EqualFold(projectVersion.Name, arg.Name) {
@ -1056,7 +1055,7 @@ func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.Inser
ProjectVersionID: arg.ProjectVersionID,
BeforeID: arg.BeforeID,
Transition: arg.Transition,
Initiator: arg.Initiator,
InitiatorID: arg.InitiatorID,
JobID: arg.JobID,
ProvisionerState: arg.ProvisionerState,
}

159
coderd/database/dump.sql generated
View File

@ -57,12 +57,6 @@ CREATE TYPE provisioner_type AS ENUM (
'terraform'
);
CREATE TYPE userstatus AS ENUM (
'active',
'dormant',
'decommissioned'
);
CREATE TYPE workspace_transition AS ENUM (
'start',
'stop',
@ -72,7 +66,7 @@ CREATE TYPE workspace_transition AS ENUM (
CREATE TABLE api_keys (
id text NOT NULL,
hashed_secret bytea NOT NULL,
user_id text NOT NULL,
user_id uuid NOT NULL,
application boolean NOT NULL,
name text NOT NULL,
last_used timestamp with time zone NOT NULL,
@ -90,7 +84,7 @@ CREATE TABLE api_keys (
CREATE TABLE files (
hash character varying(64) NOT NULL,
created_at timestamp with time zone NOT NULL,
created_by text NOT NULL,
created_by uuid NOT NULL,
mimetype character varying(64) NOT NULL,
data bytea NOT NULL
);
@ -101,25 +95,30 @@ CREATE TABLE licenses (
created_at timestamp with time zone NOT NULL
);
CREATE SEQUENCE licenses_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE licenses_id_seq OWNED BY public.licenses.id;
CREATE TABLE organization_members (
organization_id text NOT NULL,
user_id text NOT NULL,
user_id uuid NOT NULL,
organization_id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
roles text[] DEFAULT '{organization-member}'::text[] NOT NULL
);
CREATE TABLE organizations (
id text NOT NULL,
id uuid NOT NULL,
name text NOT NULL,
description text NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
"default" boolean DEFAULT false NOT NULL,
auto_off_threshold bigint DEFAULT '28800000000000'::bigint NOT NULL,
cpu_provisioning_rate real DEFAULT 4.0 NOT NULL,
memory_provisioning_rate real DEFAULT 1.0 NOT NULL,
workspace_auto_off boolean DEFAULT false NOT NULL
updated_at timestamp with time zone NOT NULL
);
CREATE TABLE parameter_schemas (
@ -146,7 +145,7 @@ CREATE TABLE parameter_values (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
scope parameter_scope NOT NULL,
scope_id text NOT NULL,
scope_id uuid NOT NULL,
name character varying(64) NOT NULL,
source_scheme parameter_source_scheme NOT NULL,
source_value text NOT NULL,
@ -156,7 +155,7 @@ CREATE TABLE parameter_values (
CREATE TABLE project_versions (
id uuid NOT NULL,
project_id uuid,
organization_id text NOT NULL,
organization_id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
name character varying(64) NOT NULL,
@ -168,7 +167,7 @@ CREATE TABLE projects (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
organization_id text NOT NULL,
organization_id uuid NOT NULL,
deleted boolean DEFAULT false NOT NULL,
name character varying(64) NOT NULL,
provisioner provisioner_type NOT NULL,
@ -179,7 +178,7 @@ CREATE TABLE provisioner_daemons (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone,
organization_id text,
organization_id uuid,
name character varying(64) NOT NULL,
provisioners provisioner_type[] NOT NULL
);
@ -202,8 +201,8 @@ CREATE TABLE provisioner_jobs (
canceled_at timestamp with time zone,
completed_at timestamp with time zone,
error text,
organization_id text NOT NULL,
initiator_id text NOT NULL,
organization_id uuid NOT NULL,
initiator_id uuid NOT NULL,
provisioner provisioner_type NOT NULL,
storage_method provisioner_storage_method NOT NULL,
storage_source text NOT NULL,
@ -213,7 +212,7 @@ CREATE TABLE provisioner_jobs (
);
CREATE TABLE users (
id text NOT NULL,
id uuid NOT NULL,
email text NOT NULL,
name text NOT NULL,
revoked boolean NOT NULL,
@ -221,17 +220,7 @@ CREATE TABLE users (
hashed_password bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
temporary_password boolean DEFAULT false NOT NULL,
avatar_hash text DEFAULT ''::text NOT NULL,
ssh_key_regenerated_at timestamp with time zone DEFAULT now() NOT NULL,
username text DEFAULT ''::text NOT NULL,
dotfiles_git_uri text DEFAULT ''::text NOT NULL,
roles text[] DEFAULT '{site-member}'::text[] NOT NULL,
status userstatus DEFAULT 'active'::public.userstatus NOT NULL,
relatime timestamp with time zone DEFAULT now() NOT NULL,
gpg_key_regenerated_at timestamp with time zone DEFAULT now() NOT NULL,
_decomissioned boolean DEFAULT false NOT NULL,
shell text DEFAULT ''::text NOT NULL
username text DEFAULT ''::text NOT NULL
);
CREATE TABLE workspace_agents (
@ -260,7 +249,7 @@ CREATE TABLE workspace_builds (
before_id uuid,
after_id uuid,
transition workspace_transition NOT NULL,
initiator character varying(255) NOT NULL,
initiator_id uuid NOT NULL,
provisioner_state bytea,
job_id uuid NOT NULL
);
@ -280,85 +269,136 @@ CREATE TABLE workspaces (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
owner_id text NOT NULL,
owner_id uuid NOT NULL,
project_id uuid NOT NULL,
deleted boolean DEFAULT false NOT NULL,
name character varying(64) NOT NULL
);
ALTER TABLE ONLY files
ADD CONSTRAINT files_hash_key UNIQUE (hash);
ALTER TABLE ONLY licenses ALTER COLUMN id SET DEFAULT nextval('public.licenses_id_seq'::regclass);
ALTER TABLE ONLY parameter_schemas
ADD CONSTRAINT parameter_schemas_id_key UNIQUE (id);
ALTER TABLE ONLY api_keys
ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id);
ALTER TABLE ONLY files
ADD CONSTRAINT files_pkey PRIMARY KEY (hash);
ALTER TABLE ONLY licenses
ADD CONSTRAINT licenses_pkey PRIMARY KEY (id);
ALTER TABLE ONLY organization_members
ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id);
ALTER TABLE ONLY organizations
ADD CONSTRAINT organizations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY parameter_schemas
ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name);
ALTER TABLE ONLY parameter_schemas
ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id);
ALTER TABLE ONLY parameter_values
ADD CONSTRAINT parameter_values_id_key UNIQUE (id);
ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id);
ALTER TABLE ONLY parameter_values
ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name);
ALTER TABLE ONLY project_versions
ADD CONSTRAINT project_versions_id_key UNIQUE (id);
ADD CONSTRAINT project_versions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_versions
ADD CONSTRAINT project_versions_project_id_name_key UNIQUE (project_id, name);
ALTER TABLE ONLY projects
ADD CONSTRAINT projects_id_key UNIQUE (id);
ALTER TABLE ONLY projects
ADD CONSTRAINT projects_organization_id_name_key UNIQUE (organization_id, name);
ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_id_key UNIQUE (id);
ALTER TABLE ONLY projects
ADD CONSTRAINT projects_pkey PRIMARY KEY (id);
ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_name_key UNIQUE (name);
ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id);
ALTER TABLE ONLY provisioner_job_logs
ADD CONSTRAINT provisioner_job_logs_id_key UNIQUE (id);
ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id);
ALTER TABLE ONLY provisioner_jobs
ADD CONSTRAINT provisioner_jobs_id_key UNIQUE (id);
ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id);
ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspace_agents
ADD CONSTRAINT workspace_agents_auth_token_key UNIQUE (auth_token);
ALTER TABLE ONLY workspace_agents
ADD CONSTRAINT workspace_agents_id_key UNIQUE (id);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_id_key UNIQUE (id);
ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_workspace_id_name_key UNIQUE (workspace_id, name);
ALTER TABLE ONLY workspace_resources
ADD CONSTRAINT workspace_resources_id_key UNIQUE (id);
ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspaces
ADD CONSTRAINT workspaces_id_key UNIQUE (id);
ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id);
CREATE INDEX idx_api_keys_user ON api_keys USING btree (user_id);
CREATE INDEX idx_organization_member_organization_id_uuid ON organization_members USING btree (organization_id);
CREATE INDEX idx_organization_member_user_id_uuid ON organization_members USING btree (user_id);
CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name);
CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name));
CREATE UNIQUE INDEX idx_users_email ON users USING btree (email);
CREATE UNIQUE INDEX idx_users_username ON users USING btree (username);
CREATE UNIQUE INDEX projects_organization_id_name_idx ON projects USING btree (organization_id, name) WHERE (deleted = false);
CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username));
CREATE UNIQUE INDEX workspaces_owner_id_name_idx ON workspaces USING btree (owner_id, name) WHERE (deleted = false);
ALTER TABLE ONLY api_keys
ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY organization_members
ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY organization_members
ADD CONSTRAINT organization_members_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY parameter_schemas
ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_versions
ADD CONSTRAINT project_versions_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id);
ADD CONSTRAINT project_versions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_versions
ADD CONSTRAINT project_versions_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY projects
ADD CONSTRAINT projects_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_job_logs
ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_jobs
ADD CONSTRAINT provisioner_jobs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_agents
ADD CONSTRAINT workspace_agents_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE;
@ -375,5 +415,8 @@ ALTER TABLE ONLY workspace_resources
ADD CONSTRAINT workspace_resources_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY workspaces
ADD CONSTRAINT workspaces_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id);
ADD CONSTRAINT workspaces_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT;
ALTER TABLE ONLY workspaces
ADD CONSTRAINT workspaces_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE RESTRICT;

View File

@ -19,14 +19,17 @@ func main() {
panic(err)
}
defer closeFn()
db, err := sql.Open("postgres", connection)
if err != nil {
panic(err)
}
err = database.MigrateUp(db)
if err != nil {
panic(err)
}
cmd := exec.Command(
"pg_dump",
"--schema-only",
@ -39,10 +42,11 @@ func main() {
// queries executing against this table.
"--exclude-table=schema_migrations",
)
cmd.Env = []string{
cmd.Env = append(os.Environ(), []string{
"PGTZ=UTC",
"PGCLIENTENCODING=UTF8",
}
}...)
var output bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = os.Stderr
@ -57,7 +61,7 @@ func main() {
// Public is implicit in the schema.
"s/ public\\./ /",
// Remove database settings.
"s/SET.*;//g",
"s/SET .* = .*;//g",
// Remove select statements. These aren't useful
// to a reader of the dump.
"s/SELECT.*;//g",

View File

@ -0,0 +1,8 @@
DROP TABLE licenses;
DROP TABLE api_keys;
DROP TABLE organization_members;
DROP TABLE organizations;
DROP TABLE users;
DROP TYPE login_type;

View File

@ -4,28 +4,18 @@
-- All tables and types are stolen from:
-- https://github.com/coder/m/blob/47b6fc383347b9f9fab424d829c482defd3e1fe2/product/coder/pkg/database/dump.sql
DO $$ BEGIN
CREATE TYPE login_type AS ENUM (
'built-in',
'saml',
'oidc'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--
-- Name: users; Type: TABLE; Schema: public; Owner: coder
--
DO $$ BEGIN
CREATE TYPE userstatus AS ENUM (
'active',
'dormant',
'decommissioned'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE TYPE login_type AS ENUM (
'built-in',
'saml',
'oidc'
);
CREATE TABLE IF NOT EXISTS users (
id text NOT NULL,
id uuid NOT NULL,
email text NOT NULL,
name text NOT NULL,
revoked boolean NOT NULL,
@ -33,44 +23,51 @@ CREATE TABLE IF NOT EXISTS users (
hashed_password bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
temporary_password boolean DEFAULT false NOT NULL,
avatar_hash text DEFAULT '' :: text NOT NULL,
ssh_key_regenerated_at timestamp with time zone DEFAULT now() NOT NULL,
username text DEFAULT '' :: text NOT NULL,
dotfiles_git_uri text DEFAULT '' :: text NOT NULL,
roles text [] DEFAULT '{site-member}' :: text [] NOT NULL,
status userstatus DEFAULT 'active' :: public.userstatus NOT NULL,
relatime timestamp with time zone DEFAULT now() NOT NULL,
gpg_key_regenerated_at timestamp with time zone DEFAULT now() NOT NULL,
_decomissioned boolean DEFAULT false NOT NULL,
shell text DEFAULT '' :: text NOT NULL
username text DEFAULT ''::text NOT NULL,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email ON users USING btree (email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username ON users USING btree (username);
CREATE UNIQUE INDEX IF NOT EXISTS users_username_lower_idx ON users USING btree (lower(username));
--
-- Name: organizations; Type: TABLE; Schema: Owner: coder
--
CREATE TABLE IF NOT EXISTS organizations (
id text NOT NULL,
id uuid NOT NULL,
name text NOT NULL,
description text NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
"default" boolean DEFAULT false NOT NULL,
auto_off_threshold bigint DEFAULT '28800000000000' :: bigint NOT NULL,
cpu_provisioning_rate real DEFAULT 4.0 NOT NULL,
memory_provisioning_rate real DEFAULT 1.0 NOT NULL,
workspace_auto_off boolean DEFAULT false NOT NULL
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_organization_name ON organizations USING btree (name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_organization_name_lower ON organizations USING btree (lower(name));
CREATE TABLE IF NOT EXISTS organization_members (
organization_id text NOT NULL,
user_id text NOT NULL,
user_id uuid NOT NULL,
organization_id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
roles text [] DEFAULT '{organization-member}' :: text [] NOT NULL
roles text [] DEFAULT '{organization-member}' :: text [] NOT NULL,
PRIMARY KEY (organization_id, user_id)
);
CREATE INDEX idx_organization_member_organization_id_uuid ON organization_members USING btree (organization_id);
CREATE INDEX idx_organization_member_user_id_uuid ON organization_members USING btree (user_id);
ALTER TABLE ONLY organization_members
ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY organization_members
ADD CONSTRAINT organization_members_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
CREATE TABLE IF NOT EXISTS api_keys (
id text NOT NULL,
hashed_secret bytea NOT NULL,
user_id text NOT NULL,
user_id uuid NOT NULL,
application boolean NOT NULL,
name text NOT NULL,
last_used timestamp with time zone NOT NULL,
@ -82,11 +79,18 @@ CREATE TABLE IF NOT EXISTS api_keys (
oidc_refresh_token text DEFAULT ''::text NOT NULL,
oidc_id_token text DEFAULT ''::text NOT NULL,
oidc_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
devurl_token boolean DEFAULT false NOT NULL
devurl_token boolean DEFAULT false NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX IF NOT EXISTS idx_api_keys_user ON api_keys USING btree (user_id);
ALTER TABLE ONLY api_keys
ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
CREATE TABLE IF NOT EXISTS licenses (
id integer NOT NULL,
id serial,
license jsonb NOT NULL,
created_at timestamp with time zone NOT NULL
created_at timestamptz NOT NULL,
PRIMARY KEY (id)
);

View File

@ -1,10 +1,12 @@
-- Store arbitrary data like project source code or avatars.
CREATE TABLE files (
hash varchar(64) NOT NULL UNIQUE,
hash varchar(64) NOT NULL,
created_at timestamptz NOT NULL,
created_by text NOT NULL,
-- foreign key?
created_by uuid NOT NULL,
mimetype varchar(64) NOT NULL,
data bytea NOT NULL
data bytea NOT NULL,
PRIMARY KEY (hash)
);
CREATE TYPE provisioner_type AS ENUM ('echo', 'terraform');
@ -12,17 +14,18 @@ CREATE TYPE provisioner_type AS ENUM ('echo', 'terraform');
-- Project defines infrastructure that your software project
-- requires for development.
CREATE TABLE projects (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
-- Projects must be scoped to an organization.
organization_id text NOT NULL,
organization_id uuid NOT NULL REFERENCES organizations (id) ON DELETE CASCADE,
deleted boolean NOT NULL DEFAULT FALSE,
name varchar(64) NOT NULL,
provisioner provisioner_type NOT NULL,
-- Target's a Project Version to use for Workspaces.
-- If a Workspace doesn't match this version, it will be prompted to rebuild.
active_version_id uuid NOT NULL,
PRIMARY KEY (id),
-- Disallow projects to have the same name under
-- the same organization.
UNIQUE(organization_id, name)
@ -35,10 +38,10 @@ CREATE UNIQUE INDEX ON projects (organization_id, name) WHERE deleted = FALSE;
-- an "import" job is queued to parse parameters. A Project Version
-- can only be used if the import job succeeds.
CREATE TABLE project_versions (
id uuid NOT NULL UNIQUE,
-- This should be indexed.
project_id uuid REFERENCES projects (id),
organization_id text NOT NULL,
id uuid NOT NULL,
-- This should be indexed. It is intentionally nullable.
project_id uuid REFERENCES projects (id) ON DELETE CASCADE,
organization_id uuid NOT NULL REFERENCES organizations (id) ON DELETE CASCADE,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
-- Name is generated for ease of differentiation.
@ -49,6 +52,7 @@ CREATE TABLE project_versions (
description varchar(1048576) NOT NULL,
-- The job ID for building the project version.
job_id uuid NOT NULL,
PRIMARY KEY (id),
-- Disallow projects to have the same build name
-- multiple times.
UNIQUE(project_id, name)

View File

@ -1,11 +1,14 @@
CREATE TABLE workspaces (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
owner_id text NOT NULL,
project_id uuid NOT NULL REFERENCES projects (id),
-- Use ON DELETE RESTRICT so that we can cleanup external workspace
-- resources first.
owner_id uuid NOT NULL REFERENCES users (id) ON DELETE RESTRICT,
project_id uuid NOT NULL REFERENCES projects (id) ON DELETE RESTRICT,
deleted boolean NOT NULL DEFAULT FALSE,
name varchar(64) NOT NULL
name varchar(64) NOT NULL,
PRIMARY KEY (id)
);
-- Enforces no active workspaces have the same name.

View File

@ -1,12 +1,13 @@
CREATE TABLE IF NOT EXISTS provisioner_daemons (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz,
organization_id text,
organization_id uuid,
-- Name is generated for ease of differentiation.
-- eg. WowBananas16
name varchar(64) NOT NULL UNIQUE,
provisioners provisioner_type [ ] NOT NULL
provisioners provisioner_type [ ] NOT NULL,
PRIMARY KEY (id)
);
CREATE TYPE provisioner_job_type AS ENUM (
@ -17,21 +18,23 @@ CREATE TYPE provisioner_job_type AS ENUM (
CREATE TYPE provisioner_storage_method AS ENUM ('file');
CREATE TABLE IF NOT EXISTS provisioner_jobs (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
started_at timestamptz,
canceled_at timestamptz,
completed_at timestamptz,
error text,
organization_id text NOT NULL,
initiator_id text NOT NULL,
organization_id uuid NOT NULL REFERENCES organizations (id) ON DELETE CASCADE,
-- foreign key?
initiator_id uuid NOT NULL,
provisioner provisioner_type NOT NULL,
storage_method provisioner_storage_method NOT NULL,
storage_source text NOT NULL,
type provisioner_job_type NOT NULL,
input jsonb NOT NULL,
worker_id uuid
worker_id uuid,
PRIMARY KEY (id)
);
CREATE TYPE log_level AS ENUM (
@ -48,28 +51,30 @@ CREATE TYPE log_source AS ENUM (
);
CREATE TABLE IF NOT EXISTS provisioner_job_logs (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
created_at timestamptz NOT NULL,
source log_source NOT NULL,
level log_level NOT NULL,
stage varchar(128) NOT NULL,
output varchar(1024) NOT NULL
output varchar(1024) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE workspace_resources (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
transition workspace_transition NOT NULL,
address varchar(256) NOT NULL,
type varchar(192) NOT NULL,
name varchar(64) NOT NULL,
agent_id uuid
agent_id uuid,
PRIMARY KEY (id)
);
CREATE TABLE workspace_agents (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
first_connected_at timestamptz,
@ -81,7 +86,8 @@ CREATE TABLE workspace_agents (
environment_variables jsonb,
startup_script varchar(65534),
instance_metadata jsonb,
resource_metadata jsonb
resource_metadata jsonb,
PRIMARY KEY (id)
);
CREATE TYPE parameter_scope AS ENUM (
@ -111,7 +117,7 @@ CREATE TYPE parameter_destination_scheme AS ENUM('none', 'environment_variable',
-- a UI for users to enter values.
-- Needs to be made consistent with the examples below.
CREATE TABLE parameter_schemas (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
name varchar(64) NOT NULL,
@ -131,26 +137,28 @@ CREATE TABLE parameter_schemas (
validation_condition varchar(512) NOT NULL,
validation_type_system parameter_type_system NOT NULL,
validation_value_type varchar(64) NOT NULL,
PRIMARY KEY (id),
UNIQUE(job_id, name)
);
-- Parameters are provided to jobs for provisioning and to workspaces.
CREATE TABLE parameter_values (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
scope parameter_scope NOT NULL,
scope_id text NOT NULL,
scope_id uuid NOT NULL,
name varchar(64) NOT NULL,
source_scheme parameter_source_scheme NOT NULL,
source_value text NOT NULL,
destination_scheme parameter_destination_scheme NOT NULL,
PRIMARY KEY (id),
-- Prevents duplicates for parameters in the same scope.
UNIQUE(scope_id, name)
);
CREATE TABLE workspace_builds (
id uuid NOT NULL UNIQUE,
id uuid NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
workspace_id uuid NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
@ -159,10 +167,11 @@ CREATE TABLE workspace_builds (
before_id uuid,
after_id uuid,
transition workspace_transition NOT NULL,
initiator varchar(255) NOT NULL,
initiator_id uuid NOT NULL,
-- State stored by the provisioner
provisioner_state bytea,
-- Job ID of the action
job_id uuid NOT NULL UNIQUE REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
PRIMARY KEY (id),
UNIQUE(workspace_id, name)
);

View File

@ -209,26 +209,6 @@ func (e *ProvisionerType) Scan(src interface{}) error {
return nil
}
type UserStatus string
const (
UserstatusActive UserStatus = "active"
UserstatusDormant UserStatus = "dormant"
UserstatusDecommissioned UserStatus = "decommissioned"
)
func (e *UserStatus) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = UserStatus(s)
case string:
*e = UserStatus(s)
default:
return fmt.Errorf("unsupported scan type for UserStatus: %T", src)
}
return nil
}
type WorkspaceTransition string
const (
@ -252,7 +232,7 @@ func (e *WorkspaceTransition) Scan(src interface{}) error {
type APIKey struct {
ID string `db:"id" json:"id"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
UserID string `db:"user_id" json:"user_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
Application bool `db:"application" json:"application"`
Name string `db:"name" json:"name"`
LastUsed time.Time `db:"last_used" json:"last_used"`
@ -270,7 +250,7 @@ type APIKey struct {
type File struct {
Hash string `db:"hash" json:"hash"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
CreatedBy string `db:"created_by" json:"created_by"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Mimetype string `db:"mimetype" json:"mimetype"`
Data []byte `db:"data" json:"data"`
}
@ -282,21 +262,16 @@ type License struct {
}
type Organization struct {
ID string `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Default bool `db:"default" json:"default"`
AutoOffThreshold int64 `db:"auto_off_threshold" json:"auto_off_threshold"`
CpuProvisioningRate float32 `db:"cpu_provisioning_rate" json:"cpu_provisioning_rate"`
MemoryProvisioningRate float32 `db:"memory_provisioning_rate" json:"memory_provisioning_rate"`
WorkspaceAutoOff bool `db:"workspace_auto_off" json:"workspace_auto_off"`
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
type OrganizationMember struct {
OrganizationID string `db:"organization_id" json:"organization_id"`
UserID string `db:"user_id" json:"user_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Roles []string `db:"roles" json:"roles"`
@ -326,7 +301,7 @@ type ParameterValue struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Scope ParameterScope `db:"scope" json:"scope"`
ScopeID string `db:"scope_id" json:"scope_id"`
ScopeID uuid.UUID `db:"scope_id" json:"scope_id"`
Name string `db:"name" json:"name"`
SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"`
SourceValue string `db:"source_value" json:"source_value"`
@ -337,7 +312,7 @@ type Project struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OrganizationID string `db:"organization_id" json:"organization_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
@ -347,7 +322,7 @@ type Project struct {
type ProjectVersion struct {
ID uuid.UUID `db:"id" json:"id"`
ProjectID uuid.NullUUID `db:"project_id" json:"project_id"`
OrganizationID string `db:"organization_id" json:"organization_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
@ -359,7 +334,7 @@ type ProvisionerDaemon struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"`
OrganizationID sql.NullString `db:"organization_id" json:"organization_id"`
OrganizationID uuid.NullUUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"`
}
@ -372,8 +347,8 @@ type ProvisionerJob struct {
CanceledAt sql.NullTime `db:"canceled_at" json:"canceled_at"`
CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"`
Error sql.NullString `db:"error" json:"error"`
OrganizationID string `db:"organization_id" json:"organization_id"`
InitiatorID string `db:"initiator_id" json:"initiator_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"`
StorageSource string `db:"storage_source" json:"storage_source"`
@ -393,32 +368,22 @@ type ProvisionerJobLog struct {
}
type User struct {
ID string `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Name string `db:"name" json:"name"`
Revoked bool `db:"revoked" json:"revoked"`
LoginType LoginType `db:"login_type" json:"login_type"`
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
TemporaryPassword bool `db:"temporary_password" json:"temporary_password"`
AvatarHash string `db:"avatar_hash" json:"avatar_hash"`
SshKeyRegeneratedAt time.Time `db:"ssh_key_regenerated_at" json:"ssh_key_regenerated_at"`
Username string `db:"username" json:"username"`
DotfilesGitUri string `db:"dotfiles_git_uri" json:"dotfiles_git_uri"`
Roles []string `db:"roles" json:"roles"`
Status UserStatus `db:"status" json:"status"`
Relatime time.Time `db:"relatime" json:"relatime"`
GpgKeyRegeneratedAt time.Time `db:"gpg_key_regenerated_at" json:"gpg_key_regenerated_at"`
Decomissioned bool `db:"_decomissioned" json:"_decomissioned"`
Shell string `db:"shell" json:"shell"`
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Name string `db:"name" json:"name"`
Revoked bool `db:"revoked" json:"revoked"`
LoginType LoginType `db:"login_type" json:"login_type"`
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Username string `db:"username" json:"username"`
}
type Workspace struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OwnerID string `db:"owner_id" json:"owner_id"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
ProjectID uuid.UUID `db:"project_id" json:"project_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
@ -450,7 +415,7 @@ type WorkspaceBuild struct {
BeforeID uuid.NullUUID `db:"before_id" json:"before_id"`
AfterID uuid.NullUUID `db:"after_id" json:"after_id"`
Transition WorkspaceTransition `db:"transition" json:"transition"`
Initiator string `db:"initiator" json:"initiator"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net"
"os"
"strconv"
"sync"
"time"
@ -31,15 +32,18 @@ func Open() (string, func(), error) {
return "", nil, xerrors.Errorf("connect to ci postgres: %w", err)
}
defer db.Close()
dbName, err := cryptorand.StringCharset(cryptorand.Lower, 10)
if err != nil {
return "", nil, xerrors.Errorf("generate db name: %w", err)
}
dbName = "ci" + dbName
_, err = db.Exec("CREATE DATABASE " + dbName)
if err != nil {
return "", nil, xerrors.Errorf("create db: %w", err)
}
return "postgres://postgres:postgres@127.0.0.1:5432/" + dbName + "?sslmode=disable", func() {}, nil
}
@ -59,7 +63,7 @@ func Open() (string, func(), error) {
port, err := getFreePort()
if err != nil {
openPortMutex.Unlock()
return "", nil, xerrors.Errorf("Unable to get free port: %w", err)
return "", nil, xerrors.Errorf("get free port: %w", err)
}
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
@ -80,7 +84,8 @@ func Open() (string, func(), error) {
// https://github.com/moby/moby/issues/42442
// where the ipv4 and ipv6 ports might be _different_ and collide with other running docker containers.
HostIP: "0.0.0.0",
HostPort: fmt.Sprintf("%d", port)}},
HostPort: strconv.FormatInt(int64(port), 10),
}},
},
Mounts: []string{
// The postgres image has a VOLUME parameter in it's image.
@ -107,18 +112,23 @@ func Open() (string, func(), error) {
// Docker should hard-kill the container after 120 seconds.
err = resource.Expire(120)
if err != nil {
return "", nil, xerrors.Errorf("could not expire resource: %w", err)
return "", nil, xerrors.Errorf("expire resource: %w", err)
}
pool.MaxWait = 120 * time.Second
err = pool.Retry(func() error {
db, err := sql.Open("postgres", dbURL)
if err != nil {
return err
return xerrors.Errorf("open postgres: %w", err)
}
defer db.Close()
err = db.Ping()
_ = db.Close()
return err
if err != nil {
return xerrors.Errorf("ping postgres: %w", err)
}
return nil
})
if err != nil {
return "", nil, err

View File

@ -13,10 +13,10 @@ type querier interface {
DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error
GetAPIKeyByID(ctx context.Context, id string) (APIKey, error)
GetFileByHash(ctx context.Context, hash string) (File, error)
GetOrganizationByID(ctx context.Context, id string) (Organization, error)
GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error)
GetOrganizationByName(ctx context.Context, name string) (Organization, error)
GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error)
GetOrganizationsByUserID(ctx context.Context, userID string) ([]Organization, error)
GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error)
GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error)
GetParameterValueByScopeAndName(ctx context.Context, arg GetParameterValueByScopeAndNameParams) (ParameterValue, error)
GetParameterValuesByScope(ctx context.Context, arg GetParameterValuesByScopeParams) ([]ParameterValue, error)
@ -34,7 +34,7 @@ type querier interface {
GetProvisionerJobsByIDs(ctx context.Context, ids []uuid.UUID) ([]ProvisionerJob, error)
GetProvisionerLogsByIDBetween(ctx context.Context, arg GetProvisionerLogsByIDBetweenParams) ([]ProvisionerJobLog, error)
GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error)
GetUserByID(ctx context.Context, id string) (User, error)
GetUserByID(ctx context.Context, id uuid.UUID) (User, error)
GetUserCount(ctx context.Context) (int64, error)
GetWorkspaceAgentByAuthToken(ctx context.Context, authToken uuid.UUID) (WorkspaceAgent, error)
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)

View File

@ -683,7 +683,7 @@ INSERT INTO
before_id,
name,
transition,
initiator,
initiator_id,
job_id,
provisioner_state
)

View File

@ -148,14 +148,14 @@ func (q *sqlQuerier) GetFileByHash(ctx context.Context, hash string) (File, erro
const getOrganizationByID = `-- name: GetOrganizationByID :one
SELECT
id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off
id, name, description, created_at, updated_at
FROM
organizations
WHERE
id = $1
`
func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id string) (Organization, error) {
func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) {
row := q.db.QueryRowContext(ctx, getOrganizationByID, id)
var i Organization
err := row.Scan(
@ -164,18 +164,13 @@ func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id string) (Organi
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.Default,
&i.AutoOffThreshold,
&i.CpuProvisioningRate,
&i.MemoryProvisioningRate,
&i.WorkspaceAutoOff,
)
return i, err
}
const getOrganizationByName = `-- name: GetOrganizationByName :one
SELECT
id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off
id, name, description, created_at, updated_at
FROM
organizations
WHERE
@ -193,18 +188,13 @@ func (q *sqlQuerier) GetOrganizationByName(ctx context.Context, name string) (Or
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.Default,
&i.AutoOffThreshold,
&i.CpuProvisioningRate,
&i.MemoryProvisioningRate,
&i.WorkspaceAutoOff,
)
return i, err
}
const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one
SELECT
organization_id, user_id, created_at, updated_at, roles
user_id, organization_id, created_at, updated_at, roles
FROM
organization_members
WHERE
@ -215,16 +205,16 @@ LIMIT
`
type GetOrganizationMemberByUserIDParams struct {
OrganizationID string `db:"organization_id" json:"organization_id"`
UserID string `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) {
row := q.db.QueryRowContext(ctx, getOrganizationMemberByUserID, arg.OrganizationID, arg.UserID)
var i OrganizationMember
err := row.Scan(
&i.OrganizationID,
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
@ -234,7 +224,7 @@ func (q *sqlQuerier) GetOrganizationMemberByUserID(ctx context.Context, arg GetO
const getOrganizationsByUserID = `-- name: GetOrganizationsByUserID :many
SELECT
id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off
id, name, description, created_at, updated_at
FROM
organizations
WHERE
@ -248,7 +238,7 @@ WHERE
)
`
func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID string) ([]Organization, error) {
func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error) {
rows, err := q.db.QueryContext(ctx, getOrganizationsByUserID, userID)
if err != nil {
return nil, err
@ -263,11 +253,6 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID string
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.Default,
&i.AutoOffThreshold,
&i.CpuProvisioningRate,
&i.MemoryProvisioningRate,
&i.WorkspaceAutoOff,
); err != nil {
return nil, err
}
@ -346,7 +331,7 @@ LIMIT
type GetParameterValueByScopeAndNameParams struct {
Scope ParameterScope `db:"scope" json:"scope"`
ScopeID string `db:"scope_id" json:"scope_id"`
ScopeID uuid.UUID `db:"scope_id" json:"scope_id"`
Name string `db:"name" json:"name"`
}
@ -379,7 +364,7 @@ WHERE
type GetParameterValuesByScopeParams struct {
Scope ParameterScope `db:"scope" json:"scope"`
ScopeID string `db:"scope_id" json:"scope_id"`
ScopeID uuid.UUID `db:"scope_id" json:"scope_id"`
}
func (q *sqlQuerier) GetParameterValuesByScope(ctx context.Context, arg GetParameterValuesByScopeParams) ([]ParameterValue, error) {
@ -456,9 +441,9 @@ LIMIT
`
type GetProjectByOrganizationAndNameParams struct {
OrganizationID string `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetProjectByOrganizationAndName(ctx context.Context, arg GetProjectByOrganizationAndNameParams) (Project, error) {
@ -651,8 +636,8 @@ WHERE
`
type GetProjectsByOrganizationParams struct {
OrganizationID string `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
}
func (q *sqlQuerier) GetProjectsByOrganization(ctx context.Context, arg GetProjectsByOrganizationParams) ([]Project, error) {
@ -881,7 +866,7 @@ func (q *sqlQuerier) GetProvisionerLogsByIDBetween(ctx context.Context, arg GetP
const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
SELECT
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, temporary_password, avatar_hash, ssh_key_regenerated_at, username, dotfiles_git_uri, roles, status, relatime, gpg_key_regenerated_at, _decomissioned, shell
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
FROM
users
WHERE
@ -908,24 +893,14 @@ func (q *sqlQuerier) GetUserByEmailOrUsername(ctx context.Context, arg GetUserBy
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.TemporaryPassword,
&i.AvatarHash,
&i.SshKeyRegeneratedAt,
&i.Username,
&i.DotfilesGitUri,
pq.Array(&i.Roles),
&i.Status,
&i.Relatime,
&i.GpgKeyRegeneratedAt,
&i.Decomissioned,
&i.Shell,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, temporary_password, avatar_hash, ssh_key_regenerated_at, username, dotfiles_git_uri, roles, status, relatime, gpg_key_regenerated_at, _decomissioned, shell
id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
FROM
users
WHERE
@ -934,7 +909,7 @@ LIMIT
1
`
func (q *sqlQuerier) GetUserByID(ctx context.Context, id string) (User, error) {
func (q *sqlQuerier) GetUserByID(ctx context.Context, id uuid.UUID) (User, error) {
row := q.db.QueryRowContext(ctx, getUserByID, id)
var i User
err := row.Scan(
@ -946,17 +921,7 @@ func (q *sqlQuerier) GetUserByID(ctx context.Context, id string) (User, error) {
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.TemporaryPassword,
&i.AvatarHash,
&i.SshKeyRegeneratedAt,
&i.Username,
&i.DotfilesGitUri,
pq.Array(&i.Roles),
&i.Status,
&i.Relatime,
&i.GpgKeyRegeneratedAt,
&i.Decomissioned,
&i.Shell,
)
return i, err
}
@ -1071,7 +1036,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByResourceID(ctx context.Context, resource
const getWorkspaceBuildByID = `-- name: GetWorkspaceBuildByID :one
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1093,7 +1058,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
)
@ -1102,7 +1067,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W
const getWorkspaceBuildByJobID = `-- name: GetWorkspaceBuildByJobID :one
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1124,7 +1089,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
)
@ -1133,7 +1098,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU
const getWorkspaceBuildByWorkspaceID = `-- name: GetWorkspaceBuildByWorkspaceID :many
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1159,7 +1124,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceID(ctx context.Context, workspa
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
); err != nil {
@ -1178,7 +1143,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceID(ctx context.Context, workspa
const getWorkspaceBuildByWorkspaceIDAndName = `-- name: GetWorkspaceBuildByWorkspaceIDAndName :one
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1204,7 +1169,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndName(ctx context.Context,
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
)
@ -1213,7 +1178,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndName(ctx context.Context,
const getWorkspaceBuildByWorkspaceIDWithoutAfter = `-- name: GetWorkspaceBuildByWorkspaceIDWithoutAfter :one
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1236,7 +1201,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDWithoutAfter(ctx context.Cont
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
)
@ -1245,7 +1210,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDWithoutAfter(ctx context.Cont
const getWorkspaceBuildsByWorkspaceIDsWithoutAfter = `-- name: GetWorkspaceBuildsByWorkspaceIDsWithoutAfter :many
SELECT
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
FROM
workspace_builds
WHERE
@ -1272,7 +1237,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceIDsWithoutAfter(ctx context.Co
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
); err != nil {
@ -1327,9 +1292,9 @@ WHERE
`
type GetWorkspaceByUserIDAndNameParams struct {
OwnerID string `db:"owner_id" json:"owner_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetWorkspaceByUserIDAndName(ctx context.Context, arg GetWorkspaceByUserIDAndNameParams) (Workspace, error) {
@ -1511,8 +1476,8 @@ WHERE
`
type GetWorkspacesByUserIDParams struct {
OwnerID string `db:"owner_id" json:"owner_id"`
Deleted bool `db:"deleted" json:"deleted"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
Deleted bool `db:"deleted" json:"deleted"`
}
func (q *sqlQuerier) GetWorkspacesByUserID(ctx context.Context, arg GetWorkspacesByUserIDParams) ([]Workspace, error) {
@ -1588,7 +1553,7 @@ VALUES
type InsertAPIKeyParams struct {
ID string `db:"id" json:"id"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
UserID string `db:"user_id" json:"user_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
Application bool `db:"application" json:"application"`
Name string `db:"name" json:"name"`
LastUsed time.Time `db:"last_used" json:"last_used"`
@ -1652,7 +1617,7 @@ VALUES
type InsertFileParams struct {
Hash string `db:"hash" json:"hash"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
CreatedBy string `db:"created_by" json:"created_by"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Mimetype string `db:"mimetype" json:"mimetype"`
Data []byte `db:"data" json:"data"`
}
@ -1680,11 +1645,11 @@ const insertOrganization = `-- name: InsertOrganization :one
INSERT INTO
organizations (id, name, description, created_at, updated_at)
VALUES
($1, $2, $3, $4, $5) RETURNING id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off
($1, $2, $3, $4, $5) RETURNING id, name, description, created_at, updated_at
`
type InsertOrganizationParams struct {
ID string `db:"id" json:"id"`
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
@ -1706,11 +1671,6 @@ func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizat
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.Default,
&i.AutoOffThreshold,
&i.CpuProvisioningRate,
&i.MemoryProvisioningRate,
&i.WorkspaceAutoOff,
)
return i, err
}
@ -1725,12 +1685,12 @@ INSERT INTO
roles
)
VALUES
($1, $2, $3, $4, $5) RETURNING organization_id, user_id, created_at, updated_at, roles
($1, $2, $3, $4, $5) RETURNING user_id, organization_id, created_at, updated_at, roles
`
type InsertOrganizationMemberParams struct {
OrganizationID string `db:"organization_id" json:"organization_id"`
UserID string `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Roles []string `db:"roles" json:"roles"`
@ -1746,8 +1706,8 @@ func (q *sqlQuerier) InsertOrganizationMember(ctx context.Context, arg InsertOrg
)
var i OrganizationMember
err := row.Scan(
&i.OrganizationID,
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
@ -1879,7 +1839,7 @@ type InsertParameterValueParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Scope ParameterScope `db:"scope" json:"scope"`
ScopeID string `db:"scope_id" json:"scope_id"`
ScopeID uuid.UUID `db:"scope_id" json:"scope_id"`
SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"`
SourceValue string `db:"source_value" json:"source_value"`
DestinationScheme ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"`
@ -1931,7 +1891,7 @@ type InsertProjectParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OrganizationID string `db:"organization_id" json:"organization_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"`
@ -1980,7 +1940,7 @@ VALUES
type InsertProjectVersionParams struct {
ID uuid.UUID `db:"id" json:"id"`
ProjectID uuid.NullUUID `db:"project_id" json:"project_id"`
OrganizationID string `db:"organization_id" json:"organization_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
@ -2023,7 +1983,7 @@ VALUES
type InsertProvisionerDaemonParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
OrganizationID sql.NullString `db:"organization_id" json:"organization_id"`
OrganizationID uuid.NullUUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"`
}
@ -2070,8 +2030,8 @@ type InsertProvisionerJobParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OrganizationID string `db:"organization_id" json:"organization_id"`
InitiatorID string `db:"initiator_id" json:"initiator_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"`
StorageSource string `db:"storage_source" json:"storage_source"`
@ -2189,11 +2149,11 @@ INSERT INTO
username
)
VALUES
($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, temporary_password, avatar_hash, ssh_key_regenerated_at, username, dotfiles_git_uri, roles, status, relatime, gpg_key_regenerated_at, _decomissioned, shell
($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, username
`
type InsertUserParams struct {
ID string `db:"id" json:"id"`
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Name string `db:"name" json:"name"`
LoginType LoginType `db:"login_type" json:"login_type"`
@ -2224,17 +2184,7 @@ func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.TemporaryPassword,
&i.AvatarHash,
&i.SshKeyRegeneratedAt,
&i.Username,
&i.DotfilesGitUri,
pq.Array(&i.Roles),
&i.Status,
&i.Relatime,
&i.GpgKeyRegeneratedAt,
&i.Decomissioned,
&i.Shell,
)
return i, err
}
@ -2257,7 +2207,7 @@ type InsertWorkspaceParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OwnerID string `db:"owner_id" json:"owner_id"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
ProjectID uuid.UUID `db:"project_id" json:"project_id"`
Name string `db:"name" json:"name"`
}
@ -2358,12 +2308,12 @@ INSERT INTO
before_id,
name,
transition,
initiator,
initiator_id,
job_id,
provisioner_state
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator_id, provisioner_state, job_id
`
type InsertWorkspaceBuildParams struct {
@ -2375,7 +2325,7 @@ type InsertWorkspaceBuildParams struct {
BeforeID uuid.NullUUID `db:"before_id" json:"before_id"`
Name string `db:"name" json:"name"`
Transition WorkspaceTransition `db:"transition" json:"transition"`
Initiator string `db:"initiator" json:"initiator"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"`
}
@ -2390,7 +2340,7 @@ func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspa
arg.BeforeID,
arg.Name,
arg.Transition,
arg.Initiator,
arg.InitiatorID,
arg.JobID,
arg.ProvisionerState,
)
@ -2405,7 +2355,7 @@ func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspa
&i.BeforeID,
&i.AfterID,
&i.Transition,
&i.Initiator,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
)

View File

@ -19,6 +19,13 @@ func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID
})
return uuid.UUID{}, false
}
// Automatically set uuid.Nil to the acting users id.
if param == UserKey && rawID == "me" {
key := APIKey(r)
return key.UserID, true
}
parsed, err := uuid.Parse(rawID)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
@ -26,5 +33,6 @@ func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID
})
return uuid.UUID{}, false
}
return parsed, true
}

View File

@ -7,8 +7,6 @@ import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
)
@ -40,17 +38,15 @@ func OrganizationMemberParam(r *http.Request) database.OrganizationMember {
func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
organizationID := chi.URLParam(r, "organization")
if organizationID == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "organization must be provided",
})
orgID, ok := parseUUID(rw, r, "organization")
if !ok {
return
}
organization, err := db.GetOrganizationByID(r.Context(), organizationID)
organization, err := db.GetOrganizationByID(r.Context(), orgID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("organization %q does not exist", organizationID),
Message: fmt.Sprintf("organization %q does not exist", orgID),
})
return
}
@ -60,6 +56,7 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
})
return
}
apiKey := APIKey(r)
organizationMember, err := db.GetOrganizationMemberByUserID(r.Context(), database.GetOrganizationMemberByUserIDParams{
OrganizationID: organization.ID,

View File

@ -32,10 +32,11 @@ func TestOrganizationParam(t *testing.T) {
Name: httpmw.AuthCookie,
Value: fmt.Sprintf("%s-%s", id, secret),
})
userID, err := cryptorand.String(16)
require.NoError(t, err)
userID := uuid.New()
username, err := cryptorand.String(8)
require.NoError(t, err)
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
ID: userID,
Email: "testaccount@coder.com",
@ -86,7 +87,7 @@ func TestOrganizationParam(t *testing.T) {
r, _ = setupAuthentication(db)
rtr = chi.NewRouter()
)
chi.RouteContext(r.Context()).URLParams.Add("organization", "nothin")
chi.RouteContext(r.Context()).URLParams.Add("organization", uuid.NewString())
rtr.Use(
httpmw.ExtractAPIKey(db, nil),
httpmw.ExtractOrganizationParam(db),
@ -98,6 +99,26 @@ func TestOrganizationParam(t *testing.T) {
require.Equal(t, http.StatusNotFound, res.StatusCode)
})
t.Run("InvalidUUID", func(t *testing.T) {
t.Parallel()
var (
db = databasefake.New()
rw = httptest.NewRecorder()
r, _ = setupAuthentication(db)
rtr = chi.NewRouter()
)
chi.RouteContext(r.Context()).URLParams.Add("organization", "not-a-uuid")
rtr.Use(
httpmw.ExtractAPIKey(db, nil),
httpmw.ExtractOrganizationParam(db),
)
rtr.Get("/", nil)
rtr.ServeHTTP(rw, r)
res := rw.Result()
defer res.Body.Close()
require.Equal(t, http.StatusBadRequest, res.StatusCode)
})
t.Run("NotInOrganization", func(t *testing.T) {
t.Parallel()
var (
@ -107,13 +128,13 @@ func TestOrganizationParam(t *testing.T) {
rtr = chi.NewRouter()
)
organization, err := db.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: uuid.NewString(),
ID: uuid.New(),
Name: "test",
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
})
require.NoError(t, err)
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID)
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
rtr.Use(
httpmw.ExtractAPIKey(db, nil),
httpmw.ExtractOrganizationParam(db),
@ -134,7 +155,7 @@ func TestOrganizationParam(t *testing.T) {
rtr = chi.NewRouter()
)
organization, err := db.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: uuid.NewString(),
ID: uuid.New(),
Name: "test",
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
@ -147,7 +168,7 @@ func TestOrganizationParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID)
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
rtr.Use(
httpmw.ExtractAPIKey(db, nil),
httpmw.ExtractOrganizationParam(db),

View File

@ -46,7 +46,7 @@ func ExtractProjectParam(db database.Store) func(http.Handler) http.Handler {
}
ctx := context.WithValue(r.Context(), projectParamContextKey{}, project)
chi.RouteContext(ctx).URLParams.Add("organization", project.OrganizationID)
chi.RouteContext(ctx).URLParams.Add("organization", project.OrganizationID.String())
next.ServeHTTP(rw, r.WithContext(ctx))
})
}

View File

@ -32,8 +32,8 @@ func TestProjectParam(t *testing.T) {
Name: httpmw.AuthCookie,
Value: fmt.Sprintf("%s-%s", id, secret),
})
userID, err := cryptorand.String(16)
require.NoError(t, err)
userID := uuid.New()
username, err := cryptorand.String(8)
require.NoError(t, err)
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
@ -47,6 +47,7 @@ func TestProjectParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
ID: id,
UserID: user.ID,
@ -55,8 +56,8 @@ func TestProjectParam(t *testing.T) {
ExpiresAt: database.Now().Add(time.Minute),
})
require.NoError(t, err)
orgID, err := cryptorand.String(16)
require.NoError(t, err)
orgID := uuid.New()
organization, err := db.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: orgID,
Name: "banana",
@ -65,6 +66,7 @@ func TestProjectParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertOrganizationMember(r.Context(), database.InsertOrganizationMemberParams{
OrganizationID: orgID,
UserID: user.ID,

View File

@ -47,7 +47,7 @@ func ExtractProjectVersionParam(db database.Store) func(http.Handler) http.Handl
}
ctx := context.WithValue(r.Context(), projectVersionParamContextKey{}, projectVersion)
chi.RouteContext(ctx).URLParams.Add("organization", projectVersion.OrganizationID)
chi.RouteContext(ctx).URLParams.Add("organization", projectVersion.OrganizationID.String())
next.ServeHTTP(rw, r.WithContext(ctx))
})
}

View File

@ -32,8 +32,8 @@ func TestProjectVersionParam(t *testing.T) {
Name: httpmw.AuthCookie,
Value: fmt.Sprintf("%s-%s", id, secret),
})
userID, err := cryptorand.String(16)
require.NoError(t, err)
userID := uuid.New()
username, err := cryptorand.String(8)
require.NoError(t, err)
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
@ -47,6 +47,7 @@ func TestProjectVersionParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
ID: id,
UserID: user.ID,
@ -55,8 +56,8 @@ func TestProjectVersionParam(t *testing.T) {
ExpiresAt: database.Now().Add(time.Minute),
})
require.NoError(t, err)
orgID, err := cryptorand.String(16)
require.NoError(t, err)
orgID := uuid.New()
organization, err := db.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: orgID,
Name: "banana",
@ -65,6 +66,7 @@ func TestProjectVersionParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertOrganizationMember(r.Context(), database.InsertOrganizationMemberParams{
OrganizationID: orgID,
UserID: user.ID,
@ -72,6 +74,7 @@ func TestProjectVersionParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
project, err := db.InsertProject(context.Background(), database.InsertProjectParams{
ID: uuid.New(),
OrganizationID: organization.ID,

View File

@ -5,12 +5,12 @@ import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
)
const UserKey = "user"
type userParamContextKey struct{}
// UserParam returns the user from the ExtractUserParam handler.
@ -26,21 +26,20 @@ func UserParam(r *http.Request) database.User {
func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "user")
if userID == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "user id or name must be provided",
})
userID, ok := parseUUID(rw, r, UserKey)
if !ok {
return
}
apiKey := APIKey(r)
if apiKey.UserID != userID && userID != "me" {
if apiKey.UserID != userID {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "getting non-personal users isn't supported yet",
})
return
}
user, err := db.GetUserByID(r.Context(), apiKey.UserID)
user, err := db.GetUserByID(r.Context(), userID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err.Error()),

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database"
@ -32,18 +33,20 @@ func TestUserParam(t *testing.T) {
Value: fmt.Sprintf("%s-%s", id, secret),
})
_, err := db.InsertUser(r.Context(), database.InsertUserParams{
ID: "bananas",
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
ID: uuid.New(),
})
require.NoError(t, err)
_, err = db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
ID: id,
UserID: "bananas",
UserID: user.ID,
HashedSecret: hashed[:],
LastUsed: database.Now(),
ExpiresAt: database.Now().Add(time.Minute),
})
require.NoError(t, err)
return db, rw, r
}
@ -82,7 +85,7 @@ func TestUserParam(t *testing.T) {
require.Equal(t, http.StatusBadRequest, res.StatusCode)
})
t.Run("Me", func(t *testing.T) {
t.Run("me", func(t *testing.T) {
t.Parallel()
db, rw, r := setup(t)

View File

@ -32,8 +32,8 @@ func TestWorkspaceBuildParam(t *testing.T) {
Name: httpmw.AuthCookie,
Value: fmt.Sprintf("%s-%s", id, secret),
})
userID, err := cryptorand.String(16)
require.NoError(t, err)
userID := uuid.New()
username, err := cryptorand.String(8)
require.NoError(t, err)
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
@ -47,6 +47,7 @@ func TestWorkspaceBuildParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
ID: id,
UserID: user.ID,
@ -55,6 +56,7 @@ func TestWorkspaceBuildParam(t *testing.T) {
ExpiresAt: database.Now().Add(time.Minute),
})
require.NoError(t, err)
workspace, err := db.InsertWorkspace(context.Background(), database.InsertWorkspaceParams{
ID: uuid.New(),
ProjectID: uuid.New(),
@ -64,7 +66,7 @@ func TestWorkspaceBuildParam(t *testing.T) {
require.NoError(t, err)
ctx := chi.NewRouteContext()
ctx.URLParams.Add("user", userID)
ctx.URLParams.Add("user", userID.String())
ctx.URLParams.Add("workspace", workspace.Name)
r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, ctx))
return r, workspace

View File

@ -32,8 +32,8 @@ func TestWorkspaceParam(t *testing.T) {
Name: httpmw.AuthCookie,
Value: fmt.Sprintf("%s-%s", id, secret),
})
userID, err := cryptorand.String(16)
require.NoError(t, err)
userID := uuid.New()
username, err := cryptorand.String(8)
require.NoError(t, err)
user, err := db.InsertUser(r.Context(), database.InsertUserParams{
@ -47,6 +47,7 @@ func TestWorkspaceParam(t *testing.T) {
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
ID: id,
UserID: user.ID,
@ -105,7 +106,7 @@ func TestWorkspaceParam(t *testing.T) {
r, _ := setup(db)
workspace, err := db.InsertWorkspace(context.Background(), database.InsertWorkspaceParams{
ID: uuid.New(),
OwnerID: "not-me",
OwnerID: uuid.New(),
Name: "hello",
})
require.NoError(t, err)

View File

@ -91,7 +91,7 @@ func (api *api) postProjectVersionsByOrganization(rw http.ResponseWriter, r *htt
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Scope: database.ParameterScopeImportJob,
ScopeID: jobID.String(),
ScopeID: jobID,
SourceScheme: parameterValue.SourceScheme,
SourceValue: parameterValue.SourceValue,
DestinationScheme: parameterValue.DestinationScheme,
@ -200,10 +200,11 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
var project codersdk.Project
err = api.Database.InTx(func(db database.Store) error {
now := database.Now()
dbProject, err := db.InsertProject(r.Context(), database.InsertProjectParams{
ID: uuid.New(),
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
CreatedAt: now,
UpdatedAt: now,
OrganizationID: organization.ID,
Name: createProject.Name,
Provisioner: importJob.Provisioner,
@ -212,6 +213,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
if err != nil {
return xerrors.Errorf("insert project: %s", err)
}
err = db.UpdateProjectVersionByID(r.Context(), database.UpdateProjectVersionByIDParams{
ID: projectVersion.ID,
ProjectID: uuid.NullUUID{
@ -222,6 +224,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
if err != nil {
return xerrors.Errorf("insert project version: %s", err)
}
for _, parameterValue := range createProject.ParameterValues {
_, err = db.InsertParameterValue(r.Context(), database.InsertParameterValueParams{
ID: uuid.New(),
@ -229,7 +232,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Scope: database.ParameterScopeProject,
ScopeID: dbProject.ID.String(),
ScopeID: dbProject.ID,
SourceScheme: parameterValue.SourceScheme,
SourceValue: parameterValue.SourceValue,
DestinationScheme: parameterValue.DestinationScheme,
@ -238,6 +241,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
return xerrors.Errorf("insert parameter value: %w", err)
}
}
project = convertProject(dbProject, 0)
return nil
})
@ -291,18 +295,20 @@ func (api *api) projectByOrganizationAndName(rw http.ResponseWriter, r *http.Req
OrganizationID: organization.ID,
Name: projectName,
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("no project found by name %q in the %q organization", projectName, organization.Name),
})
return
}
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("no project found by name %q in the %q organization", projectName, organization.Name),
})
return
}
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get project by organization and name: %s", err),
})
return
}
workspaceCounts, err := api.Database.GetWorkspaceOwnerCountsByProjectIDs(r.Context(), []uuid.UUID{project.ID})
if errors.Is(err, sql.ErrNoRows) {
err = nil
@ -313,10 +319,12 @@ func (api *api) projectByOrganizationAndName(rw http.ResponseWriter, r *http.Req
})
return
}
count := uint32(0)
if len(workspaceCounts) > 0 {
count = uint32(workspaceCounts[0].Count)
}
render.Status(r, http.StatusOK)
render.JSON(rw, r, convertProject(project, count))
}

View File

@ -19,7 +19,7 @@ func TestProvisionerDaemonsByOrganization(t *testing.T) {
t.Run("NoAuth", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_, err := client.ProvisionerDaemonsByOrganization(context.Background(), "someorg")
_, err := client.ProvisionerDaemonsByOrganization(context.Background(), uuid.New())
require.Error(t, err)
})

View File

@ -14,8 +14,8 @@ import (
// ComputeScope targets identifiers to pull parameters from.
type ComputeScope struct {
ProjectImportJobID uuid.UUID
OrganizationID string
UserID string
OrganizationID uuid.UUID
UserID uuid.UUID
ProjectID uuid.NullUUID
WorkspaceID uuid.NullUUID
}
@ -71,7 +71,7 @@ func Compute(ctx context.Context, db database.Store, scope ComputeScope, options
// Job parameters come second!
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
Scope: database.ParameterScopeImportJob,
ScopeID: scope.ProjectImportJobID.String(),
ScopeID: scope.ProjectImportJobID,
})
if err != nil {
return nil, err
@ -101,7 +101,7 @@ func Compute(ctx context.Context, db database.Store, scope ComputeScope, options
DestinationScheme: parameterSchema.DefaultDestinationScheme,
SourceValue: parameterSchema.DefaultSourceValue,
Scope: database.ParameterScopeImportJob,
ScopeID: scope.ProjectImportJobID.String(),
ScopeID: scope.ProjectImportJobID,
}, true)
if err != nil {
return nil, xerrors.Errorf("insert default value: %w", err)
@ -115,7 +115,7 @@ func Compute(ctx context.Context, db database.Store, scope ComputeScope, options
// Project parameters come third!
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
Scope: database.ParameterScopeProject,
ScopeID: scope.ProjectID.UUID.String(),
ScopeID: scope.ProjectID.UUID,
})
if err != nil {
return nil, err
@ -135,7 +135,7 @@ func Compute(ctx context.Context, db database.Store, scope ComputeScope, options
// Workspace parameters come last!
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
Scope: database.ParameterScopeWorkspace,
ScopeID: scope.WorkspaceID.UUID.String(),
ScopeID: scope.WorkspaceID.UUID,
})
if err != nil {
return nil, err

View File

@ -18,7 +18,7 @@ func TestCompute(t *testing.T) {
generateScope := func() parameter.ComputeScope {
return parameter.ComputeScope{
ProjectImportJobID: uuid.New(),
OrganizationID: uuid.NewString(),
OrganizationID: uuid.New(),
ProjectID: uuid.NullUUID{
UUID: uuid.New(),
Valid: true,
@ -27,7 +27,7 @@ func TestCompute(t *testing.T) {
UUID: uuid.New(),
Valid: true,
},
UserID: uuid.NewString(),
UserID: uuid.New(),
}
}
type parameterOptions struct {
@ -88,7 +88,7 @@ func TestCompute(t *testing.T) {
computedValue := computed[0]
require.True(t, computedValue.DefaultSourceValue)
require.Equal(t, database.ParameterScopeImportJob, computedValue.Scope)
require.Equal(t, scope.ProjectImportJobID.String(), computedValue.ScopeID)
require.Equal(t, scope.ProjectImportJobID, computedValue.ScopeID)
require.Equal(t, computedValue.SourceValue, parameterSchema.DefaultSourceValue)
})
@ -114,7 +114,7 @@ func TestCompute(t *testing.T) {
ID: uuid.New(),
Name: parameterSchema.Name,
Scope: database.ParameterScopeImportJob,
ScopeID: scope.ProjectImportJobID.String(),
ScopeID: scope.ProjectImportJobID,
SourceScheme: database.ParameterSourceSchemeData,
SourceValue: "secondnop",
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
@ -139,7 +139,7 @@ func TestCompute(t *testing.T) {
ID: uuid.New(),
Name: parameterSchema.Name,
Scope: database.ParameterScopeProject,
ScopeID: scope.ProjectID.UUID.String(),
ScopeID: scope.ProjectID.UUID,
SourceScheme: database.ParameterSourceSchemeData,
SourceValue: "nop",
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
@ -160,11 +160,12 @@ func TestCompute(t *testing.T) {
parameterSchema := generateParameter(t, db, parameterOptions{
ProjectImportJobID: scope.ProjectImportJobID,
})
_, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
ID: uuid.New(),
Name: parameterSchema.Name,
Scope: database.ParameterScopeWorkspace,
ScopeID: scope.WorkspaceID.UUID.String(),
ScopeID: scope.WorkspaceID.UUID,
SourceScheme: database.ParameterSourceSchemeData,
SourceValue: "nop",
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
@ -189,7 +190,7 @@ func TestCompute(t *testing.T) {
ID: uuid.New(),
Name: parameterSchema.Name,
Scope: database.ParameterScopeWorkspace,
ScopeID: scope.WorkspaceID.UUID.String(),
ScopeID: scope.WorkspaceID.UUID,
SourceScheme: database.ParameterSourceSchemeData,
SourceValue: "nop",
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,

View File

@ -138,7 +138,7 @@ func convertParameterValue(parameterValue database.ParameterValue) codersdk.Para
}
}
func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.ParameterScope, string, bool) {
func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.ParameterScope, uuid.UUID, bool) {
var scope database.ParameterScope
switch chi.URLParam(r, "scope") {
case string(codersdk.ParameterOrganization):
@ -153,8 +153,17 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("invalid scope %q", scope),
})
return scope, "", false
return scope, uuid.Nil, false
}
return scope, chi.URLParam(r, "id"), true
id := chi.URLParam(r, "id")
uid, err := uuid.Parse(id)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("invalid uuid %q: %s", id, err),
})
return scope, uuid.Nil, false
}
return scope, uid, true
}

View File

@ -47,7 +47,7 @@ func TestDeleteProject(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
coderdtest.CreateWorkspace(t, client, "me", project.ID)
coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
err := client.DeleteProject(context.Background(), project.ID)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)

View File

@ -9,6 +9,7 @@ import (
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
)
@ -37,7 +38,7 @@ func TestProvisionerJobLogs(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
before := time.Now().UTC()
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
@ -75,7 +76,7 @@ func TestProvisionerJobLogs(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
before := database.Now()
ctx, cancelFunc := context.WithCancel(context.Background())
t.Cleanup(cancelFunc)
@ -111,7 +112,7 @@ func TestProvisionerJobLogs(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
logs, err := client.WorkspaceBuildLogsBefore(context.Background(), workspace.LatestBuild.ID, time.Now())
require.NoError(t, err)

View File

@ -32,12 +32,14 @@ func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) {
})
return
}
if userCount == 0 {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "The initial user has not been created!",
})
return
}
httpapi.Write(rw, http.StatusOK, httpapi.Response{
Message: "The initial user has already been created!",
})
@ -49,6 +51,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
if !httpapi.Read(rw, r, &createUser) {
return
}
// This should only function for the first user.
userCount, err := api.Database.GetUserCount(r.Context())
if err != nil {
@ -57,6 +60,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
})
return
}
// If a user already exists, the initial admin user no longer can be created.
if userCount != 0 {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
@ -64,6 +68,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
})
return
}
hashedPassword, err := userpassword.Hash(createUser.Password)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
@ -77,7 +82,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
var organization database.Organization
err = api.Database.InTx(func(s database.Store) error {
user, err = api.Database.InsertUser(r.Context(), database.InsertUserParams{
ID: uuid.NewString(),
ID: uuid.New(),
Email: createUser.Email,
HashedPassword: []byte(hashedPassword),
Username: createUser.Username,
@ -89,8 +94,8 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
return xerrors.Errorf("create user: %w", err)
}
organization, err = api.Database.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: uuid.NewString(),
Name: createUser.Organization,
ID: uuid.New(),
Name: createUser.OrganizationName,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
})
@ -190,7 +195,7 @@ func (api *api) postUsers(rw http.ResponseWriter, r *http.Request) {
var user database.User
err = api.Database.InTx(func(db database.Store) error {
user, err = db.InsertUser(r.Context(), database.InsertUserParams{
ID: uuid.NewString(),
ID: uuid.New(),
Email: createUser.Email,
HashedPassword: []byte(hashedPassword),
Username: createUser.Username,
@ -317,7 +322,7 @@ func (api *api) postOrganizationsByUser(rw http.ResponseWriter, r *http.Request)
var organization database.Organization
err = api.Database.InTx(func(db database.Store) error {
organization, err = api.Database.InsertOrganization(r.Context(), database.InsertOrganizationParams{
ID: uuid.NewString(),
ID: uuid.New(),
Name: req.Name,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
@ -611,7 +616,7 @@ func (api *api) postWorkspacesByUser(rw http.ResponseWriter, r *http.Request) {
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Scope: database.ParameterScopeWorkspace,
ScopeID: workspace.ID.String(),
ScopeID: workspace.ID,
SourceScheme: parameterValue.SourceScheme,
SourceValue: parameterValue.SourceValue,
DestinationScheme: parameterValue.DestinationScheme,
@ -649,7 +654,7 @@ func (api *api) postWorkspacesByUser(rw http.ResponseWriter, r *http.Request) {
WorkspaceID: workspace.ID,
ProjectVersionID: projectVersion.ID,
Name: namesgenerator.GetRandomName(1),
Initiator: apiKey.UserID,
InitiatorID: apiKey.UserID,
Transition: database.WorkspaceTransitionStart,
JobID: provisionerJob.ID,
})
@ -725,35 +730,35 @@ func (api *api) workspacesByUser(rw http.ResponseWriter, r *http.Request) {
return
}
buildByWorkspaceID := map[string]database.WorkspaceBuild{}
buildByWorkspaceID := map[uuid.UUID]database.WorkspaceBuild{}
for _, workspaceBuild := range workspaceBuilds {
buildByWorkspaceID[workspaceBuild.WorkspaceID.String()] = workspaceBuild
buildByWorkspaceID[workspaceBuild.WorkspaceID] = workspaceBuild
}
projectByID := map[string]database.Project{}
projectByID := map[uuid.UUID]database.Project{}
for _, project := range projects {
projectByID[project.ID.String()] = project
projectByID[project.ID] = project
}
jobByID := map[string]database.ProvisionerJob{}
jobByID := map[uuid.UUID]database.ProvisionerJob{}
for _, job := range jobs {
jobByID[job.ID.String()] = job
jobByID[job.ID] = job
}
apiWorkspaces := make([]codersdk.Workspace, 0, len(workspaces))
for _, workspace := range workspaces {
build, exists := buildByWorkspaceID[workspace.ID.String()]
build, exists := buildByWorkspaceID[workspace.ID]
if !exists {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("build not found for workspace %q", workspace.Name),
})
return
}
project, exists := projectByID[workspace.ProjectID.String()]
project, exists := projectByID[workspace.ProjectID]
if !exists {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("project not found for workspace %q", workspace.Name),
})
return
}
job, exists := jobByID[build.JobID.String()]
job, exists := jobByID[build.JobID]
if !exists {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("build job not found for workspace %q", workspace.Name),

View File

@ -27,10 +27,10 @@ func TestFirstUser(t *testing.T) {
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
_, err := client.CreateFirstUser(context.Background(), codersdk.CreateFirstUserRequest{
Email: "some@email.com",
Username: "exampleuser",
Password: "password",
Organization: "someorg",
Email: "some@email.com",
Username: "exampleuser",
Password: "password",
OrganizationName: "someorg",
})
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
@ -62,10 +62,10 @@ func TestPostLogin(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
req := codersdk.CreateFirstUserRequest{
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
Organization: "testorg",
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
OrganizationName: "testorg",
}
_, err := client.CreateFirstUser(context.Background(), req)
require.NoError(t, err)
@ -82,10 +82,10 @@ func TestPostLogin(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
req := codersdk.CreateFirstUserRequest{
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
Organization: "testorg",
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
OrganizationName: "testorg",
}
_, err := client.CreateFirstUser(context.Background(), req)
require.NoError(t, err)
@ -137,13 +137,13 @@ func TestPostUsers(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)
me, err := client.User(context.Background(), "")
me, err := client.User(context.Background(), codersdk.Me)
require.NoError(t, err)
_, err = client.CreateUser(context.Background(), codersdk.CreateUserRequest{
Email: me.Email,
Username: me.Username,
Password: "password",
OrganizationID: "someorg",
OrganizationID: uuid.New(),
})
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
@ -155,7 +155,7 @@ func TestPostUsers(t *testing.T) {
client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)
_, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
OrganizationID: "not-exists",
OrganizationID: uuid.New(),
Email: "another@user.org",
Username: "someone-else",
Password: "testing",
@ -170,7 +170,7 @@ func TestPostUsers(t *testing.T) {
client := coderdtest.New(t, nil)
first := coderdtest.CreateFirstUser(t, client)
other := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
org, err := other.CreateOrganization(context.Background(), "", codersdk.CreateOrganizationRequest{
org, err := other.CreateOrganization(context.Background(), codersdk.Me, codersdk.CreateOrganizationRequest{
Name: "another",
})
require.NoError(t, err)
@ -204,7 +204,7 @@ func TestUserByName(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
_, err := client.User(context.Background(), "")
_, err := client.User(context.Background(), codersdk.Me)
require.NoError(t, err)
}
@ -212,7 +212,7 @@ func TestOrganizationsByUser(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
orgs, err := client.OrganizationsByUser(context.Background(), "")
orgs, err := client.OrganizationsByUser(context.Background(), codersdk.Me)
require.NoError(t, err)
require.NotNil(t, orgs)
require.Len(t, orgs, 1)
@ -224,7 +224,7 @@ func TestOrganizationByUserAndName(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)
_, err := client.OrganizationByName(context.Background(), "", "nothing")
_, err := client.OrganizationByName(context.Background(), codersdk.Me, "nothing")
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
@ -235,11 +235,11 @@ func TestOrganizationByUserAndName(t *testing.T) {
client := coderdtest.New(t, nil)
first := coderdtest.CreateFirstUser(t, client)
other := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
org, err := other.CreateOrganization(context.Background(), "", codersdk.CreateOrganizationRequest{
org, err := other.CreateOrganization(context.Background(), codersdk.Me, codersdk.CreateOrganizationRequest{
Name: "another",
})
require.NoError(t, err)
_, err = client.OrganizationByName(context.Background(), "", org.Name)
_, err = client.OrganizationByName(context.Background(), codersdk.Me, org.Name)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
@ -251,7 +251,7 @@ func TestOrganizationByUserAndName(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
org, err := client.Organization(context.Background(), user.OrganizationID)
require.NoError(t, err)
_, err = client.OrganizationByName(context.Background(), "", org.Name)
_, err = client.OrganizationByName(context.Background(), codersdk.Me, org.Name)
require.NoError(t, err)
})
}
@ -264,7 +264,7 @@ func TestPostOrganizationsByUser(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
org, err := client.Organization(context.Background(), user.OrganizationID)
require.NoError(t, err)
_, err = client.CreateOrganization(context.Background(), "", codersdk.CreateOrganizationRequest{
_, err = client.CreateOrganization(context.Background(), codersdk.Me, codersdk.CreateOrganizationRequest{
Name: org.Name,
})
var apiErr *codersdk.Error
@ -276,7 +276,7 @@ func TestPostOrganizationsByUser(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
_, err := client.CreateOrganization(context.Background(), "", codersdk.CreateOrganizationRequest{
_, err := client.CreateOrganization(context.Background(), codersdk.Me, codersdk.CreateOrganizationRequest{
Name: "new",
})
require.NoError(t, err)
@ -291,7 +291,7 @@ func TestPostAPIKey(t *testing.T) {
_ = coderdtest.CreateFirstUser(t, client)
client.SessionToken = ""
_, err := client.CreateAPIKey(context.Background(), "")
_, err := client.CreateAPIKey(context.Background(), codersdk.Me)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
@ -301,7 +301,7 @@ func TestPostAPIKey(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
apiKey, err := client.CreateAPIKey(context.Background(), "")
apiKey, err := client.CreateAPIKey(context.Background(), codersdk.Me)
require.NotNil(t, apiKey)
require.GreaterOrEqual(t, len(apiKey.Key), 2)
require.NoError(t, err)
@ -314,7 +314,7 @@ func TestPostWorkspacesByUser(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
_, err := client.CreateWorkspace(context.Background(), "", codersdk.CreateWorkspaceRequest{
_, err := client.CreateWorkspace(context.Background(), codersdk.Me, codersdk.CreateWorkspaceRequest{
ProjectID: uuid.New(),
Name: "workspace",
})
@ -330,14 +330,14 @@ func TestPostWorkspacesByUser(t *testing.T) {
first := coderdtest.CreateFirstUser(t, client)
other := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
org, err := other.CreateOrganization(context.Background(), "", codersdk.CreateOrganizationRequest{
org, err := other.CreateOrganization(context.Background(), codersdk.Me, codersdk.CreateOrganizationRequest{
Name: "another",
})
require.NoError(t, err)
version := coderdtest.CreateProjectVersion(t, other, org.ID, nil)
project := coderdtest.CreateProject(t, other, org.ID, version.ID)
_, err = client.CreateWorkspace(context.Background(), "", codersdk.CreateWorkspaceRequest{
_, err = client.CreateWorkspace(context.Background(), codersdk.Me, codersdk.CreateWorkspaceRequest{
ProjectID: project.ID,
Name: "workspace",
})
@ -355,8 +355,8 @@ func TestPostWorkspacesByUser(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
_, err := client.CreateWorkspace(context.Background(), "", codersdk.CreateWorkspaceRequest{
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.CreateWorkspace(context.Background(), codersdk.Me, codersdk.CreateWorkspaceRequest{
ProjectID: project.ID,
Name: workspace.Name,
})
@ -374,7 +374,7 @@ func TestPostWorkspacesByUser(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
_ = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
})
}
@ -384,7 +384,7 @@ func TestWorkspacesByUser(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)
_, err := client.WorkspacesByUser(context.Background(), "")
_, err := client.WorkspacesByUser(context.Background(), codersdk.Me)
require.NoError(t, err)
})
t.Run("List", func(t *testing.T) {
@ -395,8 +395,8 @@ func TestWorkspacesByUser(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
workspaces, err := client.WorkspacesByUser(context.Background(), "")
_ = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
workspaces, err := client.WorkspacesByUser(context.Background(), codersdk.Me)
require.NoError(t, err)
require.Len(t, workspaces, 1)
})
@ -408,7 +408,7 @@ func TestWorkspaceByUserAndName(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)
_, err := client.WorkspaceByName(context.Background(), "", "something")
_, err := client.WorkspaceByName(context.Background(), codersdk.Me, "something")
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
@ -421,8 +421,8 @@ func TestWorkspaceByUserAndName(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
_, err := client.WorkspaceByName(context.Background(), "", workspace.Name)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.WorkspaceByName(context.Background(), codersdk.Me, workspace.Name)
require.NoError(t, err)
})
}

View File

@ -101,7 +101,7 @@ func convertWorkspaceBuild(workspaceBuild database.WorkspaceBuild, job codersdk.
AfterID: workspaceBuild.AfterID.UUID,
Name: workspaceBuild.Name,
Transition: workspaceBuild.Transition,
Initiator: workspaceBuild.Initiator,
InitiatorID: workspaceBuild.InitiatorID,
Job: job,
}
}

View File

@ -22,7 +22,7 @@ func TestWorkspaceBuild(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.WorkspaceBuild(context.Background(), workspace.LatestBuild.ID)
require.NoError(t, err)
}
@ -43,7 +43,7 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
var build codersdk.WorkspaceBuild
require.Eventually(t, func() bool {
var err error
@ -72,7 +72,7 @@ func TestWorkspaceBuildResources(t *testing.T) {
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
closeDaemon.Close()
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.WorkspaceResourcesByBuild(context.Background(), workspace.LatestBuild.ID)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
@ -105,7 +105,7 @@ func TestWorkspaceBuildResources(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
resources, err := client.WorkspaceResourcesByBuild(context.Background(), workspace.LatestBuild.ID)
require.NoError(t, err)
@ -152,7 +152,7 @@ func TestWorkspaceBuildLogs(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
ctx, cancelFunc := context.WithCancel(context.Background())
t.Cleanup(cancelFunc)
logs, err := client.WorkspaceBuildLogsAfter(ctx, workspace.LatestBuild.ID, before)

View File

@ -44,7 +44,7 @@ func TestPostWorkspaceAuthAWSInstanceIdentity(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
client.HTTPClient = metadataClient
@ -110,7 +110,7 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
_, err := client.AuthWorkspaceGoogleInstanceIdentity(context.Background(), "", metadata)

View File

@ -44,7 +44,7 @@ func TestWorkspaceResource(t *testing.T) {
})
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
resources, err := client.WorkspaceResourcesByBuild(context.Background(), workspace.LatestBuild.ID)
require.NoError(t, err)
@ -81,7 +81,7 @@ func TestWorkspaceAgentListen(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
daemonCloser.Close()

View File

@ -222,7 +222,7 @@ func (api *api) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
BeforeID: priorHistoryID,
Name: namesgenerator.GetRandomName(1),
ProvisionerState: priorHistory.ProvisionerState,
Initiator: apiKey.UserID,
InitiatorID: apiKey.UserID,
Transition: createBuild.Transition,
JobID: provisionerJob.ID,
})

View File

@ -23,7 +23,7 @@ func TestWorkspace(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.Workspace(context.Background(), workspace.ID)
require.NoError(t, err)
}
@ -38,7 +38,7 @@ func TestWorkspaceBuilds(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.WorkspaceBuilds(context.Background(), workspace.ID)
require.NoError(t, err)
})
@ -54,7 +54,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
ProjectVersionID: uuid.New(),
Transition: database.WorkspaceTransitionStart,
@ -75,7 +75,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
})
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
_, err := client.CreateWorkspace(context.Background(), "", codersdk.CreateWorkspaceRequest{
_, err := client.CreateWorkspace(context.Background(), codersdk.Me, codersdk.CreateWorkspaceRequest{
ProjectID: project.ID,
Name: "workspace",
})
@ -94,7 +94,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
// Close here so workspace build doesn't process!
closeDaemon.Close()
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
ProjectVersionID: project.ActiveVersionID,
Transition: database.WorkspaceTransitionStart,
@ -113,7 +113,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
ProjectVersionID: project.ActiveVersionID,
@ -135,7 +135,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
build, err := client.CreateWorkspaceBuild(context.Background(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{
Transition: database.WorkspaceTransitionDelete,
@ -160,7 +160,7 @@ func TestWorkspaceBuildByName(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
_, err := client.WorkspaceBuildByName(context.Background(), workspace.ID, "something")
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
@ -175,7 +175,7 @@ func TestWorkspaceBuildByName(t *testing.T) {
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
build, err := client.WorkspaceBuild(context.Background(), workspace.LatestBuild.ID)
require.NoError(t, err)
_, err = client.WorkspaceBuildByName(context.Background(), workspace.ID, build.Name)

View File

@ -8,13 +8,14 @@ import (
"time"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/coderd/database"
)
// Organization is the JSON representation of a Coder organization.
type Organization struct {
ID string `json:"id" validate:"required"`
ID uuid.UUID `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
CreatedAt time.Time `json:"created_at" validate:"required"`
UpdatedAt time.Time `json:"updated_at" validate:"required"`
@ -47,86 +48,113 @@ type CreateProjectRequest struct {
ParameterValues []CreateParameterRequest `json:"parameter_values"`
}
func (c *Client) Organization(ctx context.Context, id string) (Organization, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", id), nil)
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s", id.String()), nil)
if err != nil {
return Organization{}, err
return Organization{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Organization{}, readBodyAsError(res)
}
var organization Organization
return organization, json.NewDecoder(res.Body).Decode(&organization)
}
// ProvisionerDaemonsByOrganization returns provisioner daemons available for an organization.
func (c *Client) ProvisionerDaemonsByOrganization(ctx context.Context, organization string) ([]ProvisionerDaemon, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/provisionerdaemons", organization), nil)
func (c *Client) ProvisionerDaemonsByOrganization(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerDaemon, error) {
res, err := c.request(ctx, http.MethodGet,
fmt.Sprintf("/api/v2/organizations/%s/provisionerdaemons", organizationID.String()),
nil,
)
if err != nil {
return nil, err
return nil, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var daemons []ProvisionerDaemon
return daemons, json.NewDecoder(res.Body).Decode(&daemons)
}
// CreateProjectVersion processes source-code and optionally associates the version with a project.
// Executing without a project is useful for validating source-code.
func (c *Client) CreateProjectVersion(ctx context.Context, organization string, req CreateProjectVersionRequest) (ProjectVersion, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/organizations/%s/projectversions", organization), req)
func (c *Client) CreateProjectVersion(ctx context.Context, organizationID uuid.UUID, req CreateProjectVersionRequest) (ProjectVersion, error) {
res, err := c.request(ctx, http.MethodPost,
fmt.Sprintf("/api/v2/organizations/%s/projectversions", organizationID.String()),
req,
)
if err != nil {
return ProjectVersion{}, err
return ProjectVersion{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return ProjectVersion{}, readBodyAsError(res)
}
var projectVersion ProjectVersion
return projectVersion, json.NewDecoder(res.Body).Decode(&projectVersion)
}
// CreateProject creates a new project inside an organization.
func (c *Client) CreateProject(ctx context.Context, organization string, request CreateProjectRequest) (Project, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/organizations/%s/projects", organization), request)
func (c *Client) CreateProject(ctx context.Context, organizationID uuid.UUID, request CreateProjectRequest) (Project, error) {
res, err := c.request(ctx, http.MethodPost,
fmt.Sprintf("/api/v2/organizations/%s/projects", organizationID.String()),
request,
)
if err != nil {
return Project{}, err
return Project{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Project{}, readBodyAsError(res)
}
var project Project
return project, json.NewDecoder(res.Body).Decode(&project)
}
// ProjectsByOrganization lists all projects inside of an organization.
func (c *Client) ProjectsByOrganization(ctx context.Context, organization string) ([]Project, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/projects", organization), nil)
func (c *Client) ProjectsByOrganization(ctx context.Context, organizationID uuid.UUID) ([]Project, error) {
res, err := c.request(ctx, http.MethodGet,
fmt.Sprintf("/api/v2/organizations/%s/projects", organizationID.String()),
nil,
)
if err != nil {
return nil, err
return nil, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var projects []Project
return projects, json.NewDecoder(res.Body).Decode(&projects)
}
// ProjectByName finds a project inside the organization provided with a case-insensitive name.
func (c *Client) ProjectByName(ctx context.Context, organization, name string) (Project, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/projects/%s", organization, name), nil)
func (c *Client) ProjectByName(ctx context.Context, organizationID uuid.UUID, name string) (Project, error) {
res, err := c.request(ctx, http.MethodGet,
fmt.Sprintf("/api/v2/organizations/%s/projects/%s", organizationID.String(), name),
nil,
)
if err != nil {
return Project{}, err
return Project{}, xerrors.Errorf("execute request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Project{}, readBodyAsError(res)
}
var project Project
return project, json.NewDecoder(res.Body).Decode(&project)
}

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
@ -27,7 +28,7 @@ type Parameter struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Scope ParameterScope `db:"scope" json:"scope"`
ScopeID string `db:"scope_id" json:"scope_id"`
ScopeID uuid.UUID `db:"scope_id" json:"scope_id"`
Name string `db:"name" json:"name"`
SourceScheme database.ParameterSourceScheme `db:"source_scheme" json:"source_scheme"`
DestinationScheme database.ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"`
@ -41,40 +42,47 @@ type CreateParameterRequest struct {
DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"`
}
func (c *Client) CreateParameter(ctx context.Context, scope ParameterScope, id string, req CreateParameterRequest) (Parameter, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/parameters/%s/%s", scope, id), req)
func (c *Client) CreateParameter(ctx context.Context, scope ParameterScope, id uuid.UUID, req CreateParameterRequest) (Parameter, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/parameters/%s/%s", scope, id.String()), req)
if err != nil {
return Parameter{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Parameter{}, readBodyAsError(res)
}
var param Parameter
return param, json.NewDecoder(res.Body).Decode(&param)
}
func (c *Client) DeleteParameter(ctx context.Context, scope ParameterScope, id, name string) error {
res, err := c.request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/parameters/%s/%s/%s", scope, id, name), nil)
func (c *Client) DeleteParameter(ctx context.Context, scope ParameterScope, id uuid.UUID, name string) error {
res, err := c.request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/parameters/%s/%s/%s", scope, id.String(), name), nil)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return readBodyAsError(res)
}
_, _ = io.Copy(io.Discard, res.Body)
return nil
}
func (c *Client) Parameters(ctx context.Context, scope ParameterScope, id string) ([]Parameter, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/parameters/%s/%s", scope, id), nil)
func (c *Client) Parameters(ctx context.Context, scope ParameterScope, id uuid.UUID) ([]Parameter, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/parameters/%s/%s", scope, id.String()), nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var parameters []Parameter
return parameters, json.NewDecoder(res.Body).Decode(&parameters)
}

View File

@ -12,14 +12,13 @@ import (
"github.com/coder/coder/coderd/database"
)
// Project is the JSON representation of a Coder project.
// This type matches the database object for now, but is
// abstracted for ease of change later on.
// Project is the JSON representation of a Coder project. This type matches the
// database object for now, but is abstracted for ease of change later on.
type Project struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OrganizationID string `json:"organization_id"`
OrganizationID uuid.UUID `json:"organization_id"`
Name string `json:"name"`
Provisioner database.ProvisionerType `json:"provisioner"`
ActiveVersionID uuid.UUID `json:"active_version_id"`

View File

@ -10,32 +10,35 @@ import (
"github.com/google/uuid"
)
// Me is used as a replacement for your own ID.
var Me = uuid.Nil
// User represents a user in Coder.
type User struct {
ID string `json:"id" validate:"required"`
ID uuid.UUID `json:"id" validate:"required"`
Email string `json:"email" validate:"required"`
CreatedAt time.Time `json:"created_at" validate:"required"`
Username string `json:"username" validate:"required"`
}
type CreateFirstUserRequest struct {
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
Organization string `json:"organization" validate:"required,username"`
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
OrganizationName string `json:"organization" validate:"required,username"`
}
// CreateFirstUserResponse contains IDs for newly created user info.
type CreateFirstUserResponse struct {
UserID string `json:"user_id"`
OrganizationID string `json:"organization_id"`
UserID uuid.UUID `json:"user_id"`
OrganizationID uuid.UUID `json:"organization_id"`
}
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
OrganizationID string `json:"organization_id" validate:"required"`
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
OrganizationID uuid.UUID `json:"organization_id" validate:"required"`
}
// LoginWithPasswordRequest enables callers to authenticate with email and password.
@ -113,11 +116,8 @@ func (c *Client) CreateUser(ctx context.Context, req CreateUserRequest) (User, e
}
// CreateAPIKey generates an API key for the user ID provided.
func (c *Client) CreateAPIKey(ctx context.Context, id string) (*GenerateAPIKeyResponse, error) {
if id == "" {
id = "me"
}
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", id), nil)
func (c *Client) CreateAPIKey(ctx context.Context, userID uuid.UUID) (*GenerateAPIKeyResponse, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", uuidOrMe(userID)), nil)
if err != nil {
return nil, err
}
@ -162,12 +162,9 @@ func (c *Client) Logout(ctx context.Context) error {
}
// User returns a user for the ID provided.
// If the ID string is empty, the current user will be returned.
func (c *Client) User(ctx context.Context, id string) (User, error) {
if id == "" {
id = "me"
}
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s", id), nil)
// If the uuid is nil, the current user will be returned.
func (c *Client) User(ctx context.Context, id uuid.UUID) (User, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s", uuidOrMe(id)), nil)
if err != nil {
return User{}, err
}
@ -180,11 +177,8 @@ func (c *Client) User(ctx context.Context, id string) (User, error) {
}
// OrganizationsByUser returns all organizations the user is a member of.
func (c *Client) OrganizationsByUser(ctx context.Context, id string) ([]Organization, error) {
if id == "" {
id = "me"
}
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations", id), nil)
func (c *Client) OrganizationsByUser(ctx context.Context, userID uuid.UUID) ([]Organization, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations", uuidOrMe(userID)), nil)
if err != nil {
return nil, err
}
@ -196,11 +190,8 @@ func (c *Client) OrganizationsByUser(ctx context.Context, id string) ([]Organiza
return orgs, json.NewDecoder(res.Body).Decode(&orgs)
}
func (c *Client) OrganizationByName(ctx context.Context, user, name string) (Organization, error) {
if user == "" {
user = "me"
}
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations/%s", user, name), nil)
func (c *Client) OrganizationByName(ctx context.Context, userID uuid.UUID, name string) (Organization, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/organizations/%s", uuidOrMe(userID), name), nil)
if err != nil {
return Organization{}, err
}
@ -213,68 +204,74 @@ func (c *Client) OrganizationByName(ctx context.Context, user, name string) (Org
}
// CreateOrganization creates an organization and adds the provided user as an admin.
func (c *Client) CreateOrganization(ctx context.Context, user string, req CreateOrganizationRequest) (Organization, error) {
if user == "" {
user = "me"
}
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/organizations", user), req)
func (c *Client) CreateOrganization(ctx context.Context, userID uuid.UUID, req CreateOrganizationRequest) (Organization, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/organizations", uuidOrMe(userID)), req)
if err != nil {
return Organization{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Organization{}, readBodyAsError(res)
}
var org Organization
return org, json.NewDecoder(res.Body).Decode(&org)
}
// CreateWorkspace creates a new workspace for the project specified.
func (c *Client) CreateWorkspace(ctx context.Context, user string, request CreateWorkspaceRequest) (Workspace, error) {
if user == "" {
user = "me"
}
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/workspaces", user), request)
func (c *Client) CreateWorkspace(ctx context.Context, userID uuid.UUID, request CreateWorkspaceRequest) (Workspace, error) {
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/workspaces", uuidOrMe(userID)), request)
if err != nil {
return Workspace{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Workspace{}, readBodyAsError(res)
}
var workspace Workspace
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
}
// WorkspacesByUser returns all workspaces the specified user has access to.
func (c *Client) WorkspacesByUser(ctx context.Context, user string) ([]Workspace, error) {
if user == "" {
user = "me"
}
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspaces", user), nil)
func (c *Client) WorkspacesByUser(ctx context.Context, userID uuid.UUID) ([]Workspace, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspaces", uuidOrMe(userID)), nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, readBodyAsError(res)
}
var workspaces []Workspace
return workspaces, json.NewDecoder(res.Body).Decode(&workspaces)
}
func (c *Client) WorkspaceByName(ctx context.Context, user, name string) (Workspace, error) {
if user == "" {
user = "me"
}
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspaces/%s", user, name), nil)
func (c *Client) WorkspaceByName(ctx context.Context, userID uuid.UUID, name string) (Workspace, error) {
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspaces/%s", uuidOrMe(userID), name), nil)
if err != nil {
return Workspace{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Workspace{}, readBodyAsError(res)
}
var workspace Workspace
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
}
// uuidOrMe returns the provided uuid as a string if it's valid, ortherwise
// `me`.
func uuidOrMe(id uuid.UUID) string {
if id == Me {
return "me"
}
return id.String()
}

View File

@ -24,7 +24,7 @@ type WorkspaceBuild struct {
AfterID uuid.UUID `json:"after_id"`
Name string `json:"name"`
Transition database.WorkspaceTransition `json:"transition"`
Initiator string `json:"initiator"`
InitiatorID uuid.UUID `json:"initiator_id"`
Job ProvisionerJob `json:"job"`
}

View File

@ -18,7 +18,7 @@ type Workspace struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OwnerID string `json:"owner_id"`
OwnerID uuid.UUID `json:"owner_id"`
ProjectID uuid.UUID `json:"project_id"`
ProjectName string `json:"project_name"`
LatestBuild WorkspaceBuild `json:"latest_build"`

View File

@ -38,6 +38,7 @@ func List() ([]Example, error) {
returnError = xerrors.Errorf("read dir: %w", err)
return
}
for _, dir := range dirs {
exampleID := dir.Name()
// Each one of these is a example!
@ -46,31 +47,37 @@ func List() ([]Example, error) {
returnError = xerrors.Errorf("example %q does not contain README.md", exampleID)
return
}
frontMatter, err := pageparser.ParseFrontMatterAndContent(bytes.NewReader(readme))
if err != nil {
returnError = xerrors.Errorf("parse example %q front matter: %w", exampleID, err)
return
}
nameRaw, exists := frontMatter.FrontMatter["name"]
if !exists {
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
return
}
name, valid := nameRaw.(string)
if !valid {
returnError = xerrors.Errorf("example %q name isn't a string", exampleID)
return
}
descriptionRaw, exists := frontMatter.FrontMatter["description"]
if !exists {
returnError = xerrors.Errorf("example %q front matter does not contain name", exampleID)
return
}
description, valid := descriptionRaw.(string)
if !valid {
returnError = xerrors.Errorf("example %q description isn't a string", exampleID)
return
}
examples = append(examples, Example{
ID: exampleID,
Name: name,
@ -87,8 +94,9 @@ func Archive(exampleID string) ([]byte, error) {
rawData, err, _ := archives.Do(exampleID, func() (interface{}, error) {
examples, err := List()
if err != nil {
return nil, err
return nil, xerrors.Errorf("list: %w", err)
}
var selected Example
for _, example := range examples {
if example.ID != exampleID {
@ -97,6 +105,7 @@ func Archive(exampleID string) ([]byte, error) {
selected = example
break
}
if selected.ID == "" {
return nil, xerrors.Errorf("example with id %q not found", exampleID)
}
@ -114,27 +123,33 @@ func Archive(exampleID string) ([]byte, error) {
if err != nil {
return nil, xerrors.Errorf("open file: %w", err)
}
info, err := file.Stat()
if err != nil {
return nil, xerrors.Errorf("stat file: %w", err)
}
if info.IsDir() {
continue
}
data := make([]byte, info.Size())
_, err = file.Read(data)
if err != nil {
return nil, xerrors.Errorf("read data: %w", err)
}
header, err := tar.FileInfoHeader(info, entry.Name())
if err != nil {
return nil, xerrors.Errorf("get file header: %w", err)
}
header.Mode = 0644
err = tarWriter.WriteHeader(header)
if err != nil {
return nil, xerrors.Errorf("write file: %w", err)
}
_, err = tarWriter.Write(data)
if err != nil {
return nil, xerrors.Errorf("write: %w", err)
@ -144,6 +159,7 @@ func Archive(exampleID string) ([]byte, error) {
if err != nil {
return nil, xerrors.Errorf("flush archive: %w", err)
}
return buffer.Bytes(), nil
})
if err != nil {