Skip to content

Commit cc11f71

Browse files
committed
Merge branch 'main' into bryphe/prototype/workspaces-page
2 parents dd38938 + c65850b commit cc11f71

39 files changed

+1721
-328
lines changed

.github/workflows/coder.yaml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ jobs:
7979
- uses: actions/setup-go@v2
8080
with:
8181
go-version: "^1.17"
82-
- run:
83-
curl -sSL
82+
- run: curl -sSL
8483
https://github.com/kyleconroy/sqlc/releases/download/v1.11.0/sqlc_1.11.0_linux_amd64.tar.gz
8584
| sudo tar -C /usr/bin -xz sqlc
8685

@@ -151,21 +150,18 @@ jobs:
151150
- run: go install gotest.tools/gotestsum@latest
152151

153152
- uses: hashicorp/setup-terraform@v1
154-
if: runner.os == 'Linux'
155153
with:
156154
terraform_version: 1.1.2
157155
terraform_wrapper: false
158156

159157
- name: Test with Mock Database
160-
run:
161-
gotestsum --jsonfile="gotests.json" --packages="./..." --
158+
run: gotestsum --jsonfile="gotests.json" --packages="./..." --
162159
-covermode=atomic -coverprofile="gotests.coverage" -timeout=3m
163-
-count=3 -race -short -parallel=2
160+
-count=5 -race -short -parallel=2
164161

165162
- name: Test with PostgreSQL Database
166163
if: runner.os == 'Linux'
167-
run:
168-
DB=true gotestsum --jsonfile="gotests.json" --packages="./..." --
164+
run: DB=true gotestsum --jsonfile="gotests.json" --packages="./..." --
169165
-covermode=atomic -coverprofile="gotests.coverage" -timeout=3m
170166
-count=1 -race -parallel=2
171167

.vscode/settings.json

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,32 @@
2323
}
2424
]
2525
},
26-
"cSpell.words": ["coderd", "coderdtest", "codersdk", "httpmw", "oneof", "stretchr", "xerrors"]
26+
"cSpell.words": [
27+
"coderd",
28+
"coderdtest",
29+
"codersdk",
30+
"drpc",
31+
"drpcconn",
32+
"drpcmux",
33+
"drpcserver",
34+
"goleak",
35+
"hashicorp",
36+
"httpmw",
37+
"moby",
38+
"nhooyr",
39+
"nolint",
40+
"nosec",
41+
"oneof",
42+
"protobuf",
43+
"provisionerd",
44+
"provisionersdk",
45+
"retrier",
46+
"sdkproto",
47+
"stretchr",
48+
"tfexec",
49+
"tfstate",
50+
"unconvert",
51+
"xerrors",
52+
"yamux"
53+
]
2754
}

coderd/cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"cdr.dev/slog"
1212
"cdr.dev/slog/sloggers/sloghuman"
1313
"github.com/coder/coder/coderd"
14+
"github.com/coder/coder/database"
1415
"github.com/coder/coder/database/databasefake"
1516
)
1617

@@ -24,6 +25,7 @@ func Root() *cobra.Command {
2425
handler := coderd.New(&coderd.Options{
2526
Logger: slog.Make(sloghuman.Sink(os.Stderr)),
2627
Database: databasefake.New(),
28+
Pubsub: database.NewPubsubInMemory(),
2729
})
2830

2931
listener, err := net.Listen("tcp", address)

coderd/coderd.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Options struct {
2323
func New(options *Options) http.Handler {
2424
api := &api{
2525
Database: options.Database,
26+
Logger: options.Logger,
2627
Pubsub: options.Pubsub,
2728
}
2829

@@ -64,6 +65,10 @@ func New(options *Options) http.Handler {
6465
r.Route("/history", func(r chi.Router) {
6566
r.Get("/", api.projectHistoryByOrganization)
6667
r.Post("/", api.postProjectHistoryByOrganization)
68+
r.Route("/{projecthistory}", func(r chi.Router) {
69+
r.Use(httpmw.ExtractProjectHistoryParam(api.Database))
70+
r.Get("/", api.projectHistoryByOrganizationAndName)
71+
})
6772
})
6873
})
6974
})
@@ -84,11 +89,19 @@ func New(options *Options) http.Handler {
8489
r.Route("/history", func(r chi.Router) {
8590
r.Post("/", api.postWorkspaceHistoryByUser)
8691
r.Get("/", api.workspaceHistoryByUser)
87-
r.Get("/latest", api.latestWorkspaceHistoryByUser)
92+
r.Route("/{workspacehistory}", func(r chi.Router) {
93+
r.Use(httpmw.ExtractWorkspaceHistoryParam(options.Database))
94+
r.Get("/", api.workspaceHistoryByName)
95+
})
8896
})
8997
})
9098
})
9199
})
100+
101+
r.Route("/provisioners/daemons", func(r chi.Router) {
102+
r.Get("/", api.provisionerDaemons)
103+
r.Get("/serve", api.provisionerDaemonsServe)
104+
})
92105
})
93106
r.NotFound(site.Handler().ServeHTTP)
94107
return r
@@ -98,5 +111,6 @@ func New(options *Options) http.Handler {
98111
// be added to this struct for code clarity.
99112
type api struct {
100113
Database database.Store
114+
Logger slog.Logger
101115
Pubsub database.Pubsub
102116
}

coderd/coderdtest/coderdtest.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,27 @@ package coderdtest
33
import (
44
"context"
55
"database/sql"
6+
"io"
67
"net/http/httptest"
78
"net/url"
89
"os"
910
"testing"
11+
"time"
1012

1113
"github.com/stretchr/testify/require"
1214

15+
"cdr.dev/slog"
1316
"cdr.dev/slog/sloggers/slogtest"
1417
"github.com/coder/coder/coderd"
1518
"github.com/coder/coder/codersdk"
1619
"github.com/coder/coder/cryptorand"
1720
"github.com/coder/coder/database"
1821
"github.com/coder/coder/database/databasefake"
1922
"github.com/coder/coder/database/postgres"
23+
"github.com/coder/coder/provisioner/terraform"
24+
"github.com/coder/coder/provisionerd"
25+
"github.com/coder/coder/provisionersdk"
26+
"github.com/coder/coder/provisionersdk/proto"
2027
)
2128

2229
// Server represents a test instance of coderd.
@@ -57,11 +64,46 @@ func (s *Server) RandomInitialUser(t *testing.T) coderd.CreateInitialUserRequest
5764
return req
5865
}
5966

