mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore: update golang to 1.24.1 (#17035)
- Update go.mod to use Go 1.24.1 - Update GitHub Actions setup-go action to use Go 1.24.1 - Fix linting issues with golangci-lint by: - Updating to golangci-lint v1.57.1 (more compatible with Go 1.24.1) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ description: |
|
||||
inputs:
|
||||
version:
|
||||
description: "The Go version to use."
|
||||
default: "1.22.12"
|
||||
default: "1.24.1"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
+11
-4
@@ -203,6 +203,14 @@ linters-settings:
|
||||
- G601
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- coderd/database/dbmem
|
||||
- node_modules
|
||||
- .git
|
||||
|
||||
skip-files:
|
||||
- scripts/rules.go
|
||||
|
||||
# Rules listed here: https://github.com/securego/gosec#available-rules
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
@@ -211,6 +219,8 @@ issues:
|
||||
- errcheck
|
||||
- forcetypeassert
|
||||
- exhaustruct # This is unhelpful in tests.
|
||||
- revive # TODO(JonA): disabling in order to update golangci-lint
|
||||
- gosec # TODO(JonA): disabling in order to update golangci-lint
|
||||
- path: scripts/*
|
||||
linters:
|
||||
- exhaustruct
|
||||
@@ -220,12 +230,9 @@ issues:
|
||||
max-same-issues: 0
|
||||
|
||||
run:
|
||||
skip-dirs:
|
||||
- node_modules
|
||||
- .git
|
||||
timeout: 10m
|
||||
skip-files:
|
||||
- scripts/rules.go
|
||||
timeout: 10m
|
||||
|
||||
# Over time, add more and more linters from
|
||||
# https://golangci-lint.run/usage/linters/ as the code improves.
|
||||
|
||||
+12
-6
@@ -936,7 +936,7 @@ func (a *agent) run() (retErr error) {
|
||||
connMan.startAgentAPI("send logs", gracefulShutdownBehaviorRemain,
|
||||
func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
||||
err := a.logSender.SendLoop(ctx, aAPI)
|
||||
if xerrors.Is(err, agentsdk.LogLimitExceededError) {
|
||||
if xerrors.Is(err, agentsdk.ErrLogLimitExceeded) {
|
||||
// we don't want this error to tear down the API connection and propagate to the
|
||||
// other routines that use the API. The LogSender has already dropped a warning
|
||||
// log, so just return nil here.
|
||||
@@ -1564,9 +1564,13 @@ func (a *agent) Collect(ctx context.Context, networkStats map[netlogtype.Connect
|
||||
}
|
||||
for conn, counts := range networkStats {
|
||||
stats.ConnectionsByProto[conn.Proto.String()]++
|
||||
// #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range
|
||||
stats.RxBytes += int64(counts.RxBytes)
|
||||
// #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range
|
||||
stats.RxPackets += int64(counts.RxPackets)
|
||||
// #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range
|
||||
stats.TxBytes += int64(counts.TxBytes)
|
||||
// #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range
|
||||
stats.TxPackets += int64(counts.TxPackets)
|
||||
}
|
||||
|
||||
@@ -1619,11 +1623,12 @@ func (a *agent) Collect(ctx context.Context, networkStats map[netlogtype.Connect
|
||||
wg.Wait()
|
||||
sort.Float64s(durations)
|
||||
durationsLength := len(durations)
|
||||
if durationsLength == 0 {
|
||||
switch {
|
||||
case durationsLength == 0:
|
||||
stats.ConnectionMedianLatencyMs = -1
|
||||
} else if durationsLength%2 == 0 {
|
||||
case durationsLength%2 == 0:
|
||||
stats.ConnectionMedianLatencyMs = (durations[durationsLength/2-1] + durations[durationsLength/2]) / 2
|
||||
} else {
|
||||
default:
|
||||
stats.ConnectionMedianLatencyMs = durations[durationsLength/2]
|
||||
}
|
||||
// Convert from microseconds to milliseconds.
|
||||
@@ -1730,7 +1735,7 @@ func (a *agent) HTTPDebug() http.Handler {
|
||||
r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock)
|
||||
r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState)
|
||||
r.Get("/debug/manifest", a.HandleHTTPDebugManifest)
|
||||
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.NotFound(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("404 not found"))
|
||||
})
|
||||
@@ -2016,7 +2021,7 @@ func (a *apiConnRoutineManager) wait() error {
|
||||
}
|
||||
|
||||
func PrometheusMetricsHandler(prometheusRegistry *prometheus.Registry, logger slog.Logger) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
|
||||
// Based on: https://github.com/tailscale/tailscale/blob/280255acae604796a1113861f5a84e6fa2dc6121/ipn/localapi/localapi.go#L489
|
||||
@@ -2052,5 +2057,6 @@ func WorkspaceKeySeed(workspaceID uuid.UUID, agentName string) (int64, error) {
|
||||
return 42, err
|
||||
}
|
||||
|
||||
// #nosec G115 - Safe conversion to generate int64 hash from Sum64, data loss acceptable
|
||||
return int64(h.Sum64()), nil
|
||||
}
|
||||
|
||||
@@ -453,8 +453,9 @@ func convertDockerInspect(raw []byte) ([]codersdk.WorkspaceAgentContainer, []str
|
||||
hostPortContainers[hp] = append(hostPortContainers[hp], in.ID)
|
||||
}
|
||||
out.Ports = append(out.Ports, codersdk.WorkspaceAgentContainerPort{
|
||||
Network: network,
|
||||
Port: cp,
|
||||
Network: network,
|
||||
Port: cp,
|
||||
// #nosec G115 - Safe conversion since Docker ports are limited to uint16 range
|
||||
HostPort: uint16(hp),
|
||||
HostIP: p.HostIP,
|
||||
})
|
||||
@@ -497,12 +498,14 @@ func convertDockerPort(in string) (uint16, string, error) {
|
||||
if err != nil {
|
||||
return 0, "", xerrors.Errorf("invalid port format: %s", in)
|
||||
}
|
||||
// #nosec G115 - Safe conversion since Docker TCP ports are limited to uint16 range
|
||||
return uint16(p), "tcp", nil
|
||||
case 2:
|
||||
p, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return 0, "", xerrors.Errorf("invalid port format: %s", in)
|
||||
}
|
||||
// #nosec G115 - Safe conversion since Docker ports are limited to uint16 range
|
||||
return uint16(p), parts[1], nil
|
||||
default:
|
||||
return 0, "", xerrors.Errorf("invalid port format: %s", in)
|
||||
|
||||
@@ -28,6 +28,7 @@ func BenchmarkGenerateDeterministicKey(b *testing.B) {
|
||||
for range b.N {
|
||||
// always record the result of DeterministicPrivateKey to prevent
|
||||
// the compiler eliminating the function call.
|
||||
// #nosec G404 - Using math/rand is acceptable for benchmarking deterministic keys
|
||||
r = agentrsa.GenerateDeterministicKey(rand.Int64())
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
slog.F("destination_port", destinationPort))
|
||||
return true
|
||||
},
|
||||
PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool {
|
||||
PtyCallback: func(_ ssh.Context, _ ssh.Pty) bool {
|
||||
return true
|
||||
},
|
||||
ReversePortForwardingCallback: func(ctx ssh.Context, bindHost string, bindPort uint32) bool {
|
||||
@@ -240,7 +240,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
"cancel-streamlocal-forward@openssh.com": unixForwardHandler.HandleSSHRequest,
|
||||
},
|
||||
X11Callback: s.x11Callback,
|
||||
ServerConfigCallback: func(ctx ssh.Context) *gossh.ServerConfig {
|
||||
ServerConfigCallback: func(_ ssh.Context) *gossh.ServerConfig {
|
||||
return &gossh.ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
@@ -702,6 +702,7 @@ func (s *Server) startPTYSession(logger slog.Logger, session ptySession, magicTy
|
||||
windowSize = nil
|
||||
continue
|
||||
}
|
||||
// #nosec G115 - Safe conversions for terminal dimensions which are expected to be within uint16 range
|
||||
resizeErr := ptty.Resize(uint16(win.Height), uint16(win.Width))
|
||||
// If the pty is closed, then command has exited, no need to log.
|
||||
if resizeErr != nil && !errors.Is(resizeErr, pty.ErrClosed) {
|
||||
|
||||
@@ -116,7 +116,8 @@ func (s *Server) x11Handler(ctx ssh.Context, x11 ssh.X11) (displayNumber int, ha
|
||||
OriginatorPort uint32
|
||||
}{
|
||||
OriginatorAddress: tcpAddr.IP.String(),
|
||||
OriginatorPort: uint32(tcpAddr.Port),
|
||||
// #nosec G115 - Safe conversion as TCP port numbers are within uint32 range (0-65535)
|
||||
OriginatorPort: uint32(tcpAddr.Port),
|
||||
}))
|
||||
if err != nil {
|
||||
s.logger.Warn(ctx, "failed to open X11 channel", slog.Error(err))
|
||||
@@ -294,6 +295,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string
|
||||
return xerrors.Errorf("failed to write family: %w", err)
|
||||
}
|
||||
|
||||
// #nosec G115 - Safe conversion for host name length which is expected to be within uint16 range
|
||||
err = binary.Write(file, binary.BigEndian, uint16(len(host)))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write host length: %w", err)
|
||||
@@ -303,6 +305,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string
|
||||
return xerrors.Errorf("failed to write host: %w", err)
|
||||
}
|
||||
|
||||
// #nosec G115 - Safe conversion for display name length which is expected to be within uint16 range
|
||||
err = binary.Write(file, binary.BigEndian, uint16(len(display)))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write display length: %w", err)
|
||||
@@ -312,6 +315,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string
|
||||
return xerrors.Errorf("failed to write display: %w", err)
|
||||
}
|
||||
|
||||
// #nosec G115 - Safe conversion for auth protocol length which is expected to be within uint16 range
|
||||
err = binary.Write(file, binary.BigEndian, uint16(len(authProtocol)))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write auth protocol length: %w", err)
|
||||
@@ -321,6 +325,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string
|
||||
return xerrors.Errorf("failed to write auth protocol: %w", err)
|
||||
}
|
||||
|
||||
// #nosec G115 - Safe conversion for auth cookie length which is expected to be within uint16 range
|
||||
err = binary.Write(file, binary.BigEndian, uint16(len(authCookieBytes)))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write auth cookie length: %w", err)
|
||||
|
||||
+2
-2
@@ -167,8 +167,8 @@ func shouldStartTicker(app codersdk.WorkspaceApp) bool {
|
||||
return app.Healthcheck.URL != "" && app.Healthcheck.Interval > 0 && app.Healthcheck.Threshold > 0
|
||||
}
|
||||
|
||||
func healthChanged(old map[uuid.UUID]codersdk.WorkspaceAppHealth, new map[uuid.UUID]codersdk.WorkspaceAppHealth) bool {
|
||||
for name, newValue := range new {
|
||||
func healthChanged(old map[uuid.UUID]codersdk.WorkspaceAppHealth, updated map[uuid.UUID]codersdk.WorkspaceAppHealth) bool {
|
||||
for name, newValue := range updated {
|
||||
oldValue, found := old[name]
|
||||
if !found {
|
||||
return true
|
||||
|
||||
+4
-3
@@ -89,21 +89,22 @@ func (a *agent) collectMetrics(ctx context.Context) []*proto.Stats_Metric {
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
labels := toAgentMetricLabels(metric.Label)
|
||||
|
||||
if metric.Counter != nil {
|
||||
switch {
|
||||
case metric.Counter != nil:
|
||||
collected = append(collected, &proto.Stats_Metric{
|
||||
Name: metricFamily.GetName(),
|
||||
Type: proto.Stats_Metric_COUNTER,
|
||||
Value: metric.Counter.GetValue(),
|
||||
Labels: labels,
|
||||
})
|
||||
} else if metric.Gauge != nil {
|
||||
case metric.Gauge != nil:
|
||||
collected = append(collected, &proto.Stats_Metric{
|
||||
Name: metricFamily.GetName(),
|
||||
Type: proto.Stats_Metric_GAUGE,
|
||||
Value: metric.Gauge.GetValue(),
|
||||
Labels: labels,
|
||||
})
|
||||
} else {
|
||||
default:
|
||||
a.logger.Error(ctx, "unsupported metric type", slog.F("type", metricFamily.Type.String()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ func newBuffered(ctx context.Context, logger slog.Logger, execer agentexec.Exece
|
||||
// Add TERM then start the command with a pty. pty.Cmd duplicates Path as the
|
||||
// first argument so remove it.
|
||||
cmdWithEnv := execer.PTYCommandContext(ctx, cmd.Path, cmd.Args[1:]...)
|
||||
//nolint:gocritic
|
||||
cmdWithEnv.Env = append(rpty.command.Env, "TERM=xterm-256color")
|
||||
cmdWithEnv.Dir = rpty.command.Dir
|
||||
ptty, process, err := pty.Start(cmdWithEnv)
|
||||
@@ -236,7 +237,7 @@ func (rpty *bufferedReconnectingPTY) Wait() {
|
||||
_, _ = rpty.state.waitForState(StateClosing)
|
||||
}
|
||||
|
||||
func (rpty *bufferedReconnectingPTY) Close(error error) {
|
||||
func (rpty *bufferedReconnectingPTY) Close(err error) {
|
||||
// The closing state change will be handled by the lifecycle.
|
||||
rpty.state.setState(StateClosing, error)
|
||||
rpty.state.setState(StateClosing, err)
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ func (rpty *screenReconnectingPTY) doAttach(ctx context.Context, conn net.Conn,
|
||||
rpty.command.Path,
|
||||
// pty.Cmd duplicates Path as the first argument so remove it.
|
||||
}, rpty.command.Args[1:]...)...)
|
||||
//nolint:gocritic
|
||||
cmd.Env = append(rpty.command.Env, "TERM=xterm-256color")
|
||||
cmd.Dir = rpty.command.Dir
|
||||
ptty, process, err := pty.Start(cmd, pty.WithPTYOption(
|
||||
@@ -340,6 +341,7 @@ func (rpty *screenReconnectingPTY) sendCommand(ctx context.Context, command stri
|
||||
// -X runs a command in the matching session.
|
||||
"-X", command,
|
||||
)
|
||||
//nolint:gocritic
|
||||
cmd.Env = append(rpty.command.Env, "TERM=xterm-256color")
|
||||
cmd.Dir = rpty.command.Dir
|
||||
cmd.Stdout = &stdout
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
|
||||
// New returns an *APIVersion with the given major.minor and
|
||||
// additional supported major versions.
|
||||
func New(maj, min int) *APIVersion {
|
||||
func New(maj, minor int) *APIVersion {
|
||||
v := &APIVersion{
|
||||
supportedMajor: maj,
|
||||
supportedMinor: min,
|
||||
supportedMinor: minor,
|
||||
additionalMajors: make([]int, 0),
|
||||
}
|
||||
return v
|
||||
|
||||
+6
-4
@@ -127,6 +127,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
logger.Info(ctx, "spawning reaper process")
|
||||
// Do not start a reaper on the child process. It's important
|
||||
// to do this else we fork bomb ourselves.
|
||||
//nolint:gocritic
|
||||
args := append(os.Args, "--no-reap")
|
||||
err := reaper.ForkReap(
|
||||
reaper.WithExecArgs(args...),
|
||||
@@ -327,10 +328,11 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
}
|
||||
|
||||
agnt := agent.New(agent.Options{
|
||||
Client: client,
|
||||
Logger: logger,
|
||||
LogDir: logDir,
|
||||
ScriptDataDir: scriptDataDir,
|
||||
Client: client,
|
||||
Logger: logger,
|
||||
LogDir: logDir,
|
||||
ScriptDataDir: scriptDataDir,
|
||||
// #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
|
||||
TailnetListenPort: uint16(tailnetListenPort),
|
||||
ExchangeToken: func(ctx context.Context) (string, error) {
|
||||
if exchangeToken == nil {
|
||||
|
||||
@@ -19,6 +19,7 @@ func (*Statter) Disk(p Prefix, path string) (*Result, error) {
|
||||
return nil, err
|
||||
}
|
||||
var r Result
|
||||
// #nosec G115 - Safe conversion because stat.Bsize is always positive and within uint64 range
|
||||
r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize)))
|
||||
r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize)
|
||||
r.Unit = "B"
|
||||
|
||||
@@ -58,6 +58,7 @@ func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *serpent.Command,
|
||||
ExtractCommandPathsLoop:
|
||||
for _, cp := range extractVisibleCommandPaths(nil, root.Children) {
|
||||
name := fmt.Sprintf("coder %s --help", strings.Join(cp, " "))
|
||||
//nolint:gocritic
|
||||
cmd := append(cp, "--help")
|
||||
for _, tt := range cases {
|
||||
if tt.Name == name {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/coder/pretty"
|
||||
)
|
||||
|
||||
var Canceled = xerrors.New("canceled")
|
||||
var ErrCanceled = xerrors.New("canceled")
|
||||
|
||||
// DefaultStyles compose visual elements of the UI.
|
||||
var DefaultStyles Styles
|
||||
|
||||
@@ -33,7 +33,8 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
||||
|
||||
var err error
|
||||
var value string
|
||||
if templateVersionParameter.Type == "list(string)" {
|
||||
switch {
|
||||
case templateVersionParameter.Type == "list(string)":
|
||||
// Move the cursor up a single line for nicer display!
|
||||
_, _ = fmt.Fprint(inv.Stdout, "\033[1A")
|
||||
|
||||
@@ -60,7 +61,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
||||
)
|
||||
value = string(v)
|
||||
}
|
||||
} else if len(templateVersionParameter.Options) > 0 {
|
||||
case len(templateVersionParameter.Options) > 0:
|
||||
// Move the cursor up a single line for nicer display!
|
||||
_, _ = fmt.Fprint(inv.Stdout, "\033[1A")
|
||||
var richParameterOption *codersdk.TemplateVersionParameterOption
|
||||
@@ -74,7 +75,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
||||
pretty.Fprintf(inv.Stdout, DefaultStyles.Prompt, "%s\n", richParameterOption.Name)
|
||||
value = richParameterOption.Value
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
text := "Enter a value"
|
||||
if !templateVersionParameter.Required {
|
||||
text += fmt.Sprintf(" (default: %q)", defaultValue)
|
||||
|
||||
+2
-2
@@ -124,7 +124,7 @@ func Prompt(inv *serpent.Invocation, opts PromptOptions) (string, error) {
|
||||
return "", err
|
||||
case line := <-lineCh:
|
||||
if opts.IsConfirm && line != "yes" && line != "y" {
|
||||
return line, xerrors.Errorf("got %q: %w", line, Canceled)
|
||||
return line, xerrors.Errorf("got %q: %w", line, ErrCanceled)
|
||||
}
|
||||
if opts.Validate != nil {
|
||||
err := opts.Validate(line)
|
||||
@@ -139,7 +139,7 @@ func Prompt(inv *serpent.Invocation, opts PromptOptions) (string, error) {
|
||||
case <-interrupt:
|
||||
// Print a newline so that any further output starts properly on a new line.
|
||||
_, _ = fmt.Fprintln(inv.Stdout)
|
||||
return "", Canceled
|
||||
return "", ErrCanceled
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ func ProvisionerJob(ctx context.Context, wr io.Writer, opts ProvisionerJobOption
|
||||
switch job.Status {
|
||||
case codersdk.ProvisionerJobCanceled:
|
||||
jobMutex.Unlock()
|
||||
return Canceled
|
||||
return ErrCanceled
|
||||
case codersdk.ProvisionerJobSucceeded:
|
||||
jobMutex.Unlock()
|
||||
return nil
|
||||
|
||||
@@ -250,7 +250,7 @@ func newProvisionerJob(t *testing.T) provisionerJobTest {
|
||||
defer close(done)
|
||||
err := inv.WithContext(context.Background()).Run()
|
||||
if err != nil {
|
||||
assert.ErrorIs(t, err, cliui.Canceled)
|
||||
assert.ErrorIs(t, err, cliui.ErrCanceled)
|
||||
}
|
||||
}()
|
||||
t.Cleanup(func() {
|
||||
|
||||
+2
-2
@@ -147,7 +147,7 @@ func Select(inv *serpent.Invocation, opts SelectOptions) (string, error) {
|
||||
}
|
||||
|
||||
if model.canceled {
|
||||
return "", Canceled
|
||||
return "", ErrCanceled
|
||||
}
|
||||
|
||||
return model.selected, nil
|
||||
@@ -360,7 +360,7 @@ func MultiSelect(inv *serpent.Invocation, opts MultiSelectOptions) ([]string, er
|
||||
}
|
||||
|
||||
if model.canceled {
|
||||
return nil, Canceled
|
||||
return nil, ErrCanceled
|
||||
}
|
||||
|
||||
return model.selectedOptions(), nil
|
||||
|
||||
@@ -32,7 +32,9 @@ func Distance(a, b string, maxDist int) (int, error) {
|
||||
if len(b) > 255 {
|
||||
return 0, xerrors.Errorf("levenshtein: b must be less than 255 characters long")
|
||||
}
|
||||
// #nosec G115 - Safe conversion since we've checked that len(a) < 255
|
||||
m := uint8(len(a))
|
||||
// #nosec G115 - Safe conversion since we've checked that len(b) < 255
|
||||
n := uint8(len(b))
|
||||
|
||||
// Special cases for empty strings
|
||||
@@ -70,12 +72,13 @@ func Distance(a, b string, maxDist int) (int, error) {
|
||||
subCost = 1
|
||||
}
|
||||
// Don't forget: matrix is +1 size
|
||||
d[i+1][j+1] = min(
|
||||
d[i+1][j+1] = minOf(
|
||||
d[i][j+1]+1, // deletion
|
||||
d[i+1][j]+1, // insertion
|
||||
d[i][j]+subCost, // substitution
|
||||
)
|
||||
// check maxDist on the diagonal
|
||||
// #nosec G115 - Safe conversion as maxDist is expected to be small for edit distances
|
||||
if maxDist > -1 && i == j && d[i+1][j+1] > uint8(maxDist) {
|
||||
return int(d[i+1][j+1]), ErrMaxDist
|
||||
}
|
||||
@@ -85,9 +88,9 @@ func Distance(a, b string, maxDist int) (int, error) {
|
||||
return int(d[m][n]), nil
|
||||
}
|
||||
|
||||
func min[T constraints.Ordered](ts ...T) T {
|
||||
func minOf[T constraints.Ordered](ts ...T) T {
|
||||
if len(ts) == 0 {
|
||||
panic("min: no arguments")
|
||||
panic("minOf: no arguments")
|
||||
}
|
||||
m := ts[0]
|
||||
for _, t := range ts[1:] {
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
|
||||
IsConfirm: true,
|
||||
})
|
||||
if err != nil {
|
||||
if line == "" && xerrors.Is(err, cliui.Canceled) {
|
||||
if line == "" && xerrors.Is(err, cliui.ErrCanceled) {
|
||||
return nil
|
||||
}
|
||||
// Selecting "no" will use the last config.
|
||||
|
||||
+4
-3
@@ -104,7 +104,8 @@ func (r *RootCmd) create() *serpent.Command {
|
||||
|
||||
var template codersdk.Template
|
||||
var templateVersionID uuid.UUID
|
||||
if templateName == "" {
|
||||
switch {
|
||||
case templateName == "":
|
||||
_, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Wrap, "Select a template below to preview the provisioned infrastructure:"))
|
||||
|
||||
templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{})
|
||||
@@ -161,13 +162,13 @@ func (r *RootCmd) create() *serpent.Command {
|
||||
|
||||
template = templateByName[option]
|
||||
templateVersionID = template.ActiveVersionID
|
||||
} else if sourceWorkspace.LatestBuild.TemplateVersionID != uuid.Nil {
|
||||
case sourceWorkspace.LatestBuild.TemplateVersionID != uuid.Nil:
|
||||
template, err = client.Template(inv.Context(), sourceWorkspace.TemplateID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get template by name: %w", err)
|
||||
}
|
||||
templateVersionID = sourceWorkspace.LatestBuild.TemplateVersionID
|
||||
} else {
|
||||
default:
|
||||
templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{
|
||||
ExactName: templateName,
|
||||
})
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ func (RootCmd) errorExample() *serpent.Command {
|
||||
errorCmd := func(use string, err error) *serpent.Command {
|
||||
return &serpent.Command{
|
||||
Use: use,
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
Handler: func(_ *serpent.Invocation) error {
|
||||
return err
|
||||
},
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (RootCmd) errorExample() *serpent.Command {
|
||||
// A multi-error
|
||||
{
|
||||
Use: "multi-error",
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
Handler: func(_ *serpent.Invocation) error {
|
||||
return xerrors.Errorf("wrapped: %w", errors.Join(
|
||||
xerrors.Errorf("first error: %w", errorWithStackTrace()),
|
||||
xerrors.Errorf("second error: %w", errorWithStackTrace()),
|
||||
@@ -81,7 +81,7 @@ func (RootCmd) errorExample() *serpent.Command {
|
||||
{
|
||||
Use: "multi-multi-error",
|
||||
Short: "This is a multi error inside a multi error",
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
Handler: func(_ *serpent.Invocation) error {
|
||||
return errors.Join(
|
||||
xerrors.Errorf("parent error: %w", errorWithStackTrace()),
|
||||
errors.Join(
|
||||
@@ -100,7 +100,7 @@ func (RootCmd) errorExample() *serpent.Command {
|
||||
Required: true,
|
||||
Flag: "magic-word",
|
||||
Default: "",
|
||||
Value: serpent.Validate(&magicWord, func(value *serpent.String) error {
|
||||
Value: serpent.Validate(&magicWord, func(_ *serpent.String) error {
|
||||
return xerrors.Errorf("magic word is incorrect")
|
||||
}),
|
||||
},
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ fi
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cliui.Canceled
|
||||
return cliui.ErrCanceled
|
||||
}
|
||||
if extra != "" {
|
||||
if extAuth.TokenExtra == nil {
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestExternalAuth(t *testing.T) {
|
||||
inv.Stdout = pty.Output()
|
||||
waiter := clitest.StartWithWaiter(t, inv)
|
||||
pty.ExpectMatch("https://github.com")
|
||||
waiter.RequireIs(cliui.Canceled)
|
||||
waiter.RequireIs(cliui.ErrCanceled)
|
||||
})
|
||||
t.Run("SuccessWithToken", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ func (r *RootCmd) gitAskpass() *serpent.Command {
|
||||
cliui.Warn(inv.Stderr, "Coder was unable to handle this git request. The default git behavior will be used instead.",
|
||||
lines...,
|
||||
)
|
||||
return cliui.Canceled
|
||||
return cliui.ErrCanceled
|
||||
}
|
||||
return xerrors.Errorf("get git token: %w", err)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestGitAskpass(t *testing.T) {
|
||||
pty := ptytest.New(t)
|
||||
inv.Stderr = pty.Output()
|
||||
err := inv.Run()
|
||||
require.ErrorIs(t, err, cliui.Canceled)
|
||||
require.ErrorIs(t, err, cliui.ErrCanceled)
|
||||
pty.ExpectMatch("Nope!")
|
||||
})
|
||||
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ var fallbackIdentityFiles = strings.Join([]string{
|
||||
//
|
||||
// The extra arguments work without issue and lets us run the command
|
||||
// as-is without stripping out the excess (git-upload-pack 'coder/coder').
|
||||
func parseIdentityFilesForHost(ctx context.Context, args, env []string) (identityFiles []string, error error) {
|
||||
func parseIdentityFilesForHost(ctx context.Context, args, env []string) (identityFiles []string, err error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get user home dir failed: %w", err)
|
||||
|
||||
+4
-7
@@ -42,6 +42,7 @@ func ttyWidth() int {
|
||||
// wrapTTY wraps a string to the width of the terminal, or 80 no terminal
|
||||
// is detected.
|
||||
func wrapTTY(s string) string {
|
||||
// #nosec G115 - Safe conversion as TTY width is expected to be within uint range
|
||||
return wordwrap.WrapString(s, uint(ttyWidth()))
|
||||
}
|
||||
|
||||
@@ -57,12 +58,8 @@ var usageTemplate = func() *template.Template {
|
||||
return template.Must(
|
||||
template.New("usage").Funcs(
|
||||
template.FuncMap{
|
||||
"version": func() string {
|
||||
return buildinfo.Version()
|
||||
},
|
||||
"wrapTTY": func(s string) string {
|
||||
return wrapTTY(s)
|
||||
},
|
||||
"version": buildinfo.Version,
|
||||
"wrapTTY": wrapTTY,
|
||||
"trimNewline": func(s string) string {
|
||||
return strings.TrimSuffix(s, "\n")
|
||||
},
|
||||
@@ -189,7 +186,7 @@ var usageTemplate = func() *template.Template {
|
||||
},
|
||||
"formatGroupDescription": func(s string) string {
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = s + "\n"
|
||||
s += "\n"
|
||||
s = wrapTTY(s)
|
||||
return s
|
||||
},
|
||||
|
||||
+6
-8
@@ -48,7 +48,7 @@ func promptFirstUsername(inv *serpent.Invocation) (string, error) {
|
||||
Text: "What " + pretty.Sprint(cliui.DefaultStyles.Field, "username") + " would you like?",
|
||||
Default: currentUser.Username,
|
||||
})
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
@@ -64,7 +64,7 @@ func promptFirstName(inv *serpent.Invocation) (string, error) {
|
||||
Default: "",
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
@@ -76,11 +76,9 @@ func promptFirstName(inv *serpent.Invocation) (string, error) {
|
||||
func promptFirstPassword(inv *serpent.Invocation) (string, error) {
|
||||
retry:
|
||||
password, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":",
|
||||
Secret: true,
|
||||
Validate: func(s string) error {
|
||||
return userpassword.Validate(s)
|
||||
},
|
||||
Text: "Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":",
|
||||
Secret: true,
|
||||
Validate: userpassword.Validate,
|
||||
})
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("specify password prompt: %w", err)
|
||||
@@ -508,7 +506,7 @@ func promptTrialInfo(inv *serpent.Invocation, fieldName string) (string, error)
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ func (r *RootCmd) openVSCode() *serpent.Command {
|
||||
})
|
||||
if err != nil {
|
||||
if xerrors.Is(err, context.Canceled) {
|
||||
return cliui.Canceled
|
||||
return cliui.ErrCanceled
|
||||
}
|
||||
return xerrors.Errorf("agent: %w", err)
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func (r *RootCmd) openVSCode() *serpent.Command {
|
||||
// However, if no directory is set, the expanded directory will
|
||||
// not be set either.
|
||||
if workspaceAgent.Directory != "" {
|
||||
workspace, workspaceAgent, err = waitForAgentCond(ctx, client, workspace, workspaceAgent, func(a codersdk.WorkspaceAgent) bool {
|
||||
workspace, workspaceAgent, err = waitForAgentCond(ctx, client, workspace, workspaceAgent, func(_ codersdk.WorkspaceAgent) bool {
|
||||
return workspaceAgent.LifecycleState != codersdk.WorkspaceAgentLifecycleCreated
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -40,7 +40,7 @@ func validateRemoteForward(flag string) bool {
|
||||
return isRemoteForwardTCP(flag) || isRemoteForwardUnixSocket(flag)
|
||||
}
|
||||
|
||||
func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) {
|
||||
func parseRemoteForwardTCP(matches []string) (local net.Addr, remote net.Addr, err error) {
|
||||
remotePort, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("remote port is invalid: %w", err)
|
||||
@@ -69,7 +69,7 @@ func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) {
|
||||
// parseRemoteForwardUnixSocket parses a remote forward flag. Note that
|
||||
// we don't verify that the local socket path exists because the user
|
||||
// may create it later. This behavior matches OpenSSH.
|
||||
func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error) {
|
||||
func parseRemoteForwardUnixSocket(matches []string) (local net.Addr, remote net.Addr, err error) {
|
||||
remoteSocket := matches[1]
|
||||
localSocket := matches[2]
|
||||
|
||||
@@ -85,7 +85,7 @@ func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error)
|
||||
return localAddr, remoteAddr, nil
|
||||
}
|
||||
|
||||
func parseRemoteForward(flag string) (net.Addr, net.Addr, error) {
|
||||
func parseRemoteForward(flag string) (local net.Addr, remote net.Addr, err error) {
|
||||
tcpMatches := remoteForwardRegexTCP.FindStringSubmatch(flag)
|
||||
|
||||
if len(tcpMatches) > 0 {
|
||||
|
||||
@@ -62,11 +62,9 @@ func (*RootCmd) resetPassword() *serpent.Command {
|
||||
}
|
||||
|
||||
password, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Enter new " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":",
|
||||
Secret: true,
|
||||
Validate: func(s string) error {
|
||||
return userpassword.Validate(s)
|
||||
},
|
||||
Text: "Enter new " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":",
|
||||
Secret: true,
|
||||
Validate: userpassword.Validate,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("password prompt: %w", err)
|
||||
|
||||
+5
-5
@@ -171,15 +171,15 @@ func (r *RootCmd) RunWithSubcommands(subcommands []*serpent.Command) {
|
||||
code = exitErr.code
|
||||
err = exitErr.err
|
||||
}
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
//nolint:revive
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
//nolint:revive,gocritic
|
||||
os.Exit(code)
|
||||
}
|
||||
f := PrettyErrorFormatter{w: os.Stderr, verbose: r.verbose}
|
||||
if err != nil {
|
||||
f.Format(err)
|
||||
}
|
||||
//nolint:revive
|
||||
//nolint:revive,gocritic
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
@@ -891,7 +891,7 @@ func DumpHandler(ctx context.Context, name string) {
|
||||
|
||||
done:
|
||||
if sigStr == "SIGQUIT" {
|
||||
//nolint:revive
|
||||
//nolint:revive,gocritic
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1045,7 +1045,7 @@ func formatMultiError(from string, multi []error, opts *formatOpts) string {
|
||||
prefix := fmt.Sprintf("%d. ", i+1)
|
||||
if len(prefix) < len(indent) {
|
||||
// Indent the prefix to match the indent
|
||||
prefix = prefix + strings.Repeat(" ", len(indent)-len(prefix))
|
||||
prefix += strings.Repeat(" ", len(indent)-len(prefix))
|
||||
}
|
||||
errStr = prefix + errStr
|
||||
// Now looks like
|
||||
|
||||
+2
-2
@@ -1764,9 +1764,9 @@ func parseTLSCipherSuites(ciphers []string) ([]tls.CipherSuite, error) {
|
||||
// hasSupportedVersion is a helper function that returns true if the list
|
||||
// of supported versions contains a version between min and max.
|
||||
// If the versions list is outside the min/max, then it returns false.
|
||||
func hasSupportedVersion(min, max uint16, versions []uint16) bool {
|
||||
func hasSupportedVersion(minVal, maxVal uint16, versions []uint16) bool {
|
||||
for _, v := range versions {
|
||||
if v >= min && v <= max {
|
||||
if v >= minVal && v <= maxVal {
|
||||
// If one version is in between min/max, return true.
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1701,6 +1701,7 @@ func TestServer(t *testing.T) {
|
||||
// Next, we instruct the same server to display the YAML config
|
||||
// and then save it.
|
||||
inv = inv.WithContext(testutil.Context(t, testutil.WaitMedium))
|
||||
//nolint:gocritic
|
||||
inv.Args = append(args, "--write-config")
|
||||
fi, err := os.OpenFile(testutil.TempFile(t, "", "coder-config-test-*"), os.O_WRONLY|os.O_CREATE, 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
+1
-1
@@ -264,7 +264,7 @@ func (r *RootCmd) ssh() *serpent.Command {
|
||||
})
|
||||
if err != nil {
|
||||
if xerrors.Is(err, context.Canceled) {
|
||||
return cliui.Canceled
|
||||
return cliui.ErrCanceled
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -341,7 +341,7 @@ func TestSSH(t *testing.T) {
|
||||
|
||||
cmdDone := tGo(t, func() {
|
||||
err := inv.WithContext(ctx).Run()
|
||||
assert.ErrorIs(t, err, cliui.Canceled)
|
||||
assert.ErrorIs(t, err, cliui.ErrCanceled)
|
||||
})
|
||||
pty.ExpectMatch(wantURL)
|
||||
cancel()
|
||||
|
||||
+4
-3
@@ -147,12 +147,13 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
autostopRequirementWeeks = template.AutostopRequirement.Weeks
|
||||
}
|
||||
|
||||
if len(autostartRequirementDaysOfWeek) == 1 && autostartRequirementDaysOfWeek[0] == "all" {
|
||||
switch {
|
||||
case len(autostartRequirementDaysOfWeek) == 1 && autostartRequirementDaysOfWeek[0] == "all":
|
||||
// Set it to every day of the week
|
||||
autostartRequirementDaysOfWeek = []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}
|
||||
} else if !userSetOption(inv, "autostart-requirement-weekdays") {
|
||||
case !userSetOption(inv, "autostart-requirement-weekdays"):
|
||||
autostartRequirementDaysOfWeek = template.AutostartRequirement.DaysOfWeek
|
||||
} else if len(autostartRequirementDaysOfWeek) == 0 {
|
||||
case len(autostartRequirementDaysOfWeek) == 0:
|
||||
autostartRequirementDaysOfWeek = []string{}
|
||||
}
|
||||
|
||||
|
||||
@@ -723,6 +723,7 @@ func TestTemplatePush(t *testing.T) {
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
|
||||
|
||||
// Test the cli command.
|
||||
//nolint:gocritic
|
||||
modifiedTemplateVariables := append(initialTemplateVariables,
|
||||
&proto.TemplateVariable{
|
||||
Name: "second_variable",
|
||||
@@ -792,6 +793,7 @@ func TestTemplatePush(t *testing.T) {
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
|
||||
|
||||
// Test the cli command.
|
||||
//nolint:gocritic
|
||||
modifiedTemplateVariables := append(initialTemplateVariables,
|
||||
&proto.TemplateVariable{
|
||||
Name: "second_variable",
|
||||
@@ -839,6 +841,7 @@ func TestTemplatePush(t *testing.T) {
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
|
||||
|
||||
// Test the cli command.
|
||||
//nolint:gocritic
|
||||
modifiedTemplateVariables := append(initialTemplateVariables,
|
||||
&proto.TemplateVariable{
|
||||
Name: "second_variable",
|
||||
@@ -905,6 +908,7 @@ func TestTemplatePush(t *testing.T) {
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
|
||||
|
||||
// Test the cli command.
|
||||
//nolint:gocritic
|
||||
modifiedTemplateVariables := append(initialTemplateVariables,
|
||||
&proto.TemplateVariable{
|
||||
Name: "second_variable",
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ func parseCLISchedule(parts ...string) (*cron.Schedule, error) {
|
||||
func parseDuration(raw string) (time.Duration, error) {
|
||||
// If the user input a raw number, assume minutes
|
||||
if isDigit(raw) {
|
||||
raw = raw + "m"
|
||||
raw += "m"
|
||||
}
|
||||
d, err := time.ParseDuration(raw)
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
|
||||
})
|
||||
if err != nil {
|
||||
if xerrors.Is(err, context.Canceled) {
|
||||
return cliui.Canceled
|
||||
return cliui.ErrCanceled
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -89,7 +89,7 @@ func main() {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
@@ -100,7 +100,7 @@ func main() {
|
||||
Default: cliui.ConfirmYes,
|
||||
IsConfirm: true,
|
||||
})
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
if errors.Is(err, cliui.ErrCanceled) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
@@ -371,7 +371,7 @@ func main() {
|
||||
gitlabAuthed.Store(true)
|
||||
}()
|
||||
return cliui.ExternalAuth(inv.Context(), inv.Stdout, cliui.ExternalAuthOptions{
|
||||
Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionExternalAuth, error) {
|
||||
Fetch: func(_ context.Context) ([]codersdk.TemplateVersionExternalAuth, error) {
|
||||
count.Add(1)
|
||||
return []codersdk.TemplateVersionExternalAuth{{
|
||||
ID: "github",
|
||||
|
||||
+1
-11
@@ -1,26 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
_ "time/tzdata"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"github.com/coder/coder/v2/agent/agentexec"
|
||||
_ "github.com/coder/coder/v2/buildinfo/resources"
|
||||
"github.com/coder/coder/v2/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "agent-exec" {
|
||||
err := agentexec.CLI()
|
||||
_, _ = fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// This preserves backwards compatibility with an init function that is causing grief for
|
||||
// web terminals using agent-exec + screen. See https://github.com/coder/coder/pull/15817
|
||||
tea.InitTerminal()
|
||||
|
||||
var rootCmd cli.RootCmd
|
||||
rootCmd.RunWithSubcommands(rootCmd.AGPL())
|
||||
}
|
||||
|
||||
@@ -101,11 +101,12 @@ func (a *LogsAPI) BatchCreateLogs(ctx context.Context, req *agentproto.BatchCrea
|
||||
}
|
||||
|
||||
logs, err := a.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAt: a.now(),
|
||||
Output: output,
|
||||
Level: level,
|
||||
LogSourceID: logSourceID,
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAt: a.now(),
|
||||
Output: output,
|
||||
Level: level,
|
||||
LogSourceID: logSourceID,
|
||||
// #nosec G115 - Safe conversion as output length is expected to be within int32 range
|
||||
OutputLength: int32(outputLength),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Generated
+1
-1
@@ -11561,7 +11561,7 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.",
|
||||
"description": "Deprecated: Use HTTPAddress or TLS.Address instead.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/serpent.HostPort"
|
||||
|
||||
Generated
+1
-1
@@ -10325,7 +10325,7 @@
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.",
|
||||
"description": "Deprecated: Use HTTPAddress or TLS.Address instead.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/serpent.HostPort"
|
||||
|
||||
+3
-3
@@ -257,12 +257,12 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var userIds []uuid.UUID
|
||||
var userIDs []uuid.UUID
|
||||
for _, key := range keys {
|
||||
userIds = append(userIds, key.UserID)
|
||||
userIDs = append(userIDs, key.UserID)
|
||||
}
|
||||
|
||||
users, _ := api.Database.GetUsersByIDs(ctx, userIds)
|
||||
users, _ := api.Database.GetUsersByIDs(ctx, userIDs)
|
||||
usersByID := map[uuid.UUID]database.User{}
|
||||
for _, user := range users {
|
||||
usersByID[user.ID] = user
|
||||
|
||||
@@ -134,20 +134,22 @@ func TestGenerate(t *testing.T) {
|
||||
assert.WithinDuration(t, dbtime.Now(), key.CreatedAt, time.Second*5)
|
||||
assert.WithinDuration(t, dbtime.Now(), key.UpdatedAt, time.Second*5)
|
||||
|
||||
if tc.params.LifetimeSeconds > 0 {
|
||||
switch {
|
||||
case tc.params.LifetimeSeconds > 0:
|
||||
assert.Equal(t, tc.params.LifetimeSeconds, key.LifetimeSeconds)
|
||||
} else if !tc.params.ExpiresAt.IsZero() {
|
||||
case !tc.params.ExpiresAt.IsZero():
|
||||
// Should not be a delta greater than 5 seconds.
|
||||
assert.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, int64(tc.params.DefaultLifetime.Seconds()), key.LifetimeSeconds)
|
||||
}
|
||||
|
||||
if !tc.params.ExpiresAt.IsZero() {
|
||||
switch {
|
||||
case !tc.params.ExpiresAt.IsZero():
|
||||
assert.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt)
|
||||
} else if tc.params.LifetimeSeconds > 0 {
|
||||
case tc.params.LifetimeSeconds > 0:
|
||||
assert.WithinDuration(t, dbtime.Now().Add(time.Duration(tc.params.LifetimeSeconds)*time.Second), key.ExpiresAt, time.Second*5)
|
||||
} else {
|
||||
default:
|
||||
assert.WithinDuration(t, dbtime.Now().Add(tc.params.DefaultLifetime), key.ExpiresAt, time.Second*5)
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,9 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
// #nosec G115 - Safe conversion as pagination offset is expected to be within int32 range
|
||||
filter.OffsetOpt = int32(page.Offset)
|
||||
// #nosec G115 - Safe conversion as pagination limit is expected to be within int32 range
|
||||
filter.LimitOpt = int32(page.Limit)
|
||||
|
||||
if filter.Username == "me" {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type Auditor interface {
|
||||
Export(ctx context.Context, alog database.AuditLog) error
|
||||
diff(old, new any) Map
|
||||
diff(old, newVal any) Map
|
||||
}
|
||||
|
||||
type AdditionalFields struct {
|
||||
|
||||
@@ -60,10 +60,10 @@ func Diff[T Auditable](a Auditor, left, right T) Map { return a.diff(left, right
|
||||
// the Auditor feature interface. Only types in the same package as the
|
||||
// interface can implement unexported methods.
|
||||
type Differ struct {
|
||||
DiffFn func(old, new any) Map
|
||||
DiffFn func(old, newVal any) Map
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (d Differ) diff(old, new any) Map {
|
||||
return d.DiffFn(old, new)
|
||||
func (d Differ) diff(old, newVal any) Map {
|
||||
return d.DiffFn(old, newVal)
|
||||
}
|
||||
|
||||
+35
-30
@@ -407,11 +407,12 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
||||
|
||||
var userID uuid.UUID
|
||||
key, ok := httpmw.APIKeyOptional(p.Request)
|
||||
if ok {
|
||||
switch {
|
||||
case ok:
|
||||
userID = key.UserID
|
||||
} else if req.UserID != uuid.Nil {
|
||||
case req.UserID != uuid.Nil:
|
||||
userID = req.UserID
|
||||
} else {
|
||||
default:
|
||||
// if we do not have a user associated with the audit action
|
||||
// we do not want to audit
|
||||
// (this pertains to logins; we don't want to capture non-user login attempts)
|
||||
@@ -425,16 +426,17 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
||||
|
||||
ip := ParseIP(p.Request.RemoteAddr)
|
||||
auditLog := database.AuditLog{
|
||||
ID: uuid.New(),
|
||||
Time: dbtime.Now(),
|
||||
UserID: userID,
|
||||
Ip: ip,
|
||||
UserAgent: sql.NullString{String: p.Request.UserAgent(), Valid: true},
|
||||
ResourceType: either(req.Old, req.New, ResourceType[T], req.params.Action),
|
||||
ResourceID: either(req.Old, req.New, ResourceID[T], req.params.Action),
|
||||
ResourceTarget: either(req.Old, req.New, ResourceTarget[T], req.params.Action),
|
||||
Action: action,
|
||||
Diff: diffRaw,
|
||||
ID: uuid.New(),
|
||||
Time: dbtime.Now(),
|
||||
UserID: userID,
|
||||
Ip: ip,
|
||||
UserAgent: sql.NullString{String: p.Request.UserAgent(), Valid: true},
|
||||
ResourceType: either(req.Old, req.New, ResourceType[T], req.params.Action),
|
||||
ResourceID: either(req.Old, req.New, ResourceID[T], req.params.Action),
|
||||
ResourceTarget: either(req.Old, req.New, ResourceTarget[T], req.params.Action),
|
||||
Action: action,
|
||||
Diff: diffRaw,
|
||||
// #nosec G115 - Safe conversion as HTTP status code is expected to be within int32 range (typically 100-599)
|
||||
StatusCode: int32(sw.Status),
|
||||
RequestID: httpmw.RequestID(p.Request),
|
||||
AdditionalFields: additionalFieldsRaw,
|
||||
@@ -475,17 +477,18 @@ func BackgroundAudit[T Auditable](ctx context.Context, p *BackgroundAuditParams[
|
||||
}
|
||||
|
||||
auditLog := database.AuditLog{
|
||||
ID: uuid.New(),
|
||||
Time: p.Time,
|
||||
UserID: p.UserID,
|
||||
OrganizationID: requireOrgID[T](ctx, p.OrganizationID, p.Log),
|
||||
Ip: ip,
|
||||
UserAgent: sql.NullString{Valid: p.UserAgent != "", String: p.UserAgent},
|
||||
ResourceType: either(p.Old, p.New, ResourceType[T], p.Action),
|
||||
ResourceID: either(p.Old, p.New, ResourceID[T], p.Action),
|
||||
ResourceTarget: either(p.Old, p.New, ResourceTarget[T], p.Action),
|
||||
Action: p.Action,
|
||||
Diff: diffRaw,
|
||||
ID: uuid.New(),
|
||||
Time: p.Time,
|
||||
UserID: p.UserID,
|
||||
OrganizationID: requireOrgID[T](ctx, p.OrganizationID, p.Log),
|
||||
Ip: ip,
|
||||
UserAgent: sql.NullString{Valid: p.UserAgent != "", String: p.UserAgent},
|
||||
ResourceType: either(p.Old, p.New, ResourceType[T], p.Action),
|
||||
ResourceID: either(p.Old, p.New, ResourceID[T], p.Action),
|
||||
ResourceTarget: either(p.Old, p.New, ResourceTarget[T], p.Action),
|
||||
Action: p.Action,
|
||||
Diff: diffRaw,
|
||||
// #nosec G115 - Safe conversion as HTTP status code is expected to be within int32 range (typically 100-599)
|
||||
StatusCode: int32(p.Status),
|
||||
RequestID: p.RequestID,
|
||||
AdditionalFields: p.AdditionalFields,
|
||||
@@ -554,17 +557,19 @@ func BaggageFromContext(ctx context.Context) WorkspaceBuildBaggage {
|
||||
return d
|
||||
}
|
||||
|
||||
func either[T Auditable, R any](old, new T, fn func(T) R, auditAction database.AuditAction) R {
|
||||
if ResourceID(new) != uuid.Nil {
|
||||
return fn(new)
|
||||
} else if ResourceID(old) != uuid.Nil {
|
||||
func either[T Auditable, R any](old, newVal T, fn func(T) R, auditAction database.AuditAction) R {
|
||||
switch {
|
||||
case ResourceID(newVal) != uuid.Nil:
|
||||
return fn(newVal)
|
||||
case ResourceID(old) != uuid.Nil:
|
||||
return fn(old)
|
||||
} else if auditAction == database.AuditActionLogin || auditAction == database.AuditActionLogout {
|
||||
case auditAction == database.AuditActionLogin || auditAction == database.AuditActionLogout:
|
||||
// If the request action is a login or logout, we always want to audit it even if
|
||||
// there is no diff. See the comment in audit.InitRequest for more detail.
|
||||
return fn(old)
|
||||
default:
|
||||
panic("both old and new are nil")
|
||||
}
|
||||
panic("both old and new are nil")
|
||||
}
|
||||
|
||||
func ParseIP(ipStr string) pqtype.Inet {
|
||||
|
||||
@@ -52,6 +52,7 @@ func Test_isEligibleForAutostart(t *testing.T) {
|
||||
for i, weekday := range schedule.DaysOfWeek {
|
||||
// Find the local weekday
|
||||
if okTick.In(localLocation).Weekday() == weekday {
|
||||
// #nosec G115 - Safe conversion as i is the index of a 7-day week and will be in the range 0-6
|
||||
okWeekdayBit = 1 << uint(i)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -829,7 +829,7 @@ func New(options *Options) *API {
|
||||
// we do not override subdomain app routes.
|
||||
r.Get("/latency-check", tracing.StatusWriterMiddleware(prometheusMW(LatencyCheck())).ServeHTTP)
|
||||
|
||||
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("OK")) })
|
||||
r.Get("/healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("OK")) })
|
||||
|
||||
// Attach workspace apps routes.
|
||||
r.Group(func(r chi.Router) {
|
||||
@@ -844,7 +844,7 @@ func New(options *Options) *API {
|
||||
r.Route("/derp", func(r chi.Router) {
|
||||
r.Get("/", derpHandler.ServeHTTP)
|
||||
// This is used when UDP is blocked, and latency must be checked via HTTP(s).
|
||||
r.Get("/latency-check", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Get("/latency-check", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
})
|
||||
@@ -901,7 +901,7 @@ func New(options *Options) *API {
|
||||
r.Route("/api/v2", func(r chi.Router) {
|
||||
api.APIHandler = r
|
||||
|
||||
r.NotFound(func(rw http.ResponseWriter, r *http.Request) { httpapi.RouteNotFound(rw) })
|
||||
r.NotFound(func(rw http.ResponseWriter, _ *http.Request) { httpapi.RouteNotFound(rw) })
|
||||
r.Use(
|
||||
// Specific routes can specify different limits, but every rate
|
||||
// limit must be configurable by the admin.
|
||||
@@ -1421,7 +1421,7 @@ func New(options *Options) *API {
|
||||
// global variable here.
|
||||
r.Get("/swagger/*", globalHTTPSwaggerHandler)
|
||||
} else {
|
||||
swaggerDisabled := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
swaggerDisabled := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
|
||||
httpapi.Write(context.Background(), rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: "Swagger documentation is disabled.",
|
||||
})
|
||||
|
||||
@@ -1194,7 +1194,7 @@ func MustWorkspace(t testing.TB, client *codersdk.Client, workspaceID uuid.UUID)
|
||||
// RequestExternalAuthCallback makes a request with the proper OAuth2 state cookie
|
||||
// to the external auth callback endpoint.
|
||||
func RequestExternalAuthCallback(t testing.TB, providerID string, client *codersdk.Client, opts ...func(*http.Request)) *http.Response {
|
||||
client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
client.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
state := "somestate"
|
||||
|
||||
@@ -339,8 +339,8 @@ func NewFakeIDP(t testing.TB, opts ...FakeIDPOpt) *FakeIDP {
|
||||
refreshIDTokenClaims: syncmap.New[string, jwt.MapClaims](),
|
||||
deviceCode: syncmap.New[string, deviceFlow](),
|
||||
hookOnRefresh: func(_ string) error { return nil },
|
||||
hookUserInfo: func(email string) (jwt.MapClaims, error) { return jwt.MapClaims{}, nil },
|
||||
hookValidRedirectURL: func(redirectURL string) error { return nil },
|
||||
hookUserInfo: func(_ string) (jwt.MapClaims, error) { return jwt.MapClaims{}, nil },
|
||||
hookValidRedirectURL: func(_ string) error { return nil },
|
||||
defaultExpire: time.Minute * 5,
|
||||
}
|
||||
|
||||
@@ -553,7 +553,7 @@ func (f *FakeIDP) ExternalLogin(t testing.TB, client *codersdk.Client, opts ...f
|
||||
f.SetRedirect(t, coderOauthURL.String())
|
||||
|
||||
cli := f.HTTPClient(client.HTTPClient)
|
||||
cli.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
cli.CheckRedirect = func(req *http.Request, _ []*http.Request) error {
|
||||
// Store the idTokenClaims to the specific state request. This ties
|
||||
// the claims 1:1 with a given authentication flow.
|
||||
state := req.URL.Query().Get("state")
|
||||
@@ -1210,7 +1210,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
|
||||
}.Encode())
|
||||
}))
|
||||
|
||||
mux.NotFound(func(rw http.ResponseWriter, r *http.Request) {
|
||||
mux.NotFound(func(_ http.ResponseWriter, r *http.Request) {
|
||||
f.logger.Error(r.Context(), "http call not found", slogRequestFields(r)...)
|
||||
t.Errorf("unexpected request to IDP at path %q. Not supported", r.URL.Path)
|
||||
})
|
||||
|
||||
@@ -151,7 +151,7 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [
|
||||
assertUniqueRoutes(t, swaggerComments)
|
||||
assertSingleAnnotations(t, swaggerComments)
|
||||
|
||||
err := chi.Walk(router, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||
err := chi.Walk(router, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
|
||||
method = strings.ToLower(method)
|
||||
if route != "/" && strings.HasSuffix(route, "/") {
|
||||
route = route[:len(route)-1]
|
||||
|
||||
@@ -33,8 +33,8 @@ var _ database.Store = (*querier)(nil)
|
||||
|
||||
const wrapname = "dbauthz.querier"
|
||||
|
||||
// NoActorError is returned if no actor is present in the context.
|
||||
var NoActorError = xerrors.Errorf("no authorization actor in context")
|
||||
// ErrNoActor is returned if no actor is present in the context.
|
||||
var ErrNoActor = xerrors.Errorf("no authorization actor in context")
|
||||
|
||||
// NotAuthorizedError is a sentinel error that unwraps to sql.ErrNoRows.
|
||||
// This allows the internal error to be read by the caller if needed. Otherwise
|
||||
@@ -69,7 +69,7 @@ func IsNotAuthorizedError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if xerrors.Is(err, NoActorError) {
|
||||
if xerrors.Is(err, ErrNoActor) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func (q *querier) Wrappers() []string {
|
||||
func (q *querier) authorizeContext(ctx context.Context, action policy.Action, object rbac.Objecter) error {
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return NoActorError
|
||||
return ErrNoActor
|
||||
}
|
||||
|
||||
err := q.auth.Authorize(ctx, act, action, object.RBACObject())
|
||||
@@ -466,7 +466,7 @@ func insertWithAction[
|
||||
// Fetch the rbac subject
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return empty, NoActorError
|
||||
return empty, ErrNoActor
|
||||
}
|
||||
|
||||
// Authorize the action
|
||||
@@ -544,7 +544,7 @@ func fetchWithAction[
|
||||
// Fetch the rbac subject
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return empty, NoActorError
|
||||
return empty, ErrNoActor
|
||||
}
|
||||
|
||||
// Fetch the database object
|
||||
@@ -620,7 +620,7 @@ func fetchAndQuery[
|
||||
// Fetch the rbac subject
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return empty, NoActorError
|
||||
return empty, ErrNoActor
|
||||
}
|
||||
|
||||
// Fetch the database object
|
||||
@@ -654,7 +654,7 @@ func fetchWithPostFilter[
|
||||
// Fetch the rbac subject
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return empty, NoActorError
|
||||
return empty, ErrNoActor
|
||||
}
|
||||
|
||||
// Fetch the database object
|
||||
@@ -673,7 +673,7 @@ func fetchWithPostFilter[
|
||||
func prepareSQLFilter(ctx context.Context, authorizer rbac.Authorizer, action policy.Action, resourceType string) (rbac.PreparedAuthorized, error) {
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, NoActorError
|
||||
return nil, ErrNoActor
|
||||
}
|
||||
|
||||
return authorizer.Prepare(ctx, act, action, resourceType)
|
||||
@@ -752,7 +752,7 @@ func (*querier) convertToDeploymentRoles(names []string) []rbac.RoleIdentifier {
|
||||
func (q *querier) canAssignRoles(ctx context.Context, orgID uuid.UUID, added, removed []rbac.RoleIdentifier) error {
|
||||
actor, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return NoActorError
|
||||
return ErrNoActor
|
||||
}
|
||||
|
||||
roleAssign := rbac.ResourceAssignRole
|
||||
@@ -961,7 +961,7 @@ func (q *querier) customRoleEscalationCheck(ctx context.Context, actor rbac.Subj
|
||||
func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole) error {
|
||||
act, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return NoActorError
|
||||
return ErrNoActor
|
||||
}
|
||||
|
||||
// Org permissions require an org role
|
||||
@@ -1667,8 +1667,8 @@ func (q *querier) GetDeploymentWorkspaceStats(ctx context.Context) (database.Get
|
||||
return q.db.GetDeploymentWorkspaceStats(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetEligibleProvisionerDaemonsByProvisionerJobIDs(ctx context.Context, provisionerJobIds []uuid.UUID) ([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, error) {
|
||||
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetEligibleProvisionerDaemonsByProvisionerJobIDs)(ctx, provisionerJobIds)
|
||||
func (q *querier) GetEligibleProvisionerDaemonsByProvisionerJobIDs(ctx context.Context, provisionerJobIDs []uuid.UUID) ([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, error) {
|
||||
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetEligibleProvisionerDaemonsByProvisionerJobIDs)(ctx, provisionerJobIDs)
|
||||
}
|
||||
|
||||
func (q *querier) GetExternalAuthLink(ctx context.Context, arg database.GetExternalAuthLinkParams) (database.ExternalAuthLink, error) {
|
||||
@@ -3050,11 +3050,11 @@ func (q *querier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, created
|
||||
return q.db.GetWorkspaceResourcesCreatedAfter(ctx, createdAt)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Context, templateIds []uuid.UUID) ([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) {
|
||||
func (q *querier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Context, templateIDs []uuid.UUID) ([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx, templateIds)
|
||||
return q.db.GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx, templateIDs)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesParams) ([]database.GetWorkspacesRow, error) {
|
||||
@@ -3245,6 +3245,7 @@ func (q *querier) InsertOrganizationMember(ctx context.Context, arg database.Ins
|
||||
}
|
||||
|
||||
// All roles are added roles. Org member is always implied.
|
||||
//nolint:gocritic
|
||||
addedRoles := append(orgRoles, rbac.ScopedRoleOrgMember(arg.OrganizationID))
|
||||
err = q.canAssignRoles(ctx, arg.OrganizationID, addedRoles, []rbac.RoleIdentifier{})
|
||||
if err != nil {
|
||||
@@ -3397,7 +3398,7 @@ func (q *querier) InsertUserGroupsByName(ctx context.Context, arg database.Inser
|
||||
// This will add the user to all named groups. This counts as updating a group.
|
||||
// NOTE: instead of checking if the user has permission to update each group, we instead
|
||||
// check if the user has permission to update *a* group in the org.
|
||||
fetch := func(ctx context.Context, arg database.InsertUserGroupsByNameParams) (rbac.Objecter, error) {
|
||||
fetch := func(_ context.Context, arg database.InsertUserGroupsByNameParams) (rbac.Objecter, error) {
|
||||
return rbac.ResourceGroup.InOrg(arg.OrganizationID), nil
|
||||
}
|
||||
return update(q.log, q.auth, fetch, q.db.InsertUserGroupsByName)(ctx, arg)
|
||||
@@ -3830,6 +3831,7 @@ func (q *querier) UpdateMemberRoles(ctx context.Context, arg database.UpdateMemb
|
||||
}
|
||||
|
||||
// The org member role is always implied.
|
||||
//nolint:gocritic
|
||||
impliedTypes := append(scopedGranted, rbac.ScopedRoleOrgMember(arg.OrgID))
|
||||
|
||||
added, removed := rbac.ChangeRoleSet(originalRoles, impliedTypes)
|
||||
@@ -3930,7 +3932,7 @@ func (q *querier) UpdateProvisionerJobWithCancelByID(ctx context.Context, arg da
|
||||
// Only owners can cancel workspace builds
|
||||
actor, ok := ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return NoActorError
|
||||
return ErrNoActor
|
||||
}
|
||||
if !slice.Contains(actor.Roles.Names(), rbac.RoleOwner()) {
|
||||
return xerrors.Errorf("only owners can cancel workspace builds")
|
||||
|
||||
@@ -252,7 +252,7 @@ func (s *MethodTestSuite) NoActorErrorTest(callMethod func(ctx context.Context)
|
||||
s.Run("AsRemoveActor", func() {
|
||||
// Call without any actor
|
||||
_, err := callMethod(context.Background())
|
||||
s.ErrorIs(err, dbauthz.NoActorError, "method should return NoActorError error when no actor is provided")
|
||||
s.ErrorIs(err, dbauthz.ErrNoActor, "method should return NoActorError error when no actor is provided")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ type OrganizationResponse struct {
|
||||
|
||||
func (b OrganizationBuilder) EveryoneAllowance(allowance int) OrganizationBuilder {
|
||||
//nolint: revive // returns modified struct
|
||||
// #nosec G115 - Safe conversion as allowance is expected to be within int32 range
|
||||
b.allUsersAllowance = int32(allowance)
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -6057,6 +6057,7 @@ func (q *FakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg dat
|
||||
|
||||
if arg.LimitOpt > 0 {
|
||||
if int(arg.LimitOpt) > len(version) {
|
||||
// #nosec G115 - Safe conversion as version slice length is expected to be within int32 range
|
||||
arg.LimitOpt = int32(len(version))
|
||||
}
|
||||
version = version[:arg.LimitOpt]
|
||||
@@ -6691,6 +6692,7 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
|
||||
|
||||
if params.LimitOpt > 0 {
|
||||
if int(params.LimitOpt) > len(users) {
|
||||
// #nosec G115 - Safe conversion as users slice length is expected to be within int32 range
|
||||
params.LimitOpt = int32(len(users))
|
||||
}
|
||||
users = users[:params.LimitOpt]
|
||||
@@ -7618,6 +7620,7 @@ func (q *FakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context,
|
||||
|
||||
if params.LimitOpt > 0 {
|
||||
if int(params.LimitOpt) > len(history) {
|
||||
// #nosec G115 - Safe conversion as history slice length is expected to be within int32 range
|
||||
params.LimitOpt = int32(len(history))
|
||||
}
|
||||
history = history[:params.LimitOpt]
|
||||
@@ -9280,6 +9283,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.I
|
||||
LogSourceID: arg.LogSourceID,
|
||||
Output: output,
|
||||
})
|
||||
// #nosec G115 - Safe conversion as log output length is expected to be within int32 range
|
||||
outputLength += int32(len(output))
|
||||
}
|
||||
for index, agent := range q.workspaceAgents {
|
||||
@@ -12415,17 +12419,23 @@ TemplateUsageStatsInsertLoop:
|
||||
|
||||
// SELECT
|
||||
tus := database.TemplateUsageStat{
|
||||
StartTime: stat.TimeBucket,
|
||||
EndTime: stat.TimeBucket.Add(30 * time.Minute),
|
||||
TemplateID: stat.TemplateID,
|
||||
UserID: stat.UserID,
|
||||
UsageMins: int16(stat.UsageMins),
|
||||
MedianLatencyMs: sql.NullFloat64{Float64: latency.MedianLatencyMS, Valid: latencyOk},
|
||||
SshMins: int16(stat.SSHMins),
|
||||
SftpMins: int16(stat.SFTPMins),
|
||||
StartTime: stat.TimeBucket,
|
||||
EndTime: stat.TimeBucket.Add(30 * time.Minute),
|
||||
TemplateID: stat.TemplateID,
|
||||
UserID: stat.UserID,
|
||||
// #nosec G115 - Safe conversion for usage minutes which are expected to be within int16 range
|
||||
UsageMins: int16(stat.UsageMins),
|
||||
MedianLatencyMs: sql.NullFloat64{Float64: latency.MedianLatencyMS, Valid: latencyOk},
|
||||
// #nosec G115 - Safe conversion for SSH minutes which are expected to be within int16 range
|
||||
SshMins: int16(stat.SSHMins),
|
||||
// #nosec G115 - Safe conversion for SFTP minutes which are expected to be within int16 range
|
||||
SftpMins: int16(stat.SFTPMins),
|
||||
// #nosec G115 - Safe conversion for ReconnectingPTY minutes which are expected to be within int16 range
|
||||
ReconnectingPtyMins: int16(stat.ReconnectingPTYMins),
|
||||
VscodeMins: int16(stat.VSCodeMins),
|
||||
JetbrainsMins: int16(stat.JetBrainsMins),
|
||||
// #nosec G115 - Safe conversion for VSCode minutes which are expected to be within int16 range
|
||||
VscodeMins: int16(stat.VSCodeMins),
|
||||
// #nosec G115 - Safe conversion for JetBrains minutes which are expected to be within int16 range
|
||||
JetbrainsMins: int16(stat.JetBrainsMins),
|
||||
}
|
||||
if len(stat.AppUsageMinutes) > 0 {
|
||||
tus.AppUsageMins = make(map[string]int64, len(stat.AppUsageMinutes))
|
||||
|
||||
@@ -18,5 +18,6 @@ const (
|
||||
func GenLockID(name string) int64 {
|
||||
hash := fnv.New64()
|
||||
_, _ = hash.Write([]byte(name))
|
||||
// #nosec G115 - Safe conversion as FNV hash should be treated as random value and both uint64/int64 have the same range of unique values
|
||||
return int64(hash.Sum64())
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ func (s *tableStats) Add(table string, n int) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.s[table] = s.s[table] + n
|
||||
s.s[table] += n
|
||||
}
|
||||
|
||||
func (s *tableStats) Empty() []string {
|
||||
|
||||
@@ -160,6 +160,7 @@ func (t Template) DeepCopy() Template {
|
||||
func (t Template) AutostartAllowedDays() uint8 {
|
||||
// Just flip the binary 0s to 1s and vice versa.
|
||||
// There is an extra day with the 8th bit that needs to be zeroed.
|
||||
// #nosec G115 - Safe conversion for AutostartBlockDaysOfWeek which is 7 bits
|
||||
return ^uint8(t.AutostartBlockDaysOfWeek) & 0b01111111
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ func (l PGLocks) String() string {
|
||||
|
||||
// Difference returns the difference between two sets of locks.
|
||||
// This is helpful to determine what changed between the two sets.
|
||||
func (l PGLocks) Difference(to PGLocks) (new PGLocks, removed PGLocks) {
|
||||
func (l PGLocks) Difference(to PGLocks) (newVal PGLocks, removed PGLocks) {
|
||||
return slice.SymmetricDifferenceFunc(l, to, func(a, b PGLock) bool {
|
||||
return a.Equal(b)
|
||||
})
|
||||
|
||||
@@ -2119,10 +2119,11 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat
|
||||
dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
WorkspaceID: wrk.ID,
|
||||
TemplateVersionID: version.ID,
|
||||
BuildNumber: int32(i) + 2,
|
||||
Transition: trans,
|
||||
InitiatorID: tpl.CreatedBy,
|
||||
JobID: latestJob.ID,
|
||||
// #nosec G115 - Safe conversion as build number is expected to be within int32 range
|
||||
BuildNumber: int32(i) + 2,
|
||||
Transition: trans,
|
||||
InitiatorID: tpl.CreatedBy,
|
||||
JobID: latestJob.ID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3182,21 +3183,22 @@ func TestGetUserStatusCounts(t *testing.T) {
|
||||
row.Date.In(location).String(),
|
||||
i,
|
||||
)
|
||||
if row.Date.Before(createdAt) {
|
||||
switch {
|
||||
case row.Date.Before(createdAt):
|
||||
require.Equal(t, int64(0), row.Count)
|
||||
} else if row.Date.Before(firstTransitionTime) {
|
||||
case row.Date.Before(firstTransitionTime):
|
||||
if row.Status == tc.initialStatus {
|
||||
require.Equal(t, int64(1), row.Count)
|
||||
} else if row.Status == tc.targetStatus {
|
||||
require.Equal(t, int64(0), row.Count)
|
||||
}
|
||||
} else if !row.Date.After(today) {
|
||||
case !row.Date.After(today):
|
||||
if row.Status == tc.initialStatus {
|
||||
require.Equal(t, int64(0), row.Count)
|
||||
} else if row.Status == tc.targetStatus {
|
||||
require.Equal(t, int64(1), row.Count)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
t.Errorf("date %q beyond expected range end %q", row.Date, today)
|
||||
}
|
||||
}
|
||||
@@ -3337,18 +3339,19 @@ func TestGetUserStatusCounts(t *testing.T) {
|
||||
expectedCounts[d][tc.user2Transition.to] = 0
|
||||
|
||||
// Counted Values
|
||||
if d.Before(createdAt) {
|
||||
switch {
|
||||
case d.Before(createdAt):
|
||||
continue
|
||||
} else if d.Before(firstTransitionTime) {
|
||||
case d.Before(firstTransitionTime):
|
||||
expectedCounts[d][tc.user1Transition.from]++
|
||||
expectedCounts[d][tc.user2Transition.from]++
|
||||
} else if d.Before(secondTransitionTime) {
|
||||
case d.Before(secondTransitionTime):
|
||||
expectedCounts[d][tc.user1Transition.to]++
|
||||
expectedCounts[d][tc.user2Transition.from]++
|
||||
} else if d.Before(today) {
|
||||
case d.Before(today):
|
||||
expectedCounts[d][tc.user1Transition.to]++
|
||||
expectedCounts[d][tc.user2Transition.to]++
|
||||
} else {
|
||||
default:
|
||||
t.Fatalf("date %q beyond expected range end %q", d, today)
|
||||
}
|
||||
}
|
||||
@@ -3441,11 +3444,12 @@ func TestGetUserStatusCounts(t *testing.T) {
|
||||
i,
|
||||
)
|
||||
require.Equal(t, database.UserStatusActive, row.Status)
|
||||
if row.Date.Before(createdAt) {
|
||||
switch {
|
||||
case row.Date.Before(createdAt):
|
||||
require.Equal(t, int64(0), row.Count)
|
||||
} else if i == len(userStatusChanges)-1 {
|
||||
case i == len(userStatusChanges)-1:
|
||||
require.Equal(t, int64(0), row.Count)
|
||||
} else {
|
||||
default:
|
||||
require.Equal(t, int64(1), row.Count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +664,7 @@ func copyDefaultSettings(config *codersdk.ExternalAuthConfig, defaults codersdk.
|
||||
if config.Regex == "" {
|
||||
config.Regex = defaults.Regex
|
||||
}
|
||||
if config.Scopes == nil || len(config.Scopes) == 0 {
|
||||
if len(config.Scopes) == 0 {
|
||||
config.Scopes = defaults.Scopes
|
||||
}
|
||||
if config.DeviceCodeURL == "" {
|
||||
@@ -676,7 +676,7 @@ func copyDefaultSettings(config *codersdk.ExternalAuthConfig, defaults codersdk.
|
||||
if config.DisplayIcon == "" {
|
||||
config.DisplayIcon = defaults.DisplayIcon
|
||||
}
|
||||
if config.ExtraTokenKeys == nil || len(config.ExtraTokenKeys) == 0 {
|
||||
if len(config.ExtraTokenKeys) == 0 {
|
||||
config.ExtraTokenKeys = defaults.ExtraTokenKeys
|
||||
}
|
||||
|
||||
|
||||
@@ -197,14 +197,15 @@ func (r *RegionReport) Run(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(r.Region.Nodes) == 1 {
|
||||
switch {
|
||||
case len(r.Region.Nodes) == 1:
|
||||
r.Healthy = r.NodeReports[0].Severity != health.SeverityError
|
||||
r.Severity = r.NodeReports[0].Severity
|
||||
} else if unhealthyNodes == 1 {
|
||||
case unhealthyNodes == 1:
|
||||
// r.Healthy = true (by default)
|
||||
r.Severity = health.SeverityWarning
|
||||
r.Warnings = append(r.Warnings, health.Messagef(health.CodeDERPOneNodeUnhealthy, oneNodeUnhealthy))
|
||||
} else if unhealthyNodes > 1 {
|
||||
case unhealthyNodes > 1:
|
||||
r.Healthy = false
|
||||
|
||||
// Review node reports and select the highest severity.
|
||||
|
||||
@@ -195,10 +195,8 @@ func TestWorkspaceProxies(t *testing.T) {
|
||||
assert.Equal(t, tt.expectedSeverity, rpt.Severity)
|
||||
if tt.expectedError != "" && assert.NotNil(t, rpt.Error) {
|
||||
assert.Contains(t, *rpt.Error, tt.expectedError)
|
||||
} else {
|
||||
if !assert.Nil(t, rpt.Error) {
|
||||
t.Logf("error: %v", *rpt.Error)
|
||||
}
|
||||
} else if !assert.Nil(t, rpt.Error) {
|
||||
t.Logf("error: %v", *rpt.Error)
|
||||
}
|
||||
if tt.expectedWarningCode != "" && assert.NotEmpty(t, rpt.Warnings) {
|
||||
var found bool
|
||||
|
||||
@@ -226,11 +226,9 @@ func (p *QueryParamParser) Time(vals url.Values, def time.Time, queryParam, layo
|
||||
// Time uses the default time format of RFC3339Nano and always returns a UTC time.
|
||||
func (p *QueryParamParser) Time3339Nano(vals url.Values, def time.Time, queryParam string) time.Time {
|
||||
layout := time.RFC3339Nano
|
||||
return p.timeWithMutate(vals, def, queryParam, layout, func(term string) string {
|
||||
// All search queries are forced to lowercase. But the RFC format requires
|
||||
// upper case letters. So just uppercase the term.
|
||||
return strings.ToUpper(term)
|
||||
})
|
||||
// All search queries are forced to lowercase. But the RFC format requires
|
||||
// upper case letters. So just uppercase the term.
|
||||
return p.timeWithMutate(vals, def, queryParam, layout, strings.ToUpper)
|
||||
}
|
||||
|
||||
func (p *QueryParamParser) timeWithMutate(vals url.Values, def time.Time, queryParam, layout string, mutate func(term string) string) time.Time {
|
||||
|
||||
@@ -203,7 +203,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||
// Write wraps writing a response to redirect if the handler
|
||||
// specified it should. This redirect is used for user-facing pages
|
||||
// like workspace applications.
|
||||
write := func(code int, response codersdk.Response) (*database.APIKey, *rbac.Subject, bool) {
|
||||
write := func(code int, response codersdk.Response) (apiKey *database.APIKey, subject *rbac.Subject, ok bool) {
|
||||
if cfg.RedirectToLogin {
|
||||
RedirectToLogin(rw, r, nil, response.Message)
|
||||
return nil, nil, false
|
||||
|
||||
@@ -46,7 +46,7 @@ func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler
|
||||
|
||||
func WorkspaceAppCors(regex *regexp.Regexp, app appurl.ApplicationURL) func(next http.Handler) http.Handler {
|
||||
return cors.Handler(cors.Options{
|
||||
AllowOriginFunc: func(r *http.Request, rawOrigin string) bool {
|
||||
AllowOriginFunc: func(_ *http.Request, rawOrigin string) bool {
|
||||
origin, err := url.Parse(rawOrigin)
|
||||
if rawOrigin == "" || origin.Host == "" || err != nil {
|
||||
return false
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
func TestRecover(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := func(isPanic, hijack bool) http.Handler {
|
||||
handler := func(isPanic, _ bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if isPanic {
|
||||
panic("Oh no!")
|
||||
|
||||
+2
-1
@@ -325,7 +325,8 @@ func (api *API) insightsUserStatusCounts(rw http.ResponseWriter, r *http.Request
|
||||
rows, err := api.Database.GetUserStatusCounts(ctx, database.GetUserStatusCountsParams{
|
||||
StartTime: sixtyDaysAgo,
|
||||
EndTime: nextHourInLoc,
|
||||
Interval: int32(interval),
|
||||
// #nosec G115 - Interval value is small and fits in int32 (typically days or hours)
|
||||
Interval: int32(interval),
|
||||
})
|
||||
if err != nil {
|
||||
if httpapi.IsUnauthorizedError(err) {
|
||||
|
||||
+4
-2
@@ -202,8 +202,10 @@ func (api *API) paginatedMembers(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
paginatedMemberRows, err := api.Database.PaginatedOrganizationMembers(ctx, database.PaginatedOrganizationMembersParams{
|
||||
OrganizationID: organization.ID,
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
// #nosec G115 - Pagination limits are small and fit in int32
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
// #nosec G115 - Pagination offsets are small and fit in int32
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
})
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
|
||||
@@ -34,10 +34,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ValidationNoFromAddressErr = xerrors.New("'from' address not defined")
|
||||
ValidationNoToAddressErr = xerrors.New("'to' address(es) not defined")
|
||||
ValidationNoSmarthostErr = xerrors.New("'smarthost' address not defined")
|
||||
ValidationNoHelloErr = xerrors.New("'hello' not defined")
|
||||
ErrValidationNoFromAddress = xerrors.New("'from' address not defined")
|
||||
ErrValidationNoToAddress = xerrors.New("'to' address(es) not defined")
|
||||
ErrValidationNoSmarthost = xerrors.New("'smarthost' address not defined")
|
||||
ErrValidationNoHello = xerrors.New("'hello' not defined")
|
||||
|
||||
//go:embed smtp/html.gotmpl
|
||||
htmlTemplate string
|
||||
@@ -493,7 +493,7 @@ func (*SMTPHandler) validateFromAddr(from string) (string, error) {
|
||||
return "", xerrors.Errorf("parse 'from' address: %w", err)
|
||||
}
|
||||
if len(addrs) != 1 {
|
||||
return "", ValidationNoFromAddressErr
|
||||
return "", ErrValidationNoFromAddress
|
||||
}
|
||||
return from, nil
|
||||
}
|
||||
@@ -505,7 +505,7 @@ func (s *SMTPHandler) validateToAddrs(to string) ([]string, error) {
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
s.log.Warn(context.Background(), "no valid 'to' address(es) defined; some may be invalid", slog.F("defined", to))
|
||||
return nil, ValidationNoToAddressErr
|
||||
return nil, ErrValidationNoToAddress
|
||||
}
|
||||
|
||||
var out []string
|
||||
@@ -522,7 +522,7 @@ func (s *SMTPHandler) validateToAddrs(to string) ([]string, error) {
|
||||
func (s *SMTPHandler) smarthost() (string, string, error) {
|
||||
smarthost := strings.TrimSpace(string(s.cfg.Smarthost))
|
||||
if smarthost == "" {
|
||||
return "", "", ValidationNoSmarthostErr
|
||||
return "", "", ErrValidationNoSmarthost
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(string(s.cfg.Smarthost))
|
||||
@@ -538,7 +538,7 @@ func (s *SMTPHandler) smarthost() (string, string, error) {
|
||||
func (s *SMTPHandler) hello() (string, error) {
|
||||
val := s.cfg.Hello.String()
|
||||
if val == "" {
|
||||
return "", ValidationNoHelloErr
|
||||
return "", ErrValidationNoHello
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
@@ -337,6 +337,7 @@ func (m *Manager) syncUpdates(ctx context.Context) {
|
||||
uctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
// #nosec G115 - Safe conversion for max send attempts which is expected to be within int32 range
|
||||
failureParams.MaxAttempts = int32(m.cfg.MaxSendAttempts)
|
||||
failureParams.RetryInterval = int32(m.cfg.RetryInterval.Value().Seconds())
|
||||
n, err := m.store.BulkMarkNotificationMessagesFailed(uctx, failureParams)
|
||||
|
||||
@@ -192,6 +192,7 @@ type syncInterceptor struct {
|
||||
|
||||
func (b *syncInterceptor) BulkMarkNotificationMessagesSent(ctx context.Context, arg database.BulkMarkNotificationMessagesSentParams) (int64, error) {
|
||||
updated, err := b.Store.BulkMarkNotificationMessagesSent(ctx, arg)
|
||||
// #nosec G115 - Safe conversion as the count of updated notification messages is expected to be within int32 range
|
||||
b.sent.Add(int32(updated))
|
||||
if err != nil {
|
||||
b.err.Store(err)
|
||||
@@ -201,6 +202,7 @@ func (b *syncInterceptor) BulkMarkNotificationMessagesSent(ctx context.Context,
|
||||
|
||||
func (b *syncInterceptor) BulkMarkNotificationMessagesFailed(ctx context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) {
|
||||
updated, err := b.Store.BulkMarkNotificationMessagesFailed(ctx, arg)
|
||||
// #nosec G115 - Safe conversion as the count of updated notification messages is expected to be within int32 range
|
||||
b.failed.Add(int32(updated))
|
||||
if err != nil {
|
||||
b.err.Store(err)
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestMetrics(t *testing.T) {
|
||||
// See TestPendingUpdatesMetric for a more precise test.
|
||||
return true
|
||||
},
|
||||
"coderd_notifications_synced_updates_total": func(metric *dto.Metric, series string) bool {
|
||||
"coderd_notifications_synced_updates_total": func(metric *dto.Metric, _ string) bool {
|
||||
if debug {
|
||||
t.Logf("coderd_notifications_synced_updates_total = %v: %v", maxAttempts+1, metric.Counter.GetValue())
|
||||
}
|
||||
|
||||
@@ -209,7 +209,9 @@ func (n *notifier) process(ctx context.Context, success chan<- dispatchResult, f
|
||||
// messages until they are dispatched - or until the lease expires (in exceptional cases).
|
||||
func (n *notifier) fetch(ctx context.Context) ([]database.AcquireNotificationMessagesRow, error) {
|
||||
msgs, err := n.store.AcquireNotificationMessages(ctx, database.AcquireNotificationMessagesParams{
|
||||
Count: int32(n.cfg.LeaseCount),
|
||||
// #nosec G115 - Safe conversion for lease count which is expected to be within int32 range
|
||||
Count: int32(n.cfg.LeaseCount),
|
||||
// #nosec G115 - Safe conversion for max send attempts which is expected to be within int32 range
|
||||
MaxAttemptCount: int32(n.cfg.MaxSendAttempts),
|
||||
NotifierID: n.id,
|
||||
LeaseSeconds: int32(n.cfg.LeasePeriod.Value().Seconds()),
|
||||
@@ -336,6 +338,7 @@ func (n *notifier) newFailedDispatch(msg database.AcquireNotificationMessagesRow
|
||||
var result string
|
||||
|
||||
// If retryable and not the last attempt, it's a temporary failure.
|
||||
// #nosec G115 - Safe conversion as MaxSendAttempts is expected to be small enough to fit in int32
|
||||
if retryable && msg.AttemptCount < int32(n.cfg.MaxSendAttempts)-1 {
|
||||
result = ResultTempFail
|
||||
} else {
|
||||
|
||||
@@ -196,11 +196,12 @@ func verifyCollectedMetrics(t *testing.T, expected []*agentproto.Stats_Metric, a
|
||||
err := actual[i].Write(&d)
|
||||
require.NoError(t, err)
|
||||
|
||||
if e.Type == agentproto.Stats_Metric_COUNTER {
|
||||
switch e.Type {
|
||||
case agentproto.Stats_Metric_COUNTER:
|
||||
require.Equal(t, e.Value, d.Counter.GetValue())
|
||||
} else if e.Type == agentproto.Stats_Metric_GAUGE {
|
||||
case agentproto.Stats_Metric_GAUGE:
|
||||
require.Equal(t, e.Value, d.Gauge.GetValue())
|
||||
} else {
|
||||
default:
|
||||
require.Failf(t, "unsupported type: %s", string(e.Type))
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ func convertParameterInsights(rows []database.GetTemplateParameterInsightsRow) [
|
||||
if _, ok := m[key]; !ok {
|
||||
m[key] = 0
|
||||
}
|
||||
m[key] = m[key] + r.Count
|
||||
m[key] += r.Count
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,11 +216,9 @@ func TestWorkspaceLatestBuildTotals(t *testing.T) {
|
||||
Total int
|
||||
Status map[codersdk.ProvisionerJobStatus]int
|
||||
}{{
|
||||
Name: "None",
|
||||
Database: func() database.Store {
|
||||
return dbmem.New()
|
||||
},
|
||||
Total: 0,
|
||||
Name: "None",
|
||||
Database: dbmem.New,
|
||||
Total: 0,
|
||||
}, {
|
||||
Name: "Multiple",
|
||||
Database: func() database.Store {
|
||||
@@ -289,10 +287,8 @@ func TestWorkspaceLatestBuildStatuses(t *testing.T) {
|
||||
ExpectedWorkspaces int
|
||||
ExpectedStatuses map[codersdk.ProvisionerJobStatus]int
|
||||
}{{
|
||||
Name: "None",
|
||||
Database: func() database.Store {
|
||||
return dbmem.New()
|
||||
},
|
||||
Name: "None",
|
||||
Database: dbmem.New,
|
||||
ExpectedWorkspaces: 0,
|
||||
}, {
|
||||
Name: "Multiple",
|
||||
|
||||
@@ -121,7 +121,7 @@ type server struct {
|
||||
// We use the null byte (0x00) in generating a canonical map key for tags, so
|
||||
// it cannot be used in the tag keys or values.
|
||||
|
||||
var ErrorTagsContainNullByte = xerrors.New("tags cannot contain the null byte (0x00)")
|
||||
var ErrTagsContainNullByte = xerrors.New("tags cannot contain the null byte (0x00)")
|
||||
|
||||
type Tags map[string]string
|
||||
|
||||
@@ -136,7 +136,7 @@ func (t Tags) ToJSON() (json.RawMessage, error) {
|
||||
func (t Tags) Valid() error {
|
||||
for k, v := range t {
|
||||
if slices.Contains([]byte(k), 0x00) || slices.Contains([]byte(v), 0x00) {
|
||||
return ErrorTagsContainNullByte
|
||||
return ErrTagsContainNullByte
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -1996,7 +1996,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()),
|
||||
InstanceMetadata: pqtype.NullRawMessage{},
|
||||
ResourceMetadata: pqtype.NullRawMessage{},
|
||||
DisplayOrder: int32(prAgent.Order),
|
||||
// #nosec G115 - Order represents a display order value that's always small and fits in int32
|
||||
DisplayOrder: int32(prAgent.Order),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert agent: %w", err)
|
||||
@@ -2011,7 +2012,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
Key: md.Key,
|
||||
Timeout: md.Timeout,
|
||||
Interval: md.Interval,
|
||||
DisplayOrder: int32(md.Order),
|
||||
// #nosec G115 - Order represents a display order value that's always small and fits in int32
|
||||
DisplayOrder: int32(md.Order),
|
||||
}
|
||||
err := db.InsertWorkspaceAgentMetadata(ctx, p)
|
||||
if err != nil {
|
||||
@@ -2197,9 +2199,10 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
HealthcheckInterval: app.Healthcheck.Interval,
|
||||
HealthcheckThreshold: app.Healthcheck.Threshold,
|
||||
Health: health,
|
||||
DisplayOrder: int32(app.Order),
|
||||
Hidden: app.Hidden,
|
||||
OpenIn: openIn,
|
||||
// #nosec G115 - Order represents a display order value that's always small and fits in int32
|
||||
DisplayOrder: int32(app.Order),
|
||||
Hidden: app.Hidden,
|
||||
OpenIn: openIn,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert app: %w", err)
|
||||
|
||||
@@ -78,6 +78,7 @@ func convertQuery(cfg ConvertConfig, q ast.Body) (sqltypes.BooleanNode, error) {
|
||||
|
||||
func convertExpression(cfg ConvertConfig, e *ast.Expr) (sqltypes.BooleanNode, error) {
|
||||
if e.IsCall() {
|
||||
//nolint:forcetypeassert
|
||||
n, err := convertCall(cfg, e.Terms.([]*ast.Term))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("call: %w", err)
|
||||
|
||||
@@ -77,6 +77,7 @@ func (r TemplateAutostopRequirement) DaysMap() map[time.Weekday]bool {
|
||||
func daysMap(daysOfWeek uint8) map[time.Weekday]bool {
|
||||
days := make(map[time.Weekday]bool)
|
||||
for i, day := range DaysOfWeek {
|
||||
// #nosec G115 - Safe conversion, i ranges from 0-6 for days of the week
|
||||
days[day] = daysOfWeek&(1<<uint(i)) != 0
|
||||
}
|
||||
return days
|
||||
@@ -88,6 +89,7 @@ func VerifyTemplateAutostopRequirement(days uint8, weeks int64) error {
|
||||
if days&0b10000000 != 0 {
|
||||
return xerrors.New("invalid autostop requirement days, last bit is set")
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if days > 0b11111111 {
|
||||
return xerrors.New("invalid autostop requirement days, too large")
|
||||
}
|
||||
@@ -106,6 +108,7 @@ func VerifyTemplateAutostartRequirement(days uint8) error {
|
||||
if days&0b10000000 != 0 {
|
||||
return xerrors.New("invalid autostart requirement days, last bit is set")
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if days > 0b11111111 {
|
||||
return xerrors.New("invalid autostart requirement days, too large")
|
||||
}
|
||||
|
||||
@@ -97,8 +97,10 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder
|
||||
filter := database.GetWorkspacesParams{
|
||||
AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()),
|
||||
|
||||
// #nosec G115 - Safe conversion for pagination offset which is expected to be within int32 range
|
||||
Offset: int32(page.Offset),
|
||||
Limit: int32(page.Limit),
|
||||
// #nosec G115 - Safe conversion for pagination limit which is expected to be within int32 range
|
||||
Limit: int32(page.Limit),
|
||||
}
|
||||
|
||||
if query == "" {
|
||||
|
||||
@@ -729,7 +729,8 @@ func ConvertWorkspaceBuild(build database.WorkspaceBuild) WorkspaceBuild {
|
||||
WorkspaceID: build.WorkspaceID,
|
||||
JobID: build.JobID,
|
||||
TemplateVersionID: build.TemplateVersionID,
|
||||
BuildNumber: uint32(build.BuildNumber),
|
||||
// #nosec G115 - Safe conversion as build numbers are expected to be positive and within uint32 range
|
||||
BuildNumber: uint32(build.BuildNumber),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1035,11 +1036,12 @@ func ConvertTemplate(dbTemplate database.Template) Template {
|
||||
FailureTTLMillis: time.Duration(dbTemplate.FailureTTL).Milliseconds(),
|
||||
TimeTilDormantMillis: time.Duration(dbTemplate.TimeTilDormant).Milliseconds(),
|
||||
TimeTilDormantAutoDeleteMillis: time.Duration(dbTemplate.TimeTilDormantAutoDelete).Milliseconds(),
|
||||
AutostopRequirementDaysOfWeek: codersdk.BitmapToWeekdays(uint8(dbTemplate.AutostopRequirementDaysOfWeek)),
|
||||
AutostopRequirementWeeks: dbTemplate.AutostopRequirementWeeks,
|
||||
AutostartAllowedDays: codersdk.BitmapToWeekdays(dbTemplate.AutostartAllowedDays()),
|
||||
RequireActiveVersion: dbTemplate.RequireActiveVersion,
|
||||
Deprecated: dbTemplate.Deprecated != "",
|
||||
// #nosec G115 - Safe conversion as AutostopRequirementDaysOfWeek is a bitmap of 7 days, easily within uint8 range
|
||||
AutostopRequirementDaysOfWeek: codersdk.BitmapToWeekdays(uint8(dbTemplate.AutostopRequirementDaysOfWeek)),
|
||||
AutostopRequirementWeeks: dbTemplate.AutostopRequirementWeeks,
|
||||
AutostartAllowedDays: codersdk.BitmapToWeekdays(dbTemplate.AutostartAllowedDays()),
|
||||
RequireActiveVersion: dbTemplate.RequireActiveVersion,
|
||||
Deprecated: dbTemplate.Deprecated != "",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1045,7 +1045,7 @@ func (api *API) convertTemplate(
|
||||
TimeTilDormantMillis: time.Duration(template.TimeTilDormant).Milliseconds(),
|
||||
TimeTilDormantAutoDeleteMillis: time.Duration(template.TimeTilDormantAutoDelete).Milliseconds(),
|
||||
AutostopRequirement: codersdk.TemplateAutostopRequirement{
|
||||
DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)),
|
||||
DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)), // #nosec G115 - Safe conversion as AutostopRequirementDaysOfWeek is a 7-bit bitmap
|
||||
Weeks: autostopRequirementWeeks,
|
||||
},
|
||||
AutostartRequirement: codersdk.TemplateAutostartRequirement{
|
||||
|
||||
@@ -843,9 +843,11 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
|
||||
versions, err := store.GetTemplateVersionsByTemplateID(ctx, database.GetTemplateVersionsByTemplateIDParams{
|
||||
TemplateID: template.ID,
|
||||
AfterID: paginationParams.AfterID,
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
Archived: archiveFilter,
|
||||
// #nosec G115 - Pagination limits are small and fit in int32
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
// #nosec G115 - Pagination offsets are small and fit in int32
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
Archived: archiveFilter,
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, apiVersions)
|
||||
@@ -1280,10 +1282,8 @@ func (api *API) setArchiveTemplateVersion(archive bool) func(rw http.ResponseWri
|
||||
|
||||
if archiveError != nil {
|
||||
err = archiveError
|
||||
} else {
|
||||
if len(archived) == 0 {
|
||||
err = xerrors.New("Unable to archive specified version, the version is likely in use by a workspace or currently set to the active version")
|
||||
}
|
||||
} else if len(archived) == 0 {
|
||||
err = xerrors.New("Unable to archive specified version, the version is likely in use by a workspace or currently set to the active version")
|
||||
}
|
||||
} else {
|
||||
err = api.Database.UnarchiveTemplateVersion(ctx, database.UnarchiveTemplateVersionParams{
|
||||
|
||||
@@ -98,7 +98,7 @@ func TracerProvider(ctx context.Context, service string, opts TracerOpts) (*sdkt
|
||||
tracerProvider := sdktrace.NewTracerProvider(tracerOpts...)
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
// Ignore otel errors!
|
||||
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {}))
|
||||
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(_ error) {}))
|
||||
otel.SetTextMapPropagator(
|
||||
propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
|
||||
@@ -78,6 +78,7 @@ func slogFieldsToAttributes(m slog.Map) []attribute.KeyValue {
|
||||
case []int64:
|
||||
value = attribute.Int64SliceValue(v)
|
||||
case uint:
|
||||
// #nosec G115 - Safe conversion from uint to int64 as we're only using this for non-critical logging/tracing
|
||||
value = attribute.Int64Value(int64(v))
|
||||
// no uint slice method
|
||||
case uint8:
|
||||
@@ -90,6 +91,8 @@ func slogFieldsToAttributes(m slog.Map) []attribute.KeyValue {
|
||||
value = attribute.Int64Value(int64(v))
|
||||
// no uint32 slice method
|
||||
case uint64:
|
||||
// #nosec G115 - Safe conversion from uint64 to int64 as we're only using this for non-critical logging/tracing
|
||||
// This is intentionally lossy for very large values, but acceptable for tracing purposes
|
||||
value = attribute.Int64Value(int64(v))
|
||||
// no uint64 slice method
|
||||
case string:
|
||||
|
||||
@@ -176,6 +176,7 @@ func mapToBasicMap(m map[string]interface{}) map[string]interface{} {
|
||||
case int32:
|
||||
val = int64(v)
|
||||
case uint:
|
||||
// #nosec G115 - Safe conversion for test data
|
||||
val = int64(v)
|
||||
case uint8:
|
||||
val = int64(v)
|
||||
@@ -184,6 +185,7 @@ func mapToBasicMap(m map[string]interface{}) map[string]interface{} {
|
||||
case uint32:
|
||||
val = int64(v)
|
||||
case uint64:
|
||||
// #nosec G115 - Safe conversion for test data with small test values
|
||||
val = int64(v)
|
||||
case time.Duration:
|
||||
val = v.String()
|
||||
|
||||
@@ -73,7 +73,7 @@ func New(db database.Store, log slog.Logger, opts Options) *Checker {
|
||||
opts.UpdateTimeout = 30 * time.Second
|
||||
}
|
||||
if opts.Notify == nil {
|
||||
opts.Notify = func(r Result) {}
|
||||
opts.Notify = func(_ Result) {}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
+4
-3
@@ -1509,7 +1509,8 @@ func (api *API) accessTokenClaims(ctx context.Context, rw http.ResponseWriter, s
|
||||
func (api *API) userInfoClaims(ctx context.Context, rw http.ResponseWriter, state httpmw.OAuth2State, logger slog.Logger) (userInfoClaims map[string]interface{}, ok bool) {
|
||||
userInfoClaims = make(map[string]interface{})
|
||||
userInfo, err := api.OIDCConfig.Provider.UserInfo(ctx, oauth2.StaticTokenSource(state.Token))
|
||||
if err == nil {
|
||||
switch {
|
||||
case err == nil:
|
||||
err = userInfo.Claims(&userInfoClaims)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "oauth2: unable to unmarshal user info claims", slog.Error(err))
|
||||
@@ -1524,14 +1525,14 @@ func (api *API) userInfoClaims(ctx context.Context, rw http.ResponseWriter, stat
|
||||
slog.F("claim_fields", claimFields(userInfoClaims)),
|
||||
slog.F("blank", blankFields(userInfoClaims)),
|
||||
)
|
||||
} else if !strings.Contains(err.Error(), "user info endpoint is not supported by this provider") {
|
||||
case !strings.Contains(err.Error(), "user info endpoint is not supported by this provider"):
|
||||
logger.Error(ctx, "oauth2: unable to obtain user information claims", slog.Error(err))
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to obtain user information claims.",
|
||||
Detail: "The attempt to fetch claims via the UserInfo endpoint failed: " + err.Error(),
|
||||
})
|
||||
return nil, false
|
||||
} else {
|
||||
default:
|
||||
// The OIDC provider does not support the UserInfo endpoint.
|
||||
// This is not an error, but we should log it as it may mean
|
||||
// that some claims are missing.
|
||||
|
||||
@@ -1453,7 +1453,7 @@ func TestUserOIDC(t *testing.T) {
|
||||
oidctest.WithStaticUserInfo(tc.UserInfoClaims),
|
||||
}
|
||||
|
||||
if tc.AccessTokenClaims != nil && len(tc.AccessTokenClaims) > 0 {
|
||||
if len(tc.AccessTokenClaims) > 0 {
|
||||
opts = append(opts, oidctest.WithAccessTokenJWTHook(func(email string, exp time.Time) jwt.MapClaims {
|
||||
return tc.AccessTokenClaims
|
||||
}))
|
||||
|
||||
+4
-2
@@ -306,8 +306,10 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us
|
||||
CreatedAfter: params.CreatedAfter,
|
||||
CreatedBefore: params.CreatedBefore,
|
||||
GithubComUserID: params.GithubComUserID,
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
// #nosec G115 - Pagination offsets are small and fit in int32
|
||||
OffsetOpt: int32(paginationParams.Offset),
|
||||
// #nosec G115 - Pagination limits are small and fit in int32
|
||||
LimitOpt: int32(paginationParams.Limit),
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user