Skip to content

Commit 7143343

Browse files
committed
Merge remote-tracking branch 'origin/main' into add-bug-report-link/kira-pilot
2 parents a2ae1cf + f09ab03 commit 7143343

Some content is hidden

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

61 files changed

+2194
-168
lines changed

agent/agent.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
328328
command := rawCommand
329329
if len(command) == 0 {
330330
command = shell
331+
if runtime.GOOS != "windows" {
332+
// On Linux and macOS, we should start a login
333+
// shell to consume juicy environment variables!
334+
command += " -l"
335+
}
331336
}
332337

333338
// OpenSSH executes all commands with the users current shell.
@@ -348,7 +353,6 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
348353
return nil, xerrors.Errorf("getting os executable: %w", err)
349354
}
350355
cmd.Env = append(cmd.Env, fmt.Sprintf("USER=%s", username))
351-
cmd.Env = append(cmd.Env, fmt.Sprintf(`PATH=%s%c%s`, os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
352356
// Git on Windows resolves with UNIX-style paths.
353357
// If using backslashes, it's unable to find the executable.
354358
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
@@ -363,7 +367,10 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
363367
// Load environment variables passed via the agent.
364368
// These should override all variables we manually specify.
365369
for key, value := range metadata.EnvironmentVariables {
366-
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
370+
// Expanding environment variables allows for customization
371+
// of the $PATH, among other variables. Customers can prepand
372+
// or append to the $PATH, so allowing expand is required!
373+
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, os.ExpandEnv(value)))
367374
}
368375

369376
// Agent-level environment variables should take over all!

agent/agent_test.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,6 @@ func TestAgent(t *testing.T) {
6868
require.True(t, strings.HasSuffix(strings.TrimSpace(string(output)), "gitssh --"))
6969
})
7070