67+
// AddProvisionerd launches a new provisionerd instance!
68+
func (s *Server) AddProvisionerd(t *testing.T) io.Closer {
69+
tfClient, tfServer := provisionersdk.TransportPipe()
70+
ctx, cancelFunc := context.WithCancel(context.Background())
71+
t.Cleanup(func() {
72+
_ = tfClient.Close()
73+
_ = tfServer.Close()
74+
cancelFunc()
75+
})
76+
go func() {
77+
err := terraform.Serve(ctx, &terraform.ServeOptions{
78+
ServeOptions: &provisionersdk.ServeOptions{
79+
Listener: tfServer,
80+
},
81+
Logger: slogtest.Make(t, nil).Named("terraform-provisioner").Leveled(slog.LevelDebug),
82+
})
83+
require.NoError(t, err)
84+
}()
85+
86+
closer := provisionerd.New(s.Client.ProvisionerDaemonClient, &provisionerd.Options{
87+
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
88+
PollInterval: 50 * time.Millisecond,
89+
UpdateInterval: 50 * time.Millisecond,
90+
Provisioners: provisionerd.Provisioners{
91+
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(provisionersdk.Conn(tfClient)),
92+
},
93+
WorkDirectory: t.TempDir(),
94+
})
95+
t.Cleanup(func() {
96+
_ = closer.Close()
97+
})
98+
return closer
99+
}
100+
60101
// New constructs a new coderd test instance. This returned Server
61102
// should contain no side-effects.
62103
func New(t *testing.T) Server {
63104
// This can be hotswapped for a live database instance.
64105
db := databasefake.New()
106+
pubsub := database.NewPubsubInMemory()
65107
if os.Getenv("DB") != "" {
66108
connectionURL, close, err := postgres.Open()
67109
require.NoError(t, err)
@@ -74,11 +116,18 @@ func New(t *testing.T) Server {
74116
err = database.Migrate(sqlDB)
75117
require.NoError(t, err)
76118
db = database.New(sqlDB)
119+
120+
pubsub, err = database.NewPubsub(context.Background(), sqlDB, connectionURL)
121+
require.NoError(t, err)
122+
t.Cleanup(func() {
123+
_ = pubsub.Close()
124+
})
77125
}
78126

79127
handler := coderd.New(&coderd.Options{
80-
Logger: slogtest.Make(t, nil),
128+
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
81129
Database: db,
130+
Pubsub: pubsub,
82131
})
83132
srv := httptest.NewServer(handler)
84133
serverURL, err := url.Parse(srv.URL)

coderd/coderdtest/coderdtest_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ func TestNew(t *testing.T) {
1616
t.Parallel()
1717
server := coderdtest.New(t)
1818
_ = server.RandomInitialUser(t)
19+
_ = server.AddProvisionerd(t)
1920
}

coderd/projecthistory.go

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"archive/tar"
55
"bytes"
66
"database/sql"
7+
"encoding/json"
78
"errors"
89
"fmt"
910
"net/http"
@@ -12,6 +13,7 @@ import (
1213
"github.com/go-chi/render"
1314
"github.com/google/uuid"
1415
"github.com/moby/moby/pkg/namesgenerator"
16+
"golang.org/x/xerrors"
1517

1618
"github.com/coder/coder/database"
1719
"github.com/coder/coder/httpapi"
@@ -26,6 +28,7 @@ type ProjectHistory struct {
2628
UpdatedAt time.Time `json:"updated_at"`
2729
Name string `json:"name"`
2830
StorageMethod database.ProjectStorageMethod `json:"storage_method"`
31+
Import ProvisionerJob `json:"import"`
2932
}
3033

3134
// CreateProjectHistoryRequest enables callers to create a new Project Version.
@@ -50,12 +53,33 @@ func (api *api) projectHistoryByOrganization(rw http.ResponseWriter, r *http.Req
5053
}
5154
apiHistory := make([]ProjectHistory, 0)
5255
for _, version := range history {
53-
apiHistory = append(apiHistory, convertProjectHistory(version))
56+
job, err := api.Database.GetProvisionerJobByID(r.Context(), version.ImportJobID)
57+
if err != nil {
58+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
59+
Message: fmt.Sprintf("get provisioner job: %s", err),
60+
})
61+
return
62+
}
63+
apiHistory = append(apiHistory, convertProjectHistory(version, job))
5464
}
5565
render.Status(r, http.StatusOK)
5666
render.JSON(rw, r, apiHistory)
5767
}
5868

