mirror of https://github.com/coder/coder.git
chore: enable exhaustruct linter for database param structs (#9995)
This commit is contained in:
parent
352ec7bc4f
commit
e55c25e037
|
@ -10,6 +10,9 @@ linters-settings:
|
||||||
include:
|
include:
|
||||||
# Gradually extend to cover more of the codebase.
|
# Gradually extend to cover more of the codebase.
|
||||||
- 'httpmw\.\w+'
|
- 'httpmw\.\w+'
|
||||||
|
# We want to enforce all values are specified when inserting or updating
|
||||||
|
# a database row. Ref: #9936
|
||||||
|
- 'github.com/coder/coder/v2/coderd/database\.[^G][^e][^t]\w+Params'
|
||||||
gocognit:
|
gocognit:
|
||||||
min-complexity: 300
|
min-complexity: 300
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
|
||||||
return database.InsertAPIKeyParams{
|
return database.InsertAPIKeyParams{
|
||||||
ID: keyID,
|
ID: keyID,
|
||||||
UserID: params.UserID,
|
UserID: params.UserID,
|
||||||
|
LastUsed: time.Time{},
|
||||||
LifetimeSeconds: params.LifetimeSeconds,
|
LifetimeSeconds: params.LifetimeSeconds,
|
||||||
IPAddress: pqtype.Inet{
|
IPAddress: pqtype.Inet{
|
||||||
IPNet: net.IPNet{
|
IPNet: net.IPNet{
|
||||||
|
|
|
@ -154,6 +154,9 @@ func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) {
|
||||||
Diff: diff,
|
Diff: diff,
|
||||||
StatusCode: http.StatusOK,
|
StatusCode: http.StatusOK,
|
||||||
AdditionalFields: params.AdditionalFields,
|
AdditionalFields: params.AdditionalFields,
|
||||||
|
RequestID: uuid.Nil, // no request ID to attach this to
|
||||||
|
ResourceIcon: "",
|
||||||
|
OrganizationID: uuid.New(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.InternalServerError(rw, err)
|
httpapi.InternalServerError(rw, err)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package oidctest
|
package oidctest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -69,11 +70,13 @@ func (*LoginHelper) ExpireOauthToken(t *testing.T, db database.Store, user *code
|
||||||
|
|
||||||
// Expire the oauth link for the given user.
|
// Expire the oauth link for the given user.
|
||||||
updated, err := db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
updated, err := db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
||||||
OAuthAccessToken: link.OAuthAccessToken,
|
OAuthAccessToken: link.OAuthAccessToken,
|
||||||
OAuthRefreshToken: link.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: time.Now().Add(time.Hour * -1),
|
OAuthRefreshToken: link.OAuthRefreshToken,
|
||||||
UserID: link.UserID,
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
LoginType: link.LoginType,
|
OAuthExpiry: time.Now().Add(time.Hour * -1),
|
||||||
|
UserID: link.UserID,
|
||||||
|
LoginType: link.LoginType,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "expire user link")
|
require.NoError(t, err, "expire user link")
|
||||||
|
|
||||||
|
|
|
@ -4142,6 +4142,8 @@ func (q *FakeQuerier) InsertAllUsersGroup(ctx context.Context, orgID uuid.UUID)
|
||||||
Name: database.EveryoneGroup,
|
Name: database.EveryoneGroup,
|
||||||
DisplayName: "",
|
DisplayName: "",
|
||||||
OrganizationID: orgID,
|
OrganizationID: orgID,
|
||||||
|
AvatarURL: "",
|
||||||
|
QuotaAllowance: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey) (key database
|
||||||
}
|
}
|
||||||
|
|
||||||
func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent {
|
func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent {
|
||||||
workspace, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{
|
agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{
|
||||||
ID: takeFirst(orig.ID, uuid.New()),
|
ID: takeFirst(orig.ID, uuid.New()),
|
||||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||||
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
||||||
|
@ -154,9 +154,10 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen
|
||||||
ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600),
|
ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600),
|
||||||
TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"),
|
TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"),
|
||||||
MOTDFile: takeFirst(orig.TroubleshootingURL, ""),
|
MOTDFile: takeFirst(orig.TroubleshootingURL, ""),
|
||||||
|
DisplayApps: append([]database.DisplayApp{}, orig.DisplayApps...),
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "insert workspace agent")
|
require.NoError(t, err, "insert workspace agent")
|
||||||
return workspace
|
return agt
|
||||||
}
|
}
|
||||||
|
|
||||||
func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace {
|
func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace {
|
||||||
|
@ -204,6 +205,7 @@ func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuil
|
||||||
JobID: takeFirst(orig.JobID, uuid.New()),
|
JobID: takeFirst(orig.JobID, uuid.New()),
|
||||||
ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}),
|
ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}),
|
||||||
Deadline: takeFirst(orig.Deadline, dbtime.Now().Add(time.Hour)),
|
Deadline: takeFirst(orig.Deadline, dbtime.Now().Add(time.Hour)),
|
||||||
|
MaxDeadline: takeFirst(orig.MaxDeadline, time.Time{}),
|
||||||
Reason: takeFirst(orig.Reason, database.BuildReasonInitiator),
|
Reason: takeFirst(orig.Reason, database.BuildReasonInitiator),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -348,6 +350,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data
|
||||||
Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild),
|
Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild),
|
||||||
Input: takeFirstSlice(orig.Input, []byte("{}")),
|
Input: takeFirstSlice(orig.Input, []byte("{}")),
|
||||||
Tags: orig.Tags,
|
Tags: orig.Tags,
|
||||||
|
TraceMetadata: pqtype.NullRawMessage{},
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "insert job")
|
require.NoError(t, err, "insert job")
|
||||||
if ps != nil {
|
if ps != nil {
|
||||||
|
@ -359,6 +362,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data
|
||||||
StartedAt: orig.StartedAt,
|
StartedAt: orig.StartedAt,
|
||||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||||
Tags: must(json.Marshal(orig.Tags)),
|
Tags: must(json.Marshal(orig.Tags)),
|
||||||
|
WorkerID: uuid.NullUUID{},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
@ -460,6 +464,8 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx
|
||||||
TokenHashedSecret: hashedSecret[:],
|
TokenHashedSecret: hashedSecret[:],
|
||||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||||
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
||||||
|
DerpEnabled: takeFirst(orig.DerpEnabled, false),
|
||||||
|
DerpOnly: takeFirst(orig.DerpEnabled, false),
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "insert proxy")
|
require.NoError(t, err, "insert proxy")
|
||||||
|
|
||||||
|
|
|
@ -123,13 +123,15 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
|
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
|
||||||
ProviderID: config.ID,
|
ProviderID: config.ID,
|
||||||
UserID: apiKey.UserID,
|
UserID: apiKey.UserID,
|
||||||
CreatedAt: dbtime.Now(),
|
CreatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
OAuthAccessToken: token.AccessToken,
|
OAuthAccessToken: token.AccessToken,
|
||||||
OAuthRefreshToken: token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will set as required
|
||||||
OAuthExpiry: token.Expiry,
|
OAuthRefreshToken: token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
|
||||||
|
OAuthExpiry: token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
@ -140,12 +142,14 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
||||||
ProviderID: config.ID,
|
ProviderID: config.ID,
|
||||||
UserID: apiKey.UserID,
|
UserID: apiKey.UserID,
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
OAuthAccessToken: token.AccessToken,
|
OAuthAccessToken: token.AccessToken,
|
||||||
OAuthRefreshToken: token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: token.Expiry,
|
OAuthRefreshToken: token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
|
OAuthExpiry: token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
@ -211,13 +215,15 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
|
_, err = api.Database.InsertExternalAuthLink(ctx, database.InsertExternalAuthLinkParams{
|
||||||
ProviderID: externalAuthConfig.ID,
|
ProviderID: externalAuthConfig.ID,
|
||||||
UserID: apiKey.UserID,
|
UserID: apiKey.UserID,
|
||||||
CreatedAt: dbtime.Now(),
|
CreatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
OAuthAccessToken: state.Token.AccessToken,
|
OAuthAccessToken: state.Token.AccessToken,
|
||||||
OAuthRefreshToken: state.Token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will set as required
|
||||||
OAuthExpiry: state.Token.Expiry,
|
OAuthRefreshToken: state.Token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
|
||||||
|
OAuthExpiry: state.Token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
@ -228,12 +234,14 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
_, err = api.Database.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
||||||
ProviderID: externalAuthConfig.ID,
|
ProviderID: externalAuthConfig.ID,
|
||||||
UserID: apiKey.UserID,
|
UserID: apiKey.UserID,
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
OAuthAccessToken: state.Token.AccessToken,
|
OAuthAccessToken: state.Token.AccessToken,
|
||||||
OAuthRefreshToken: state.Token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: state.Token.Expiry,
|
OAuthRefreshToken: state.Token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
|
OAuthExpiry: state.Token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||||
|
|
|
@ -2,6 +2,7 @@ package externalauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -120,18 +121,20 @@ validate:
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.AccessToken != externalAuthLink.OAuthAccessToken {
|
if token.AccessToken != externalAuthLink.OAuthAccessToken {
|
||||||
// Update it
|
updatedAuthLink, err := db.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
||||||
externalAuthLink, err = db.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
ProviderID: c.ID,
|
||||||
ProviderID: c.ID,
|
UserID: externalAuthLink.UserID,
|
||||||
UserID: externalAuthLink.UserID,
|
UpdatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
OAuthAccessToken: token.AccessToken,
|
||||||
OAuthAccessToken: token.AccessToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthRefreshToken: token.RefreshToken,
|
OAuthRefreshToken: token.RefreshToken,
|
||||||
OAuthExpiry: token.Expiry,
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
|
OAuthExpiry: token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return externalAuthLink, false, xerrors.Errorf("update external auth link: %w", err)
|
return updatedAuthLink, false, xerrors.Errorf("update external auth link: %w", err)
|
||||||
}
|
}
|
||||||
|
externalAuthLink = updatedAuthLink
|
||||||
}
|
}
|
||||||
return externalAuthLink, true, nil
|
return externalAuthLink, true, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,13 +369,15 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||||
// If the API Key is associated with a user_link (e.g. Github/OIDC)
|
// If the API Key is associated with a user_link (e.g. Github/OIDC)
|
||||||
// then we want to update the relevant oauth fields.
|
// then we want to update the relevant oauth fields.
|
||||||
if link.UserID != uuid.Nil {
|
if link.UserID != uuid.Nil {
|
||||||
// nolint:gocritic
|
//nolint:gocritic // system needs to update user link
|
||||||
link, err = cfg.DB.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
|
link, err = cfg.DB.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
|
||||||
UserID: link.UserID,
|
UserID: link.UserID,
|
||||||
LoginType: link.LoginType,
|
LoginType: link.LoginType,
|
||||||
OAuthAccessToken: link.OAuthAccessToken,
|
OAuthAccessToken: link.OAuthAccessToken,
|
||||||
OAuthRefreshToken: link.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: link.OAuthExpiry,
|
OAuthRefreshToken: link.OAuthRefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
|
OAuthExpiry: link.OAuthExpiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return write(http.StatusInternalServerError, codersdk.Response{
|
return write(http.StatusInternalServerError, codersdk.Response{
|
||||||
|
@ -388,7 +390,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||||
// We only want to update this occasionally to reduce DB write
|
// We only want to update this occasionally to reduce DB write
|
||||||
// load. We update alongside the UserLink and APIKey since it's
|
// load. We update alongside the UserLink and APIKey since it's
|
||||||
// easier on the DB to colocate writes.
|
// easier on the DB to colocate writes.
|
||||||
// nolint:gocritic
|
//nolint:gocritic // system needs to update user last seen at
|
||||||
_, err = cfg.DB.UpdateUserLastSeenAt(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLastSeenAtParams{
|
_, err = cfg.DB.UpdateUserLastSeenAt(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLastSeenAtParams{
|
||||||
ID: key.UserID,
|
ID: key.UserID,
|
||||||
LastSeenAt: dbtime.Now(),
|
LastSeenAt: dbtime.Now(),
|
||||||
|
@ -405,7 +407,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||||
// If the key is valid, we also fetch the user roles and status.
|
// If the key is valid, we also fetch the user roles and status.
|
||||||
// The roles are used for RBAC authorize checks, and the status
|
// The roles are used for RBAC authorize checks, and the status
|
||||||
// is to block 'suspended' users from accessing the platform.
|
// is to block 'suspended' users from accessing the platform.
|
||||||
// nolint:gocritic
|
//nolint:gocritic // system needs to update user roles
|
||||||
roles, err := cfg.DB.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), key.UserID)
|
roles, err := cfg.DB.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), key.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return write(http.StatusUnauthorized, codersdk.Response{
|
return write(http.StatusUnauthorized, codersdk.Response{
|
||||||
|
|
|
@ -68,10 +68,11 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
|
||||||
var organization database.Organization
|
var organization database.Organization
|
||||||
err = api.Database.InTx(func(tx database.Store) error {
|
err = api.Database.InTx(func(tx database.Store) error {
|
||||||
organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{
|
organization, err = tx.InsertOrganization(ctx, database.InsertOrganizationParams{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
CreatedAt: dbtime.Now(),
|
CreatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
|
Description: "",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("create organization: %w", err)
|
return xerrors.Errorf("create organization: %w", err)
|
||||||
|
|
|
@ -263,18 +263,21 @@ func (s *server) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_Acquire
|
||||||
logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr))
|
logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr))
|
||||||
// Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
|
// Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
|
||||||
// in the database. We need to mark this job as failed so the end user can retry if they want to.
|
// in the database. We need to mark this job as failed so the end user can retry if they want to.
|
||||||
|
now := dbtime.Now()
|
||||||
err := s.Database.UpdateProvisionerJobWithCompleteByID(
|
err := s.Database.UpdateProvisionerJobWithCompleteByID(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
database.UpdateProvisionerJobWithCompleteByIDParams{
|
database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||||
ID: je.job.ID,
|
ID: je.job.ID,
|
||||||
CompletedAt: sql.NullTime{
|
CompletedAt: sql.NullTime{
|
||||||
Time: dbtime.Now(),
|
Time: now,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
|
UpdatedAt: now,
|
||||||
Error: sql.NullString{
|
Error: sql.NullString{
|
||||||
String: "connection to provisioner daemon broken",
|
String: "connection to provisioner daemon broken",
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
|
ErrorCode: sql.NullString{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(streamCtx, "error updating failed job", slog.Error(err))
|
logger.Error(streamCtx, "error updating failed job", slog.Error(err))
|
||||||
|
@ -308,6 +311,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
ErrorCode: job.ErrorCode,
|
ErrorCode: job.ErrorCode,
|
||||||
|
UpdatedAt: dbtime.Now(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("update provisioner job: %w", err)
|
return xerrors.Errorf("update provisioner job: %w", err)
|
||||||
|
@ -651,6 +655,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(request.Logs) > 0 {
|
if len(request.Logs) > 0 {
|
||||||
|
//nolint:exhaustruct // We append to the additional fields below.
|
||||||
insertParams := database.InsertProvisionerJobLogsParams{
|
insertParams := database.InsertProvisionerJobLogsParams{
|
||||||
JobID: parsedID,
|
JobID: parsedID,
|
||||||
}
|
}
|
||||||
|
@ -1061,7 +1066,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
Error: completedError,
|
Error: completedError,
|
||||||
|
ErrorCode: sql.NullString{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("update provisioner job: %w", err)
|
return nil, xerrors.Errorf("update provisioner job: %w", err)
|
||||||
|
@ -1118,6 +1124,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
|
Error: sql.NullString{},
|
||||||
|
ErrorCode: sql.NullString{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("update provisioner job: %w", err)
|
return xerrors.Errorf("update provisioner job: %w", err)
|
||||||
|
@ -1275,6 +1283,8 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
|
Error: sql.NullString{},
|
||||||
|
ErrorCode: sql.NullString{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("update provisioner job: %w", err)
|
return nil, xerrors.Errorf("update provisioner job: %w", err)
|
||||||
|
@ -1386,6 +1396,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||||
TroubleshootingURL: prAgent.GetTroubleshootingUrl(),
|
TroubleshootingURL: prAgent.GetTroubleshootingUrl(),
|
||||||
MOTDFile: prAgent.GetMotdFile(),
|
MOTDFile: prAgent.GetMotdFile(),
|
||||||
DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()),
|
DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()),
|
||||||
|
InstanceMetadata: pqtype.NullRawMessage{},
|
||||||
|
ResourceMetadata: pqtype.NullRawMessage{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("insert agent: %w", err)
|
return xerrors.Errorf("insert agent: %w", err)
|
||||||
|
@ -1628,11 +1640,13 @@ func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig ht
|
||||||
link.OAuthExpiry = token.Expiry
|
link.OAuthExpiry = token.Expiry
|
||||||
|
|
||||||
link, err = db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
link, err = db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
LoginType: database.LoginTypeOIDC,
|
LoginType: database.LoginTypeOIDC,
|
||||||
OAuthAccessToken: link.OAuthAccessToken,
|
OAuthAccessToken: link.OAuthAccessToken,
|
||||||
OAuthRefreshToken: link.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
OAuthExpiry: link.OAuthExpiry,
|
OAuthRefreshToken: link.OAuthRefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
|
OAuthExpiry: link.OAuthExpiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", xerrors.Errorf("update user link: %w", err)
|
return "", xerrors.Errorf("update user link: %w", err)
|
||||||
|
|
|
@ -273,7 +273,12 @@ func unhangJob(ctx context.Context, log slog.Logger, db database.Store, pub pubs
|
||||||
|
|
||||||
// Insert the messages into the build log.
|
// Insert the messages into the build log.
|
||||||
insertParams := database.InsertProvisionerJobLogsParams{
|
insertParams := database.InsertProvisionerJobLogsParams{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
CreatedAt: nil,
|
||||||
|
Source: nil,
|
||||||
|
Level: nil,
|
||||||
|
Stage: nil,
|
||||||
|
Output: nil,
|
||||||
}
|
}
|
||||||
now := dbtime.Now()
|
now := dbtime.Now()
|
||||||
for i, msg := range HungJobLogMessages {
|
for i, msg := range HungJobLogMessages {
|
||||||
|
|
|
@ -1327,14 +1327,16 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
|
||||||
}
|
}
|
||||||
|
|
||||||
if link.UserID == uuid.Nil {
|
if link.UserID == uuid.Nil {
|
||||||
//nolint:gocritic
|
//nolint:gocritic // System needs to insert the user link (linked_id, oauth_token, oauth_expiry).
|
||||||
link, err = tx.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
|
link, err = tx.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
LoginType: params.LoginType,
|
LoginType: params.LoginType,
|
||||||
LinkedID: params.LinkedID,
|
LinkedID: params.LinkedID,
|
||||||
OAuthAccessToken: params.State.Token.AccessToken,
|
OAuthAccessToken: params.State.Token.AccessToken,
|
||||||
OAuthRefreshToken: params.State.Token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
OAuthExpiry: params.State.Token.Expiry,
|
OAuthRefreshToken: params.State.Token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
|
OAuthExpiry: params.State.Token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("insert user link: %w", err)
|
return xerrors.Errorf("insert user link: %w", err)
|
||||||
|
@ -1342,13 +1344,15 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
|
||||||
}
|
}
|
||||||
|
|
||||||
if link.UserID != uuid.Nil {
|
if link.UserID != uuid.Nil {
|
||||||
//nolint:gocritic
|
//nolint:gocritic // System needs to update the user link (linked_id, oauth_token, oauth_expiry).
|
||||||
link, err = tx.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
|
link, err = tx.UpdateUserLink(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkParams{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
LoginType: params.LoginType,
|
LoginType: params.LoginType,
|
||||||
OAuthAccessToken: params.State.Token.AccessToken,
|
OAuthAccessToken: params.State.Token.AccessToken,
|
||||||
OAuthRefreshToken: params.State.Token.RefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
OAuthExpiry: params.State.Token.Expiry,
|
OAuthRefreshToken: params.State.Token.RefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
|
||||||
|
OAuthExpiry: params.State.Token.Expiry,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("update user link: %w", err)
|
return xerrors.Errorf("update user link: %w", err)
|
||||||
|
|
|
@ -1079,10 +1079,11 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := tx.InsertOrganization(ctx, database.InsertOrganizationParams{
|
organization, err := tx.InsertOrganization(ctx, database.InsertOrganizationParams{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Name: req.Username,
|
Name: req.Username,
|
||||||
CreatedAt: dbtime.Now(),
|
CreatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
|
Description: "",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("create organization: %w", err)
|
return xerrors.Errorf("create organization: %w", err)
|
||||||
|
@ -1101,11 +1102,12 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
|
||||||
}
|
}
|
||||||
|
|
||||||
params := database.InsertUserParams{
|
params := database.InsertUserParams{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
CreatedAt: dbtime.Now(),
|
CreatedAt: dbtime.Now(),
|
||||||
UpdatedAt: dbtime.Now(),
|
UpdatedAt: dbtime.Now(),
|
||||||
|
HashedPassword: []byte{},
|
||||||
// All new users are defaulted to members of the site.
|
// All new users are defaulted to members of the site.
|
||||||
RBACRoles: []string{},
|
RBACRoles: []string{},
|
||||||
LoginType: req.LoginType,
|
LoginType: req.LoginType,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
@ -350,6 +351,8 @@ func (b *Builder) buildTx(authFunc func(action rbac.Action, object rbac.Objecter
|
||||||
Transition: b.trans,
|
Transition: b.trans,
|
||||||
JobID: provisionerJob.ID,
|
JobID: provisionerJob.ID,
|
||||||
Reason: b.reason,
|
Reason: b.reason,
|
||||||
|
Deadline: time.Time{}, // set by provisioner upon completion
|
||||||
|
MaxDeadline: time.Time{}, // set by provisioner upon completion
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildError{http.StatusInternalServerError, "insert workspace build", err}
|
return BuildError{http.StatusInternalServerError, "insert workspace build", err}
|
||||||
|
|
|
@ -25,8 +25,8 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
|
||||||
}
|
}
|
||||||
log.Info(ctx, "encrypting user tokens", slog.F("user_count", len(userIDs)))
|
log.Info(ctx, "encrypting user tokens", slog.F("user_count", len(userIDs)))
|
||||||
for idx, uid := range userIDs {
|
for idx, uid := range userIDs {
|
||||||
err := cryptDB.InTx(func(tx database.Store) error {
|
err := cryptDB.InTx(func(cryptTx database.Store) error {
|
||||||
userLinks, err := tx.GetUserLinksByUserID(ctx, uid)
|
userLinks, err := cryptTx.GetUserLinksByUserID(ctx, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("get user links for user: %w", err)
|
return xerrors.Errorf("get user links for user: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -35,18 +35,20 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
|
||||||
log.Debug(ctx, "skipping user link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
|
log.Debug(ctx, "skipping user link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
if _, err := cryptTx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
||||||
OAuthAccessToken: userLink.OAuthAccessToken,
|
OAuthAccessToken: userLink.OAuthAccessToken,
|
||||||
OAuthRefreshToken: userLink.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: userLink.OAuthExpiry,
|
OAuthRefreshToken: userLink.OAuthRefreshToken,
|
||||||
UserID: uid,
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
LoginType: userLink.LoginType,
|
OAuthExpiry: userLink.OAuthExpiry,
|
||||||
|
UserID: uid,
|
||||||
|
LoginType: userLink.LoginType,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
|
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gitAuthLinks, err := tx.GetExternalAuthLinksByUserID(ctx, uid)
|
gitAuthLinks, err := cryptTx.GetExternalAuthLinksByUserID(ctx, uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("get git auth links for user: %w", err)
|
return xerrors.Errorf("get git auth links for user: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -55,13 +57,15 @@ func Rotate(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciphe
|
||||||
log.Debug(ctx, "skipping git auth link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
|
log.Debug(ctx, "skipping git auth link", slog.F("user_id", uid), slog.F("current", idx+1), slog.F("cipher", ciphers[0].HexDigest()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
if _, err := cryptTx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
||||||
ProviderID: gitAuthLink.ProviderID,
|
ProviderID: gitAuthLink.ProviderID,
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
UpdatedAt: gitAuthLink.UpdatedAt,
|
UpdatedAt: gitAuthLink.UpdatedAt,
|
||||||
OAuthAccessToken: gitAuthLink.OAuthAccessToken,
|
OAuthAccessToken: gitAuthLink.OAuthAccessToken,
|
||||||
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
OAuthExpiry: gitAuthLink.OAuthExpiry,
|
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
|
||||||
|
OAuthExpiry: gitAuthLink.OAuthExpiry,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
|
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
|
||||||
}
|
}
|
||||||
|
@ -120,11 +124,13 @@ func Decrypt(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciph
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
if _, err := tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
|
||||||
OAuthAccessToken: userLink.OAuthAccessToken,
|
OAuthAccessToken: userLink.OAuthAccessToken,
|
||||||
OAuthRefreshToken: userLink.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
|
||||||
OAuthExpiry: userLink.OAuthExpiry,
|
OAuthRefreshToken: userLink.OAuthRefreshToken,
|
||||||
UserID: uid,
|
OAuthRefreshTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
|
||||||
LoginType: userLink.LoginType,
|
OAuthExpiry: userLink.OAuthExpiry,
|
||||||
|
UserID: uid,
|
||||||
|
LoginType: userLink.LoginType,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
|
return xerrors.Errorf("update user link user_id=%s linked_id=%s: %w", userLink.UserID, userLink.LinkedID, err)
|
||||||
}
|
}
|
||||||
|
@ -140,12 +146,14 @@ func Decrypt(ctx context.Context, log slog.Logger, sqlDB *sql.DB, ciphers []Ciph
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
if _, err := tx.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{
|
||||||
ProviderID: gitAuthLink.ProviderID,
|
ProviderID: gitAuthLink.ProviderID,
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
UpdatedAt: gitAuthLink.UpdatedAt,
|
UpdatedAt: gitAuthLink.UpdatedAt,
|
||||||
OAuthAccessToken: gitAuthLink.OAuthAccessToken,
|
OAuthAccessToken: gitAuthLink.OAuthAccessToken,
|
||||||
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
|
OAuthAccessTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
|
||||||
OAuthExpiry: gitAuthLink.OAuthExpiry,
|
OAuthRefreshToken: gitAuthLink.OAuthRefreshToken,
|
||||||
|
OAuthRefreshTokenKeyID: sql.NullString{}, // we explicitly want to clear the key id
|
||||||
|
OAuthExpiry: gitAuthLink.OAuthExpiry,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
|
return xerrors.Errorf("update git auth link user_id=%s provider_id=%s: %w", gitAuthLink.UserID, gitAuthLink.ProviderID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,11 +420,13 @@ func (m *Manager) Close() error {
|
||||||
Time: dbtime.Now(),
|
Time: dbtime.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
RelayAddress: m.self.RelayAddress,
|
RelayAddress: m.self.RelayAddress,
|
||||||
RegionID: m.self.RegionID,
|
RegionID: m.self.RegionID,
|
||||||
Hostname: m.self.Hostname,
|
Hostname: m.self.Hostname,
|
||||||
Version: m.self.Version,
|
Version: m.self.Version,
|
||||||
Error: m.self.Error,
|
Error: m.self.Error,
|
||||||
|
DatabaseLatency: 0, // A stopped replica has no latency.
|
||||||
|
Primary: false, // A stopped replica cannot be primary.
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("update replica: %w", err)
|
return xerrors.Errorf("update replica: %w", err)
|
||||||
|
|
Loading…
Reference in New Issue