71-
t.Run("PATHHasCoder", func(t *testing.T) {
72-
t.Parallel()
73-
session := setupSSHSession(t, agent.Metadata{})
74-
command := "sh -c 'echo $PATH'"
75-
if runtime.GOOS == "windows" {
76-
command = "cmd.exe /c echo %PATH%"
77-
}
78-
output, err := session.Output(command)
79-
require.NoError(t, err)
80-
ex, err := os.Executable()
81-
t.Log(ex)
82-
require.NoError(t, err)
83-
require.True(t, strings.Contains(strings.TrimSpace(string(output)), filepath.Dir(ex)))
84-
})
85-
8671
t.Run("SessionTTY", func(t *testing.T) {
8772
t.Parallel()
8873
if runtime.GOOS == "windows" {
@@ -182,6 +167,28 @@ func TestAgent(t *testing.T) {
182167
require.Equal(t, value, strings.TrimSpace(string(output)))
183168
})
184169

170+
t.Run("EnvironmentVariableExpansion", func(t *testing.T) {
171+
t.Parallel()
172+
key := "EXAMPLE"
173+
session := setupSSHSession(t, agent.Metadata{
174+
EnvironmentVariables: map[string]string{
175+
key: "$SOMETHINGNOTSET",
176+
},
177+
})
178+
command := "sh -c 'echo $" + key + "'"
179+
if runtime.GOOS == "windows" {
180+
command = "cmd.exe /c echo %" + key + "%"
181+
}
182+
output, err := session.Output(command)
183+
require.NoError(t, err)
184+
expect := ""
185+
if runtime.GOOS == "windows" {
186+
expect = "%EXAMPLE%"
187+
}
188+
// Output should be empty, because the variable is not set!
189+
require.Equal(t, expect, strings.TrimSpace(string(output)))
190+
})
191+
185192
t.Run("StartupScript", func(t *testing.T) {
186193
t.Parallel()
187194
tempPath := filepath.Join(os.TempDir(), "content.txt")

cli/agent.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67
_ "net/http/pprof" //nolint: gosec
78
"net/url"
@@ -138,6 +139,15 @@ func workspaceAgent() *cobra.Command {
138139
}
139140
}
140141

142+
executablePath, err := os.Executable()
143+
if err != nil {
144+
return xerrors.Errorf("getting os executable: %w", err)
145+
}
146+
err = os.Setenv("PATH", fmt.Sprintf("%s%c%s", os.Getenv("PATH"), filepath.ListSeparator, filepath.Dir(executablePath)))
147+
if err != nil {
148+
return xerrors.Errorf("add executable to $PATH: %w", err)
149+
}
150+
141151
closer := agent.New(client.ListenWorkspaceAgent, &agent.Options{
142152
Logger: logger,
143153
EnvironmentVariables: map[string]string{

cli/server.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/coreos/go-systemd/daemon"
3030
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
3131
"github.com/google/go-github/v43/github"
32+
"github.com/google/uuid"
3233
"github.com/pion/turn/v2"
3334
"github.com/pion/webrtc/v3"
3435
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -53,6 +54,7 @@ import (
5354
"github.com/coder/coder/coderd/database/databasefake"
5455
"github.com/coder/coder/coderd/devtunnel"
5556
"github.com/coder/coder/coderd/gitsshkey"
57+
"github.com/coder/coder/coderd/telemetry"
5658
"github.com/coder/coder/coderd/tracing"
5759
"github.com/coder/coder/coderd/turnconn"
5860
"github.com/coder/coder/codersdk"
@@ -81,6 +83,8 @@ func server() *cobra.Command {
8183
oauth2GithubClientSecret string
8284
oauth2GithubAllowedOrganizations []string
8385
oauth2GithubAllowSignups bool
86+
telemetryEnable bool
87+
telemetryURL string
8488
tlsCertFile string
8589
tlsClientCAFile string
8690
tlsClientAuth string
@@ -134,6 +138,7 @@ func server() *cobra.Command {
134138
}
135139

136140
config := createConfig(cmd)
141+
builtinPostgres := false
137142
// Only use built-in if PostgreSQL URL isn't specified!
138143
if !inMemoryDatabase && postgresURL == "" {
139144
var closeFunc func() error
@@ -142,6 +147,7 @@ func server() *cobra.Command {
142147
if err != nil {
143148
return err
144149
}
150+
builtinPostgres = true
145151
defer func() {
146152
// Gracefully shut PostgreSQL down!
147153
_ = closeFunc()
@@ -253,6 +259,7 @@ func server() *cobra.Command {
253259
SSHKeygenAlgorithm: sshKeygenAlgorithm,
254260
TURNServer: turnServer,
255261
TracerProvider: tracerProvider,
262+
Telemetry: telemetry.NewNoop(),
256263
}
257264

258265
if oauth2GithubClientSecret != "" {
@@ -285,6 +292,48 @@ func server() *cobra.Command {
285292
}
286293
}
287294

295+
deploymentID, err := options.Database.GetDeploymentID(cmd.Context())
296+
if errors.Is(err, sql.ErrNoRows) {
297+
err = nil
298+
}
299+
if err != nil {
300+
return xerrors.Errorf("get deployment id: %w", err)
301+
}
302+
if deploymentID == "" {
303+
deploymentID = uuid.NewString()
304+
err = options.Database.InsertDeploymentID(cmd.Context(), deploymentID)
305+
if err != nil {
306+
return xerrors.Errorf("set deployment id: %w", err)
307+
}
308+
}
309+
310+
// Parse the raw telemetry URL!
311+
telemetryURL, err := url.Parse(telemetryURL)
312+
if err != nil {
313+
return xerrors.Errorf("parse telemetry url: %w", err)
314+
}
315+
// Disable telemetry if the in-memory database is used unless explicitly defined!
316+
if inMemoryDatabase && !cmd.Flags().Changed("telemetry") {
317+
telemetryEnable = false
318+
}
319+
if telemetryEnable {
320+
options.Telemetry, err = telemetry.New(telemetry.Options{
321+
BuiltinPostgres: builtinPostgres,
322+
DeploymentID: deploymentID,
323+
Database: options.Database,
324+
Logger: logger.Named("telemetry"),
325+
URL: telemetryURL,
326+
GitHubOAuth: oauth2GithubClientID != "",
327+
Prometheus: promEnabled,
328+
STUN: len(stunServers) != 0,
329+
Tunnel: tunnel,
330+
})
331+
if err != nil {
332+
return xerrors.Errorf("create telemetry reporter: %w", err)
333+
}
334+
defer options.Telemetry.Close()
335+
}
336+
288337
coderAPI := coderd.New(options)
289338
client := codersdk.New(localURL)
290339
if tlsEnable {
@@ -438,6 +487,8 @@ func server() *cobra.Command {
438487
<-devTunnelErrChan
439488
}
440489

490+
// Ensures a last report can be sent before exit!
491+
options.Telemetry.Close()
441492
cmd.Println("Waiting for WebSocket connections to close...")
442493
shutdownConns()
443494
coderAPI.Close()
@@ -485,6 +536,9 @@ func server() *cobra.Command {
485536
"Specifies organizations the user must be a member of to authenticate with GitHub.")
486537
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
487538
"Specifies whether new users can sign up with GitHub.")
539+
cliflag.BoolVarP(root.Flags(), &telemetryEnable, "telemetry", "", "CODER_TELEMETRY", true, "Specifies whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.")
540+
cliflag.StringVarP(root.Flags(), &telemetryURL, "telemetry-url", "", "CODER_TELEMETRY_URL", "https://telemetry.coder.com", "Specifies a URL to send telemetry to.")
541+
_ = root.Flags().MarkHidden("telemetry-url")
488542
cliflag.BoolVarP(root.Flags(), &tlsEnable, "tls-enable", "", "CODER_TLS_ENABLE", false, "Specifies if TLS will be enabled")
489543
cliflag.StringVarP(root.Flags(), &tlsCertFile, "tls-cert-file", "", "CODER_TLS_CERT_FILE", "",
490544
"Specifies the path to the certificate for TLS. It requires a PEM-encoded file. "+

cli/server_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@ import (
88
"crypto/tls"
99
"crypto/x509"
1010
"crypto/x509/pkix"
11+
"encoding/json"
1112
"encoding/pem"
1213
"math/big"
1314
"net"
1415
"net/http"
16+
"net/http/httptest"
1517
"net/url"
1618
"os"
1719
"runtime"
1820
"strings"
1921
"testing"
2022
"time"
2123

24+
"github.com/go-chi/chi"
2225
"github.com/stretchr/testify/assert"
2326
"github.com/stretchr/testify/require"
2427
"go.uber.org/goleak"
2528

2629
"github.com/coder/coder/cli/clitest"
2730
"github.com/coder/coder/coderd/database/postgres"
31+
"github.com/coder/coder/coderd/telemetry"
2832
"github.com/coder/coder/codersdk"
2933
)
3034

@@ -233,6 +237,37 @@ func TestServer(t *testing.T) {
233237
require.ErrorIs(t, <-errC, context.Canceled)
234238
require.Error(t, goleak.Find())
235239
})
240+
t.Run("Telemetry", func(t *testing.T) {
241+
t.Parallel()
242+
ctx, cancelFunc := context.WithCancel(context.Background())
243+
defer cancelFunc()
244+
245+
deployment := make(chan struct{}, 64)
246+
snapshot := make(chan *telemetry.Snapshot, 64)
247+
r := chi.NewRouter()
248+
r.Post("/deployment", func(w http.ResponseWriter, r *http.Request) {
249+
w.WriteHeader(http.StatusAccepted)
250+
deployment <- struct{}{}
251+
})
252+
r.Post("/snapshot", func(w http.ResponseWriter, r *http.Request) {
253+
w.WriteHeader(http.StatusAccepted)
254+
ss := &telemetry.Snapshot{}
255+
err := json.NewDecoder(r.Body).Decode(ss)
256+
require.NoError(t, err)
257+
snapshot <- ss
258+
})
259+
server := httptest.NewServer(r)
260+
t.Cleanup(server.Close)
261+
262+
root, _ := clitest.New(t, "server", "--in-memory", "--address", ":0", "--telemetry", "--telemetry-url", server.URL)
263+
errC := make(chan error)
264+
go func() {
265+
errC <- root.ExecuteContext(ctx)
266+
}()
267+
268+
<-deployment
269+
<-snapshot
270+
})
236271
}
237272

238273
func generateTLSCertificate(t testing.TB) (certPath, keyPath string) {

coderd/coderd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/coder/coder/coderd/httpapi"
2828
"github.com/coder/coder/coderd/httpmw"
2929
"github.com/coder/coder/coderd/rbac"
30+
"github.com/coder/coder/coderd/telemetry"
3031
"github.com/coder/coder/coderd/tracing"
3132
"github.com/coder/coder/coderd/turnconn"
3233
"github.com/coder/coder/coderd/wsconncache"
@@ -54,6 +55,7 @@ type Options struct {
5455
ICEServers []webrtc.ICEServer
5556
SecureAuthCookie bool
5657
SSHKeygenAlgorithm gitsshkey.Algorithm
58+
Telemetry telemetry.Reporter
5759
TURNServer *turnconn.Server
5860
TracerProvider *sdktrace.TracerProvider
5961
}

coderd/coderdtest/coderdtest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/spf13/afero"
2929

3030
"github.com/coder/coder/coderd/rbac"
31+
"github.com/coder/coder/coderd/telemetry"
3132
"github.com/coder/coder/coderd/util/ptr"
3233

3334
"cloud.google.com/go/compute/metadata"
@@ -166,6 +167,7 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, *coderd.API)
166167
TURNServer: turnServer,
167168
APIRateLimit: options.APIRateLimit,
168169
Authorizer: options.Authorizer,
170+
Telemetry: telemetry.NewNoop(),
169171
})
170172
srv.Config.Handler = coderAPI.Handler
171173
if options.IncludeProvisionerD {

0 commit comments

Comments
 (0)