69+
// Return a single project history by organization and name.
70+
func (api *api) projectHistoryByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
71+
projectHistory := httpmw.ProjectHistoryParam(r)
72+
job, err := api.Database.GetProvisionerJobByID(r.Context(), projectHistory.ImportJobID)
73+
if err != nil {
74+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
75+
Message: fmt.Sprintf("get provisioner job: %s", err),
76+
})
77+
return
78+
}
79+
render.Status(r, http.StatusOK)
80+
render.JSON(rw, r, convertProjectHistory(projectHistory, job))
81+
}
82+
5983
// Creates a new version of the project. An import job is queued to parse
6084
// the storage method provided. Once completed, the import job will specify
6185
// the version as latest.
@@ -82,37 +106,71 @@ func (api *api) postProjectHistoryByOrganization(rw http.ResponseWriter, r *http
82106
return
83107
}
84108

109+
apiKey := httpmw.APIKey(r)
85110
project := httpmw.ProjectParam(r)
86-
history, err := api.Database.InsertProjectHistory(r.Context(), database.InsertProjectHistoryParams{
87-
ID: uuid.New(),
88-
ProjectID: project.ID,
89-
CreatedAt: database.Now(),
90-
UpdatedAt: database.Now(),
91-
Name: namesgenerator.GetRandomName(1),
92-
StorageMethod: createProjectVersion.StorageMethod,
93-
StorageSource: createProjectVersion.StorageSource,
94-
// TODO: Make this do something!
95-
ImportJobID: uuid.New(),
111+
112+
var provisionerJob database.ProvisionerJob
113+
var projectHistory database.ProjectHistory
114+
err := api.Database.InTx(func(db database.Store) error {
115+
projectHistoryID := uuid.New()
116+
input, err := json.Marshal(projectImportJob{
117+
ProjectHistoryID: projectHistoryID,
118+
})
119+
if err != nil {
120+
return xerrors.Errorf("marshal import job: %w", err)
121+
}
122+
123+
provisionerJob, err = db.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
124+
ID: uuid.New(),
125+
CreatedAt: database.Now(),
126+
UpdatedAt: database.Now(),
127+
InitiatorID: apiKey.UserID,
128+
Provisioner: project.Provisioner,
129+
Type: database.ProvisionerJobTypeProjectImport,
130+
ProjectID: project.ID,
131+
Input: input,
132+
})
133+
if err != nil {
134+
return xerrors.Errorf("insert provisioner job: %w", err)
135+
}
136+
137+
projectHistory, err = api.Database.InsertProjectHistory(r.Context(), database.InsertProjectHistoryParams{
138+
ID: projectHistoryID,
139+
ProjectID: project.ID,
140+
CreatedAt: database.Now(),
141+
UpdatedAt: database.Now(),
142+
Name: namesgenerator.GetRandomName(1),
143+
StorageMethod: createProjectVersion.StorageMethod,
144+
StorageSource: createProjectVersion.StorageSource,
145+
ImportJobID: provisionerJob.ID,
146+
})
147+
if err != nil {
148+
return xerrors.Errorf("insert project history: %s", err)
149+
}
150+
return nil
96151
})
97152
if err != nil {
98153
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
99-
Message: fmt.Sprintf("insert project history: %s", err),
154+
Message: err.Error(),
100155
})
101156
return
102157
}
103158

104-
// TODO: A job to process the new version should occur here.
105-
106159
render.Status(r, http.StatusCreated)
107-
render.JSON(rw, r, convertProjectHistory(history))
160+
render.JSON(rw, r, convertProjectHistory(projectHistory, provisionerJob))
108161
}
109162

110-
func convertProjectHistory(history database.ProjectHistory) ProjectHistory {
163+
func convertProjectHistory(history database.ProjectHistory, job database.ProvisionerJob) ProjectHistory {
111164
return ProjectHistory{
112165
ID: history.ID,
113166
ProjectID: history.ProjectID,
114167
CreatedAt: history.CreatedAt,
115168
UpdatedAt: history.UpdatedAt,
116169
Name: history.Name,
170+
Import: convertProvisionerJob(job),
117171
}
118172
}
173+
174+
func projectHistoryLogsChannel(projectHistoryID uuid.UUID) string {
175+
return fmt.Sprintf("project-history-logs:%s", projectHistoryID)
176+
}

0 commit comments

Comments
 (0)