Skip to content

Commit 4b965f4

Browse files
committed
Merge branch 'main' into provisionerdaemons
2 parents ac853f1 + e6ead7d commit 4b965f4

37 files changed

+384
-144
lines changed

.github/workflows/typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ MacOS = "macOS"
88
# do as sudo replacement
99
doas = "doas"
1010
darcula = "darcula"
11+
Hashi = "Hashi"
1112

1213
[files]
1314
extend-exclude = [

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ Discord"](https://img.shields.io/badge/join-us%20on%20Discord-gray.svg?longCache
77
[![Twitter
88
Follow](https://img.shields.io/twitter/follow/coderhq?label=%40coderhq&style=social)](https://twitter.com/coderhq)
99

10-
Coder creates remote development machines so your team can develop from anywhere.
10+
Software development on your infrastructure. Offload your team's development from local workstations to cloud servers. Onboard developers in minutes. Build, test and compile at the speed of the cloud. Keep your source code and data behind your firewall.
11+
12+
> "By leveraging Terraform, Coder lets developers run any IDE on any compute platform including on-prem, AWS, Azure, GCP, DigitalOcean, Kubernetes, Docker, and more, with workspaces running on Linux, Windows, or Mac." - **Kevin Fishner Chief of Staff at [HashiCorp](https://hashicorp.com/)**
13+
1114

1215
<p align="center">
1316
<img src="./docs/images/hero-image.png">
@@ -27,10 +30,15 @@ Coder creates remote development machines so your team can develop from anywhere
2730
- Access your environment from any place on any client (even an iPad)
2831
- Onboard instantly then stay up to date continuously
2932

30-
## Getting Started
33+
## Recommended Reading
3134

32-
> **Note**:
33-
> Coder is in a beta state. [Report issues here](https://github.com/coder/coder/issues/new).
35+
- [How our development team shares one giant bare metal machine](https://coder.com/blog/how-our-development-team-shares-one-giant-bare-metal-machine?utm_source=github.com/coder/coder&utm_medium=github&utm_campaign=readme.md)
36+
- [Laptop development is dead: why remote development is the future](https://medium.com/@elliotgraebert/laptop-development-is-dead-why-remote-development-is-the-future-f92ce103fd13)
37+
- [Learn how Palantir improved build times by 78% with coder](https://blog.palantir.com/the-benefits-of-remote-ephemeral-workspaces-1a1251ed6e53).
38+
- [A software development environment is not just a container](https://coder.com/blog/not-a-container?utm_source=github.com/coder/coder&utm_medium=github&utm_campaign=readme.md).
39+
- [What Coder is not](https://coder.com/docs/coder-oss/latest/index#what-coder-is-not?utm_source=github.com/coder/coder&utm_medium=github&utm_campaign=readme.md).
40+
41+
## Getting Started
3442

3543
The easiest way to install Coder is to use our
3644
[install script](https://github.com/coder/coder/blob/main/install.sh) for Linux
@@ -84,7 +92,7 @@ Please file [an issue](https://github.com/coder/coder/issues/new) if any informa
8492
| [Coder](https://github.com/coder/coder) | Platform | OSS + Self-Managed | Pay your cloud | All [Terraform](https://www.terraform.io/registry/providers) resources, all clouds, multi-architecture: Linux, Mac, Windows, containers, VMs, amd64, arm64 |
8593
| [code-server](https://github.com/cdr/code-server) | Web IDE | OSS + Self-Managed | Pay your cloud | Linux, Mac, Windows, containers, VMs, amd64, arm64 |
8694
| [Coder (Classic)](https://coder.com/docs) | Platform | Self-Managed | Pay your cloud + license fees | Kubernetes Linux Containers |
87-
| [GitHub Codespaces](https://github.com/features/codespaces) | Platform | SaaS | 2x Azure Compute | Linux containers |
95+
| [GitHub Codespaces](https://github.com/features/codespaces) | Platform | SaaS | 2x Azure Compute | Linux Virtual Machines |
8896

8997
---
9098

cli/cliui/agent_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/coder/coder/cli/cliui"
1313
"github.com/coder/coder/codersdk"
1414
"github.com/coder/coder/pty/ptytest"
15+
"github.com/coder/coder/testutil"
1516
)
1617

1718
func TestAgent(t *testing.T) {
@@ -49,3 +50,50 @@ func TestAgent(t *testing.T) {
4950
disconnected.Store(true)
5051
<-done
5152
}
53+
54+
func TestAgentTimeoutWithTroubleshootingURL(t *testing.T) {
55+
t.Parallel()
56+
57+
ctx, _ := testutil.Context(t)
58+
59+
wantURL := "https://coder.com/troubleshoot"
60+
61+
var connected, timeout atomic.Bool
62+
cmd := &cobra.Command{
63+
RunE: func(cmd *cobra.Command, args []string) error {
64+
err := cliui.Agent(cmd.Context(), cmd.OutOrStdout(), cliui.AgentOptions{
65+
WorkspaceName: "example",
66+
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
67+
agent := codersdk.WorkspaceAgent{
68+
Status: codersdk.WorkspaceAgentConnecting,
69+
TroubleshootingURL: "https://coder.com/troubleshoot",
70+
}
71+
switch {
72+
case connected.Load():
73+
agent.Status = codersdk.WorkspaceAgentConnected
74+
case timeout.Load():
75+
agent.Status = codersdk.WorkspaceAgentTimeout
76+
}
77+
return agent, nil
78+
},
79+
FetchInterval: time.Millisecond,
80+
WarnInterval: 5 * time.Millisecond,
81+
})
82+
return err
83+
},
84+
}
85+
ptty := ptytest.New(t)
86+
cmd.SetOutput(ptty.Output())
87+
cmd.SetIn(ptty.Input())
88+
done := make(chan struct{})
89+
go func() {
90+
defer close(done)
91+
err := cmd.ExecuteContext(ctx)
92+
assert.NoError(t, err)
93+
}()
94+
ptty.ExpectMatch("Don't panic")
95+
timeout.Store(true)
96+
ptty.ExpectMatch(wantURL)
97+
connected.Store(true)
98+
<-done
99+
}

cli/deployment/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,13 @@ func newConfig() *codersdk.DeploymentConfig {
347347
Hidden: true,
348348
Default: 10 * time.Minute,
349349
},
350+
AgentFallbackTroubleshootingURL: &codersdk.DeploymentConfigField[string]{
351+
Name: "Agent Fallback Troubleshooting URL",
352+
Usage: "URL to use for agent troubleshooting when not set in the template",
353+
Flag: "agent-fallback-troubleshooting-url",
354+
Hidden: true,
355+
Default: "https://coder.com/docs/coder-oss/latest/templates#troubleshooting-templates",
356+
},
350357
AuditLogging: &codersdk.DeploymentConfigField[bool]{
351358
Name: "Audit Logging",
352359
Usage: "Specifies whether audit logging is enabled.",

cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ func newProvisionerDaemon(
961961

962962
// nolint: revive
963963
func printLogo(cmd *cobra.Command) {
964-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s - Remote development on your infrastucture\n", cliui.Styles.Bold.Render("Coder "+buildinfo.Version()))
964+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s - Software development on your infrastucture\n", cliui.Styles.Bold.Render("Coder "+buildinfo.Version()))
965965
}
966966

967967
func loadCertificates(tlsCertFiles, tlsKeyFiles []string) ([]tls.Certificate, error) {

coderd/autobuild/executor/lifecycle_executor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,14 @@ func (e *Executor) runOnce(t time.Time) Stats {
9999
// NOTE: If a workspace build is created with a given TTL and then the user either
100100
// changes or unsets the TTL, the deadline for the workspace build will not
101101
// have changed. This behavior is as expected per #2229.
102-
workspaces, err := e.db.GetWorkspaces(e.ctx, database.GetWorkspacesParams{
102+
workspaceRows, err := e.db.GetWorkspaces(e.ctx, database.GetWorkspacesParams{
103103
Deleted: false,
104104
})
105105
if err != nil {
106106
e.log.Error(e.ctx, "get workspaces for autostart or autostop", slog.Error(err))
107107
return stats
108108
}
109+
workspaces := database.ConvertWorkspaceRows(workspaceRows)
109110

110111
var eligibleWorkspaceIDs []uuid.UUID
111112
for _, ws := range workspaces {

coderd/database/databasefake/databasefake.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -742,14 +742,14 @@ func (q *fakeQuerier) GetAuthorizationUserRoles(_ context.Context, userID uuid.U
742742
}, nil
743743
}
744744

745-
func (q *fakeQuerier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesParams) ([]database.Workspace, error) {
745+
func (q *fakeQuerier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesParams) ([]database.GetWorkspacesRow, error) {
746746
// A nil auth filter means no auth filter.
747-
workspaces, err := q.GetAuthorizedWorkspaces(ctx, arg, nil)
748-
return workspaces, err
747+
workspaceRows, err := q.GetAuthorizedWorkspaces(ctx, arg, nil)
748+
return workspaceRows, err
749749
}
750750

751751
//nolint:gocyclo
752-
func (q *fakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]database.Workspace, error) {
752+
func (q *fakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]database.GetWorkspacesRow, error) {
753753
q.mutex.RLock()
754754
defer q.mutex.RUnlock()
755755

@@ -890,20 +890,43 @@ func (q *fakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
890890
workspaces = append(workspaces, workspace)
891891
}
892892

893+
beforePageCount := len(workspaces)
894+
893895
if arg.Offset > 0 {
894896
if int(arg.Offset) > len(workspaces) {
895-
return []database.Workspace{}, nil
897+
return []database.GetWorkspacesRow{}, nil
896898
}
897899
workspaces = workspaces[arg.Offset:]
898900
}
899901
if arg.Limit > 0 {
900902
if int(arg.Limit) > len(workspaces) {
901-
return workspaces, nil
903+
return convertToWorkspaceRows(workspaces, int64(beforePageCount)), nil
902904
}
903905
workspaces = workspaces[:arg.Limit]
904906
}
905907

906-
return workspaces, nil
908+
return convertToWorkspaceRows(workspaces, int64(beforePageCount)), nil
909+
}
910+
911+
func convertToWorkspaceRows(workspaces []database.Workspace, count int64) []database.GetWorkspacesRow {
912+
rows := make([]database.GetWorkspacesRow, len(workspaces))
913+
for i, w := range workspaces {
914+
rows[i] = database.GetWorkspacesRow{
915+
ID: w.ID,
916+
CreatedAt: w.CreatedAt,
917+
UpdatedAt: w.UpdatedAt,
918+
OwnerID: w.OwnerID,
919+
OrganizationID: w.OrganizationID,
920+
TemplateID: w.TemplateID,
921+
Deleted: w.Deleted,
922+
Name: w.Name,
923+
AutostartSchedule: w.AutostartSchedule,
924+
Ttl: w.Ttl,
925+
LastUsedAt: w.LastUsedAt,
926+
Count: count,
927+
}
928+
}
929+
return rows
907930
}
908931

909932
func (q *fakeQuerier) GetWorkspaceByID(_ context.Context, id uuid.UUID) (database.Workspace, error) {

coderd/database/modelmethods.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,24 @@ func ConvertUserRows(rows []GetUsersRow) []User {
9393

9494
return users
9595
}
96+
97+
func ConvertWorkspaceRows(rows []GetWorkspacesRow) []Workspace {
98+
workspaces := make([]Workspace, len(rows))
99+
for i, r := range rows {
100+
workspaces[i] = Workspace{
101+
ID: r.ID,
102+
CreatedAt: r.CreatedAt,
103+
UpdatedAt: r.UpdatedAt,
104+
OwnerID: r.OwnerID,
105+
OrganizationID: r.OrganizationID,
106+
TemplateID: r.TemplateID,
107+
Deleted: r.Deleted,
108+
Name: r.Name,
109+
AutostartSchedule: r.AutostartSchedule,
110+
Ttl: r.Ttl,
111+
LastUsedAt: r.LastUsedAt,
112+
}
113+
}
114+
115+
return workspaces
116+
}

coderd/database/modelqueries.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ func (q *sqlQuerier) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([
112112
}
113113

114114
type workspaceQuerier interface {
115-
GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]Workspace, error)
115+
GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]GetWorkspacesRow, error)
116116
}
117117

118118
// GetAuthorizedWorkspaces returns all workspaces that the user is authorized to access.
119119
// This code is copied from `GetWorkspaces` and adds the authorized filter WHERE
120120
// clause.
121-
func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]Workspace, error) {
121+
func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]GetWorkspacesRow, error) {
122122
// In order to properly use ORDER BY, OFFSET, and LIMIT, we need to inject the
123123
// authorizedFilter between the end of the where clause and those statements.
124124
filter := strings.Replace(getWorkspaces, "-- @authorize_filter", fmt.Sprintf(" AND %s", authorizedFilter.SQLString(rbac.NoACLConfig())), 1)
@@ -139,9 +139,9 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
139139
return nil, xerrors.Errorf("get authorized workspaces: %w", err)
140140
}
141141
defer rows.Close()
142-
var items []Workspace
142+
var items []GetWorkspacesRow
143143
for rows.Next() {
144-
var i Workspace
144+
var i GetWorkspacesRow
145145
if err := rows.Scan(
146146
&i.ID,
147147
&i.CreatedAt,
@@ -154,6 +154,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
154154
&i.AutostartSchedule,
155155
&i.Ttl,
156156
&i.LastUsedAt,
157+
&i.Count,
157158
); err != nil {
158159
return nil, err
159160
}

coderd/database/querier.go

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

coderd/database/queries.sql.go

Lines changed: 20 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/workspaces.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ LIMIT
1010

1111
-- name: GetWorkspaces :many
1212
SELECT
13-
workspaces.*
13+
workspaces.*, COUNT(*) OVER () as count
1414
FROM
1515
workspaces
1616
LEFT JOIN LATERAL (

coderd/provisionerjobs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
262262
}
263263
}
264264

265-
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout)
265+
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
266266
if err != nil {
267267
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
268268
Message: "Internal error reading job agent.",

coderd/telemetry/telemetry.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,11 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
380380
return nil
381381
})
382382
eg.Go(func() error {
383-
workspaces, err := r.options.Database.GetWorkspaces(ctx, database.GetWorkspacesParams{})
383+
workspaceRows, err := r.options.Database.GetWorkspaces(ctx, database.GetWorkspacesParams{})
384384
if err != nil {
385385
return xerrors.Errorf("get workspaces: %w", err)
386386
}
387+
workspaces := database.ConvertWorkspaceRows(workspaceRows)
387388
snapshot.Workspaces = make([]Workspace, 0, len(workspaces))
388389
for _, dbWorkspace := range workspaces {
389390
snapshot.Workspaces = append(snapshot.Workspaces, ConvertWorkspace(dbWorkspace))

0 commit comments

Comments
 (0)