mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
Moving from streaming manifest approach to SSE + explicit full agent reinitialization
Signed-off-by: Danny Kopping <danny@coder.com> # Conflicts: # agent/agent.go # Conflicts: # cli/agent.go
This commit is contained in:
270
agent/agent.go
270
agent/agent.go
@ -33,6 +33,9 @@ import (
|
|||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
|
||||||
|
"github.com/coder/retry"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/agent/agentcontainers"
|
"github.com/coder/coder/v2/agent/agentcontainers"
|
||||||
"github.com/coder/coder/v2/agent/agentexec"
|
"github.com/coder/coder/v2/agent/agentexec"
|
||||||
"github.com/coder/coder/v2/agent/agentscripts"
|
"github.com/coder/coder/v2/agent/agentscripts"
|
||||||
@ -47,7 +50,6 @@ import (
|
|||||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||||
"github.com/coder/coder/v2/tailnet"
|
"github.com/coder/coder/v2/tailnet"
|
||||||
tailnetproto "github.com/coder/coder/v2/tailnet/proto"
|
tailnetproto "github.com/coder/coder/v2/tailnet/proto"
|
||||||
"github.com/coder/retry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -87,8 +89,8 @@ type Options struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
ConnectRPC24(ctx context.Context) (
|
ConnectRPC23(ctx context.Context) (
|
||||||
proto.DRPCAgentClient24, tailnetproto.DRPCTailnetClient23, error,
|
proto.DRPCAgentClient23, tailnetproto.DRPCTailnetClient23, error,
|
||||||
)
|
)
|
||||||
RewriteDERPMap(derpMap *tailcfg.DERPMap)
|
RewriteDERPMap(derpMap *tailcfg.DERPMap)
|
||||||
}
|
}
|
||||||
@ -314,11 +316,11 @@ func (a *agent) runLoop() {
|
|||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
// Context canceled errors may come from websocket pings, so we
|
// Context canceled errors may come from websocket pings, so we
|
||||||
// don't want to use `errors.Is(err, context.Canceled)` here.
|
// don't want to use `errors.Is(err, context.Canceled)` here.
|
||||||
a.logger.Warn(ctx, "exiting", slog.Error(ctx.Err()))
|
a.logger.Warn(ctx, "runLoop exited with error", slog.Error(ctx.Err()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if a.isClosed() {
|
if a.isClosed() {
|
||||||
a.logger.Debug(ctx, "closed")
|
a.logger.Warn(ctx, "runLoop exited because agent is closed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
@ -408,7 +410,7 @@ func (t *trySingleflight) Do(key string, fn func()) {
|
|||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agent) reportMetadata(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func (a *agent) reportMetadata(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
tickerDone := make(chan struct{})
|
tickerDone := make(chan struct{})
|
||||||
collectDone := make(chan struct{})
|
collectDone := make(chan struct{})
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
@ -624,7 +626,7 @@ func (a *agent) reportMetadata(ctx context.Context, aAPI proto.DRPCAgentClient24
|
|||||||
|
|
||||||
// reportLifecycle reports the current lifecycle state once. All state
|
// reportLifecycle reports the current lifecycle state once. All state
|
||||||
// changes are reported in order.
|
// changes are reported in order.
|
||||||
func (a *agent) reportLifecycle(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func (a *agent) reportLifecycle(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-a.lifecycleUpdate:
|
case <-a.lifecycleUpdate:
|
||||||
@ -706,7 +708,7 @@ func (a *agent) setLifecycle(state codersdk.WorkspaceAgentLifecycle) {
|
|||||||
// fetchServiceBannerLoop fetches the service banner on an interval. It will
|
// fetchServiceBannerLoop fetches the service banner on an interval. It will
|
||||||
// not be fetched immediately; the expectation is that it is primed elsewhere
|
// not be fetched immediately; the expectation is that it is primed elsewhere
|
||||||
// (and must be done before the session actually starts).
|
// (and must be done before the session actually starts).
|
||||||
func (a *agent) fetchServiceBannerLoop(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func (a *agent) fetchServiceBannerLoop(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
ticker := time.NewTicker(a.announcementBannersRefreshInterval)
|
ticker := time.NewTicker(a.announcementBannersRefreshInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
@ -742,7 +744,7 @@ func (a *agent) run() (retErr error) {
|
|||||||
a.sessionToken.Store(&sessionToken)
|
a.sessionToken.Store(&sessionToken)
|
||||||
|
|
||||||
// ConnectRPC returns the dRPC connection we use for the Agent and Tailnet v2+ APIs
|
// ConnectRPC returns the dRPC connection we use for the Agent and Tailnet v2+ APIs
|
||||||
aAPI, tAPI, err := a.client.ConnectRPC24(a.hardCtx)
|
aAPI, tAPI, err := a.client.ConnectRPC23(a.hardCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -759,7 +761,7 @@ func (a *agent) run() (retErr error) {
|
|||||||
connMan := newAPIConnRoutineManager(a.gracefulCtx, a.hardCtx, a.logger, aAPI, tAPI)
|
connMan := newAPIConnRoutineManager(a.gracefulCtx, a.hardCtx, a.logger, aAPI, tAPI)
|
||||||
|
|
||||||
connMan.startAgentAPI("init notification banners", gracefulShutdownBehaviorStop,
|
connMan.startAgentAPI("init notification banners", gracefulShutdownBehaviorStop,
|
||||||
func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
bannersProto, err := aAPI.GetAnnouncementBanners(ctx, &proto.GetAnnouncementBannersRequest{})
|
bannersProto, err := aAPI.GetAnnouncementBanners(ctx, &proto.GetAnnouncementBannersRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("fetch service banner: %w", err)
|
return xerrors.Errorf("fetch service banner: %w", err)
|
||||||
@ -776,7 +778,7 @@ func (a *agent) run() (retErr error) {
|
|||||||
// sending logs gets gracefulShutdownBehaviorRemain because we want to send logs generated by
|
// sending logs gets gracefulShutdownBehaviorRemain because we want to send logs generated by
|
||||||
// shutdown scripts.
|
// shutdown scripts.
|
||||||
connMan.startAgentAPI("send logs", gracefulShutdownBehaviorRemain,
|
connMan.startAgentAPI("send logs", gracefulShutdownBehaviorRemain,
|
||||||
func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
err := a.logSender.SendLoop(ctx, aAPI)
|
err := a.logSender.SendLoop(ctx, aAPI)
|
||||||
if xerrors.Is(err, agentsdk.LogLimitExceededError) {
|
if xerrors.Is(err, agentsdk.LogLimitExceededError) {
|
||||||
// we don't want this error to tear down the API connection and propagate to the
|
// we don't want this error to tear down the API connection and propagate to the
|
||||||
@ -813,11 +815,10 @@ func (a *agent) run() (retErr error) {
|
|||||||
networkOK := newCheckpoint(a.logger)
|
networkOK := newCheckpoint(a.logger)
|
||||||
manifestOK := newCheckpoint(a.logger)
|
manifestOK := newCheckpoint(a.logger)
|
||||||
|
|
||||||
//connMan.startAgentAPI("handle manifest", gracefulShutdownBehaviorStop, a.handleManifest(manifestOK))
|
connMan.startAgentAPI("handle manifest", gracefulShutdownBehaviorStop, a.handleManifest(manifestOK))
|
||||||
connMan.startAgentAPI("handle manifest stream", gracefulShutdownBehaviorStop, a.handleManifestStream(manifestOK))
|
|
||||||
|
|
||||||
connMan.startAgentAPI("app health reporter", gracefulShutdownBehaviorStop,
|
connMan.startAgentAPI("app health reporter", gracefulShutdownBehaviorStop,
|
||||||
func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
if err := manifestOK.wait(ctx); err != nil {
|
if err := manifestOK.wait(ctx); err != nil {
|
||||||
return xerrors.Errorf("no manifest: %w", err)
|
return xerrors.Errorf("no manifest: %w", err)
|
||||||
}
|
}
|
||||||
@ -850,182 +851,131 @@ func (a *agent) run() (retErr error) {
|
|||||||
|
|
||||||
connMan.startAgentAPI("fetch service banner loop", gracefulShutdownBehaviorStop, a.fetchServiceBannerLoop)
|
connMan.startAgentAPI("fetch service banner loop", gracefulShutdownBehaviorStop, a.fetchServiceBannerLoop)
|
||||||
|
|
||||||
connMan.startAgentAPI("stats report loop", gracefulShutdownBehaviorStop, func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
connMan.startAgentAPI("stats report loop", gracefulShutdownBehaviorStop, func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
if err := networkOK.wait(ctx); err != nil {
|
if err := networkOK.wait(ctx); err != nil {
|
||||||
return xerrors.Errorf("no network: %w", err)
|
return xerrors.Errorf("no network: %w", err)
|
||||||
}
|
}
|
||||||
return a.statsReporter.reportLoop(ctx, aAPI)
|
return a.statsReporter.reportLoop(ctx, aAPI)
|
||||||
})
|
})
|
||||||
|
|
||||||
return connMan.wait()
|
err = connMan.wait()
|
||||||
|
a.logger.Error(context.Background(), "connection manager errored", slog.Error(err))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleManifest returns a function that fetches and processes the manifest
|
// handleManifest returns a function that fetches and processes the manifest
|
||||||
func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
return func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
return func(ctx context.Context, aAPI proto.DRPCAgentClient23) error {
|
||||||
var err error
|
var (
|
||||||
|
sentResult = false
|
||||||
|
err error
|
||||||
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if !sentResult {
|
||||||
manifestOK.complete(err)
|
manifestOK.complete(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
mp, err := aAPI.GetManifest(ctx, &proto.GetManifestRequest{})
|
mp, err := aAPI.GetManifest(ctx, &proto.GetManifestRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("fetch manifest: %w", err)
|
return xerrors.Errorf("fetch metadata: %w", err)
|
||||||
}
|
}
|
||||||
a.logger.Info(ctx, "fetched manifest", slog.F("manifest", mp))
|
a.logger.Info(ctx, "fetched manifest", slog.F("manifest", mp))
|
||||||
return a.handleSingleManifest(ctx, aAPI, manifestOK, mp)
|
manifest, err := agentsdk.ManifestFromProto(mp)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *agent) handleManifestStream(manifestOK *checkpoint) func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
|
||||||
return func(ctx context.Context, aAPI proto.DRPCAgentClient24) error {
|
|
||||||
var err error
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
manifestOK.complete(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
client, err := aAPI.StreamManifests(ctx, &proto.GetManifestRequest{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
a.logger.Critical(ctx, "failed to convert manifest", slog.F("manifest", mp), slog.Error(err))
|
||||||
a.logger.Info(ctx, "stream manifest received EOF")
|
return xerrors.Errorf("convert manifest: %w", err)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return xerrors.Errorf("stream manifests: %w", err)
|
|
||||||
}
|
}
|
||||||
|
if manifest.AgentID == uuid.Nil {
|
||||||
for {
|
return xerrors.New("nil agentID returned by manifest")
|
||||||
a.logger.Debug(ctx, "waiting on new streamed manifest")
|
|
||||||
|
|
||||||
manifest, err := client.Recv()
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("recv manifest: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.logger.Info(ctx, "received new streamed manifest", slog.F("manifest", manifest))
|
|
||||||
|
|
||||||
err = a.handleSingleManifest(ctx, aAPI, manifestOK, manifest)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("handle streamed manifest: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
a.client.RewriteDERPMap(manifest.DERPMap)
|
||||||
}
|
|
||||||
|
|
||||||
func (a *agent) handleSingleManifest(ctx context.Context, aAPI proto.DRPCAgentClient24, manifestOK *checkpoint, mp *proto.Manifest) error {
|
// Expand the directory and send it back to coderd so external
|
||||||
var (
|
// applications that rely on the directory can use it.
|
||||||
sentResult bool
|
//
|
||||||
err error
|
// An example is VS Code Remote, which must know the directory
|
||||||
)
|
// before initializing a connection.
|
||||||
defer func() {
|
manifest.Directory, err = expandDirectory(manifest.Directory)
|
||||||
if !sentResult {
|
|
||||||
manifestOK.complete(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
a.metrics.manifestsReceived.Inc()
|
|
||||||
|
|
||||||
manifest, err := agentsdk.ManifestFromProto(mp)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Critical(ctx, "failed to convert manifest", slog.F("manifest", mp), slog.Error(err))
|
|
||||||
return xerrors.Errorf("convert manifest: %w", err)
|
|
||||||
}
|
|
||||||
if manifest.AgentID == uuid.Nil {
|
|
||||||
return xerrors.New("nil agentID returned by manifest")
|
|
||||||
}
|
|
||||||
a.client.RewriteDERPMap(manifest.DERPMap)
|
|
||||||
|
|
||||||
// Expand the directory and send it back to coderd so external
|
|
||||||
// applications that rely on the directory can use it.
|
|
||||||
//
|
|
||||||
// An example is VS Code Remote, which must know the directory
|
|
||||||
// before initializing a connection.
|
|
||||||
manifest.Directory, err = expandDirectory(manifest.Directory)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("expand directory: %w", err)
|
|
||||||
}
|
|
||||||
subsys, err := agentsdk.ProtoFromSubsystems(a.subsystems)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Critical(ctx, "failed to convert subsystems", slog.Error(err))
|
|
||||||
return xerrors.Errorf("failed to convert subsystems: %w", err)
|
|
||||||
}
|
|
||||||
_, err = aAPI.UpdateStartup(ctx, &proto.UpdateStartupRequest{Startup: &proto.Startup{
|
|
||||||
Version: buildinfo.Version(),
|
|
||||||
ExpandedDirectory: manifest.Directory,
|
|
||||||
Subsystems: subsys,
|
|
||||||
}})
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("update workspace agent startup: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldManifest := a.manifest.Swap(&manifest)
|
|
||||||
manifestOK.complete(nil)
|
|
||||||
sentResult = true
|
|
||||||
|
|
||||||
// TODO: this will probably have to change in the case of prebuilds; maybe check if owner is the same,
|
|
||||||
// or add prebuild metadata to manifest?
|
|
||||||
// The startup script should only execute on the first run!
|
|
||||||
if oldManifest == nil {
|
|
||||||
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStarting)
|
|
||||||
|
|
||||||
// Perform overrides early so that Git auth can work even if users
|
|
||||||
// connect to a workspace that is not yet ready. We don't run this
|
|
||||||
// concurrently with the startup script to avoid conflicts between
|
|
||||||
// them.
|
|
||||||
if manifest.GitAuthConfigs > 0 {
|
|
||||||
// If this fails, we should consider surfacing the error in the
|
|
||||||
// startup log and setting the lifecycle state to be "start_error"
|
|
||||||
// (after startup script completion), but for now we'll just log it.
|
|
||||||
err := gitauth.OverrideVSCodeConfigs(a.filesystem)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Warn(ctx, "failed to override vscode git auth configs", slog.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.scriptRunner.Init(manifest.Scripts, aAPI.ScriptCompleted)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("init script runner: %w", err)
|
return xerrors.Errorf("expand directory: %w", err)
|
||||||
}
|
}
|
||||||
err = a.trackGoroutine(func() {
|
subsys, err := agentsdk.ProtoFromSubsystems(a.subsystems)
|
||||||
start := time.Now()
|
if err != nil {
|
||||||
// here we use the graceful context because the script runner is not directly tied
|
a.logger.Critical(ctx, "failed to convert subsystems", slog.Error(err))
|
||||||
// to the agent API.
|
return xerrors.Errorf("failed to convert subsystems: %w", err)
|
||||||
err := a.scriptRunner.Execute(a.gracefulCtx, agentscripts.ExecuteStartScripts)
|
}
|
||||||
// Measure the time immediately after the script has finished
|
_, err = aAPI.UpdateStartup(ctx, &proto.UpdateStartupRequest{Startup: &proto.Startup{
|
||||||
dur := time.Since(start).Seconds()
|
Version: buildinfo.Version(),
|
||||||
if err != nil {
|
ExpandedDirectory: manifest.Directory,
|
||||||
a.logger.Warn(ctx, "startup script(s) failed", slog.Error(err))
|
Subsystems: subsys,
|
||||||
if errors.Is(err, agentscripts.ErrTimeout) {
|
}})
|
||||||
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStartTimeout)
|
if err != nil {
|
||||||
} else {
|
return xerrors.Errorf("update workspace agent startup: %w", err)
|
||||||
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStartError)
|
}
|
||||||
|
|
||||||
|
oldManifest := a.manifest.Swap(&manifest)
|
||||||
|
manifestOK.complete(nil)
|
||||||
|
sentResult = true
|
||||||
|
|
||||||
|
// The startup script should only execute on the first run!
|
||||||
|
if oldManifest == nil {
|
||||||
|
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStarting)
|
||||||
|
|
||||||
|
// Perform overrides early so that Git auth can work even if users
|
||||||
|
// connect to a workspace that is not yet ready. We don't run this
|
||||||
|
// concurrently with the startup script to avoid conflicts between
|
||||||
|
// them.
|
||||||
|
if manifest.GitAuthConfigs > 0 {
|
||||||
|
// If this fails, we should consider surfacing the error in the
|
||||||
|
// startup log and setting the lifecycle state to be "start_error"
|
||||||
|
// (after startup script completion), but for now we'll just log it.
|
||||||
|
err := gitauth.OverrideVSCodeConfigs(a.filesystem)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn(ctx, "failed to override vscode git auth configs", slog.Error(err))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
a.setLifecycle(codersdk.WorkspaceAgentLifecycleReady)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label := "false"
|
err = a.scriptRunner.Init(manifest.Scripts, aAPI.ScriptCompleted)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
label = "true"
|
return xerrors.Errorf("init script runner: %w", err)
|
||||||
|
}
|
||||||
|
err = a.trackGoroutine(func() {
|
||||||
|
start := time.Now()
|
||||||
|
// here we use the graceful context because the script runner is not directly tied
|
||||||
|
// to the agent API.
|
||||||
|
err := a.scriptRunner.Execute(a.gracefulCtx, agentscripts.ExecuteStartScripts)
|
||||||
|
// Measure the time immediately after the script has finished
|
||||||
|
dur := time.Since(start).Seconds()
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn(ctx, "startup script(s) failed", slog.Error(err))
|
||||||
|
if errors.Is(err, agentscripts.ErrTimeout) {
|
||||||
|
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStartTimeout)
|
||||||
|
} else {
|
||||||
|
a.setLifecycle(codersdk.WorkspaceAgentLifecycleStartError)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.setLifecycle(codersdk.WorkspaceAgentLifecycleReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
label := "false"
|
||||||
|
if err == nil {
|
||||||
|
label = "true"
|
||||||
|
}
|
||||||
|
a.metrics.startupScriptSeconds.WithLabelValues(label).Set(dur)
|
||||||
|
a.scriptRunner.StartCron()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("track conn goroutine: %w", err)
|
||||||
}
|
}
|
||||||
a.metrics.startupScriptSeconds.WithLabelValues(label).Set(dur)
|
|
||||||
a.scriptRunner.StartCron()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("track conn goroutine: %w", err)
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createOrUpdateNetwork waits for the manifest to be set using manifestOK, then creates or updates
|
// createOrUpdateNetwork waits for the manifest to be set using manifestOK, then creates or updates
|
||||||
// the tailnet using the information in the manifest
|
// the tailnet using the information in the manifest
|
||||||
func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(context.Context, proto.DRPCAgentClient24) error {
|
func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(context.Context, proto.DRPCAgentClient23) error {
|
||||||
return func(ctx context.Context, _ proto.DRPCAgentClient24) (retErr error) {
|
return func(ctx context.Context, _ proto.DRPCAgentClient23) (retErr error) {
|
||||||
if err := manifestOK.wait(ctx); err != nil {
|
if err := manifestOK.wait(ctx); err != nil {
|
||||||
return xerrors.Errorf("no manifest: %w", err)
|
return xerrors.Errorf("no manifest: %w", err)
|
||||||
}
|
}
|
||||||
@ -1746,7 +1696,7 @@ const (
|
|||||||
|
|
||||||
type apiConnRoutineManager struct {
|
type apiConnRoutineManager struct {
|
||||||
logger slog.Logger
|
logger slog.Logger
|
||||||
aAPI proto.DRPCAgentClient24
|
aAPI proto.DRPCAgentClient23
|
||||||
tAPI tailnetproto.DRPCTailnetClient23
|
tAPI tailnetproto.DRPCTailnetClient23
|
||||||
eg *errgroup.Group
|
eg *errgroup.Group
|
||||||
stopCtx context.Context
|
stopCtx context.Context
|
||||||
@ -1755,7 +1705,7 @@ type apiConnRoutineManager struct {
|
|||||||
|
|
||||||
func newAPIConnRoutineManager(
|
func newAPIConnRoutineManager(
|
||||||
gracefulCtx, hardCtx context.Context, logger slog.Logger,
|
gracefulCtx, hardCtx context.Context, logger slog.Logger,
|
||||||
aAPI proto.DRPCAgentClient24, tAPI tailnetproto.DRPCTailnetClient23,
|
aAPI proto.DRPCAgentClient23, tAPI tailnetproto.DRPCTailnetClient23,
|
||||||
) *apiConnRoutineManager {
|
) *apiConnRoutineManager {
|
||||||
// routines that remain in operation during graceful shutdown use the remainCtx. They'll still
|
// routines that remain in operation during graceful shutdown use the remainCtx. They'll still
|
||||||
// exit if the errgroup hits an error, which usually means a problem with the conn.
|
// exit if the errgroup hits an error, which usually means a problem with the conn.
|
||||||
@ -1788,7 +1738,7 @@ func newAPIConnRoutineManager(
|
|||||||
// but for Tailnet.
|
// but for Tailnet.
|
||||||
func (a *apiConnRoutineManager) startAgentAPI(
|
func (a *apiConnRoutineManager) startAgentAPI(
|
||||||
name string, behavior gracefulShutdownBehavior,
|
name string, behavior gracefulShutdownBehavior,
|
||||||
f func(context.Context, proto.DRPCAgentClient24) error,
|
f func(context.Context, proto.DRPCAgentClient23) error,
|
||||||
) {
|
) {
|
||||||
logger := a.logger.With(slog.F("name", name))
|
logger := a.logger.With(slog.F("name", name))
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
|
||||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
@ -97,7 +98,7 @@ func (c *Client) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ConnectRPC23(ctx context.Context) (
|
func (c *Client) ConnectRPC23(ctx context.Context) (
|
||||||
agentproto.DRPCAgentClient24, proto.DRPCTailnetClient23, error,
|
agentproto.DRPCAgentClient23, proto.DRPCTailnetClient23, error,
|
||||||
) {
|
) {
|
||||||
conn, lis := drpcsdk.MemTransportPipe()
|
conn, lis := drpcsdk.MemTransportPipe()
|
||||||
c.LastWorkspaceAgent = func() {
|
c.LastWorkspaceAgent = func() {
|
||||||
|
@ -3098,78 +3098,73 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
|
|||||||
0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e,
|
0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e,
|
||||||
0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07,
|
0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07,
|
||||||
0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x48,
|
0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x48,
|
||||||
0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x04, 0x32, 0xc2, 0x08, 0x0a, 0x05, 0x41, 0x67, 0x65,
|
0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x04, 0x32, 0xef, 0x07, 0x0a, 0x05, 0x41, 0x67, 0x65,
|
||||||
0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||||
0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
|
0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65,
|
0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
|
0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
|
||||||
0x51, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e,
|
||||||
0x74, 0x73, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
0x6e, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
|
||||||
0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52,
|
0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
|
0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63,
|
||||||
0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74,
|
0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65,
|
||||||
0x30, 0x01, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x0b, 0x55,
|
||||||
0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
|
0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64,
|
||||||
0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,
|
0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61,
|
||||||
0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
|
||||||
0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
|
|
||||||
0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x56,
|
|
||||||
0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x22, 0x2e,
|
|
||||||
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55,
|
|
||||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
|
||||||
0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
|
|
||||||
0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
|
|
||||||
0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65,
|
|
||||||
0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
|
|
||||||
0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
|
||||||
0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
|
|
||||||
0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x15,
|
|
||||||
0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65,
|
|
||||||
0x61, 0x6c, 0x74, 0x68, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
|
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61,
|
|
||||||
0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
|
|
||||||
0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
|
||||||
0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
|
|
||||||
0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
|
||||||
0x12, 0x4e, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75,
|
|
||||||
0x70, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
|
|
||||||
0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
|
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
|
|
||||||
0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
|
|
||||||
0x12, 0x6e, 0x0a, 0x13, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d,
|
|
||||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
|
|
||||||
0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70,
|
|
||||||
0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
|
|
||||||
0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
|
|
||||||
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
|
||||||
0x12, 0x62, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c,
|
|
||||||
0x6f, 0x67, 0x73, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
|
|
||||||
0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
|
||||||
0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f,
|
|
||||||
0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74,
|
|
||||||
0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70,
|
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75,
|
|
||||||
0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x2d,
|
|
||||||
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
|
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
|
||||||
0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e,
|
0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66,
|
||||||
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47,
|
0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
|
||||||
0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61,
|
0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69,
|
||||||
0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a,
|
0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19,
|
||||||
0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64,
|
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
|
||||||
0x12, 0x34, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
|
0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x42, 0x61, 0x74,
|
||||||
0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
|
0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||||
0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52,
|
0x68, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
|
0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
|
||||||
0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
|
0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70,
|
0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
|
||||||
0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a,
|
0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48,
|
||||||
0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65,
|
0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a,
|
||||||
0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x24,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
|
||||||
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71,
|
||||||
|
0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
|
||||||
|
0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x6e, 0x0a,
|
||||||
|
0x13, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61,
|
||||||
|
0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
|
||||||
|
0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
|
||||||
|
0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
|
||||||
|
0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74,
|
||||||
|
0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a,
|
||||||
|
0x0f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73,
|
||||||
|
0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
|
||||||
|
0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67,
|
||||||
|
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||||
|
0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43,
|
||||||
|
0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
|
0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65,
|
||||||
|
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f,
|
||||||
|
0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
|
||||||
|
0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e,
|
||||||
|
0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x64,
|
||||||
|
0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41,
|
||||||
|
0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65,
|
||||||
|
0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x0f, 0x53, 0x63,
|
||||||
|
0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x34, 0x2e,
|
||||||
|
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57,
|
||||||
|
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72,
|
||||||
|
0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
|
||||||
|
0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67,
|
||||||
|
0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||||
|
0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69,
|
||||||
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63,
|
||||||
|
0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -3277,29 +3272,27 @@ var file_agent_proto_agent_proto_depIdxs = []int32{
|
|||||||
43, // 37: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
|
43, // 37: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
|
||||||
0, // 38: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
|
0, // 38: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
|
||||||
13, // 39: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
|
13, // 39: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
|
||||||
13, // 40: coder.agent.v2.Agent.StreamManifests:input_type -> coder.agent.v2.GetManifestRequest
|
15, // 40: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
|
||||||
15, // 41: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
|
17, // 41: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
|
||||||
17, // 42: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
|
20, // 42: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
|
||||||
20, // 43: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
|
21, // 43: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
|
||||||
21, // 44: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
|
24, // 44: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
|
||||||
24, // 45: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
|
26, // 45: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
|
||||||
26, // 46: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
|
29, // 46: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
|
||||||
29, // 47: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
|
31, // 47: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
|
||||||
31, // 48: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
|
34, // 48: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
|
||||||
34, // 49: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
|
12, // 49: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
|
||||||
12, // 50: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
|
14, // 50: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
|
||||||
12, // 51: coder.agent.v2.Agent.StreamManifests:output_type -> coder.agent.v2.Manifest
|
18, // 51: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
|
||||||
14, // 52: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
|
19, // 52: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
|
||||||
18, // 53: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
|
22, // 53: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
|
||||||
19, // 54: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
|
23, // 54: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
|
||||||
22, // 55: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
|
27, // 55: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
|
||||||
23, // 56: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
|
30, // 56: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
|
||||||
27, // 57: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
|
32, // 57: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
|
||||||
30, // 58: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
|
35, // 58: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
|
||||||
32, // 59: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
|
49, // [49:59] is the sub-list for method output_type
|
||||||
35, // 60: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
|
39, // [39:49] is the sub-list for method input_type
|
||||||
50, // [50:61] is the sub-list for method output_type
|
|
||||||
39, // [39:50] is the sub-list for method input_type
|
|
||||||
39, // [39:39] is the sub-list for extension type_name
|
39, // [39:39] is the sub-list for extension type_name
|
||||||
39, // [39:39] is the sub-list for extension extendee
|
39, // [39:39] is the sub-list for extension extendee
|
||||||
0, // [0:39] is the sub-list for field type_name
|
0, // [0:39] is the sub-list for field type_name
|
||||||
|
@ -297,7 +297,6 @@ message Timing {
|
|||||||
|
|
||||||
service Agent {
|
service Agent {
|
||||||
rpc GetManifest(GetManifestRequest) returns (Manifest);
|
rpc GetManifest(GetManifestRequest) returns (Manifest);
|
||||||
rpc StreamManifests(GetManifestRequest) returns (stream Manifest);
|
|
||||||
rpc GetServiceBanner(GetServiceBannerRequest) returns (ServiceBanner);
|
rpc GetServiceBanner(GetServiceBannerRequest) returns (ServiceBanner);
|
||||||
rpc UpdateStats(UpdateStatsRequest) returns (UpdateStatsResponse);
|
rpc UpdateStats(UpdateStatsRequest) returns (UpdateStatsResponse);
|
||||||
rpc UpdateLifecycle(UpdateLifecycleRequest) returns (Lifecycle);
|
rpc UpdateLifecycle(UpdateLifecycleRequest) returns (Lifecycle);
|
||||||
|
@ -39,7 +39,6 @@ type DRPCAgentClient interface {
|
|||||||
DRPCConn() drpc.Conn
|
DRPCConn() drpc.Conn
|
||||||
|
|
||||||
GetManifest(ctx context.Context, in *GetManifestRequest) (*Manifest, error)
|
GetManifest(ctx context.Context, in *GetManifestRequest) (*Manifest, error)
|
||||||
StreamManifests(ctx context.Context, in *GetManifestRequest) (DRPCAgent_StreamManifestsClient, error)
|
|
||||||
GetServiceBanner(ctx context.Context, in *GetServiceBannerRequest) (*ServiceBanner, error)
|
GetServiceBanner(ctx context.Context, in *GetServiceBannerRequest) (*ServiceBanner, error)
|
||||||
UpdateStats(ctx context.Context, in *UpdateStatsRequest) (*UpdateStatsResponse, error)
|
UpdateStats(ctx context.Context, in *UpdateStatsRequest) (*UpdateStatsResponse, error)
|
||||||
UpdateLifecycle(ctx context.Context, in *UpdateLifecycleRequest) (*Lifecycle, error)
|
UpdateLifecycle(ctx context.Context, in *UpdateLifecycleRequest) (*Lifecycle, error)
|
||||||
@ -70,46 +69,6 @@ func (c *drpcAgentClient) GetManifest(ctx context.Context, in *GetManifestReques
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *drpcAgentClient) StreamManifests(ctx context.Context, in *GetManifestRequest) (DRPCAgent_StreamManifestsClient, error) {
|
|
||||||
stream, err := c.cc.NewStream(ctx, "/coder.agent.v2.Agent/StreamManifests", drpcEncoding_File_agent_proto_agent_proto{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
x := &drpcAgent_StreamManifestsClient{stream}
|
|
||||||
if err := x.MsgSend(in, drpcEncoding_File_agent_proto_agent_proto{}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := x.CloseSend(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DRPCAgent_StreamManifestsClient interface {
|
|
||||||
drpc.Stream
|
|
||||||
Recv() (*Manifest, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type drpcAgent_StreamManifestsClient struct {
|
|
||||||
drpc.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *drpcAgent_StreamManifestsClient) GetStream() drpc.Stream {
|
|
||||||
return x.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *drpcAgent_StreamManifestsClient) Recv() (*Manifest, error) {
|
|
||||||
m := new(Manifest)
|
|
||||||
if err := x.MsgRecv(m, drpcEncoding_File_agent_proto_agent_proto{}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *drpcAgent_StreamManifestsClient) RecvMsg(m *Manifest) error {
|
|
||||||
return x.MsgRecv(m, drpcEncoding_File_agent_proto_agent_proto{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *drpcAgentClient) GetServiceBanner(ctx context.Context, in *GetServiceBannerRequest) (*ServiceBanner, error) {
|
func (c *drpcAgentClient) GetServiceBanner(ctx context.Context, in *GetServiceBannerRequest) (*ServiceBanner, error) {
|
||||||
out := new(ServiceBanner)
|
out := new(ServiceBanner)
|
||||||
err := c.cc.Invoke(ctx, "/coder.agent.v2.Agent/GetServiceBanner", drpcEncoding_File_agent_proto_agent_proto{}, in, out)
|
err := c.cc.Invoke(ctx, "/coder.agent.v2.Agent/GetServiceBanner", drpcEncoding_File_agent_proto_agent_proto{}, in, out)
|
||||||
@ -193,7 +152,6 @@ func (c *drpcAgentClient) ScriptCompleted(ctx context.Context, in *WorkspaceAgen
|
|||||||
|
|
||||||
type DRPCAgentServer interface {
|
type DRPCAgentServer interface {
|
||||||
GetManifest(context.Context, *GetManifestRequest) (*Manifest, error)
|
GetManifest(context.Context, *GetManifestRequest) (*Manifest, error)
|
||||||
StreamManifests(*GetManifestRequest, DRPCAgent_StreamManifestsStream) error
|
|
||||||
GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error)
|
GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error)
|
||||||
UpdateStats(context.Context, *UpdateStatsRequest) (*UpdateStatsResponse, error)
|
UpdateStats(context.Context, *UpdateStatsRequest) (*UpdateStatsResponse, error)
|
||||||
UpdateLifecycle(context.Context, *UpdateLifecycleRequest) (*Lifecycle, error)
|
UpdateLifecycle(context.Context, *UpdateLifecycleRequest) (*Lifecycle, error)
|
||||||
@ -211,10 +169,6 @@ func (s *DRPCAgentUnimplementedServer) GetManifest(context.Context, *GetManifest
|
|||||||
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DRPCAgentUnimplementedServer) StreamManifests(*GetManifestRequest, DRPCAgent_StreamManifestsStream) error {
|
|
||||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DRPCAgentUnimplementedServer) GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error) {
|
func (s *DRPCAgentUnimplementedServer) GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error) {
|
||||||
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
}
|
}
|
||||||
@ -253,7 +207,7 @@ func (s *DRPCAgentUnimplementedServer) ScriptCompleted(context.Context, *Workspa
|
|||||||
|
|
||||||
type DRPCAgentDescription struct{}
|
type DRPCAgentDescription struct{}
|
||||||
|
|
||||||
func (DRPCAgentDescription) NumMethods() int { return 11 }
|
func (DRPCAgentDescription) NumMethods() int { return 10 }
|
||||||
|
|
||||||
func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
||||||
switch n {
|
switch n {
|
||||||
@ -267,15 +221,6 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
)
|
)
|
||||||
}, DRPCAgentServer.GetManifest, true
|
}, DRPCAgentServer.GetManifest, true
|
||||||
case 1:
|
case 1:
|
||||||
return "/coder.agent.v2.Agent/StreamManifests", drpcEncoding_File_agent_proto_agent_proto{},
|
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
|
||||||
return nil, srv.(DRPCAgentServer).
|
|
||||||
StreamManifests(
|
|
||||||
in1.(*GetManifestRequest),
|
|
||||||
&drpcAgent_StreamManifestsStream{in2.(drpc.Stream)},
|
|
||||||
)
|
|
||||||
}, DRPCAgentServer.StreamManifests, true
|
|
||||||
case 2:
|
|
||||||
return "/coder.agent.v2.Agent/GetServiceBanner", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/GetServiceBanner", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -284,7 +229,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*GetServiceBannerRequest),
|
in1.(*GetServiceBannerRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.GetServiceBanner, true
|
}, DRPCAgentServer.GetServiceBanner, true
|
||||||
case 3:
|
case 2:
|
||||||
return "/coder.agent.v2.Agent/UpdateStats", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/UpdateStats", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -293,7 +238,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*UpdateStatsRequest),
|
in1.(*UpdateStatsRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.UpdateStats, true
|
}, DRPCAgentServer.UpdateStats, true
|
||||||
case 4:
|
case 3:
|
||||||
return "/coder.agent.v2.Agent/UpdateLifecycle", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/UpdateLifecycle", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -302,7 +247,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*UpdateLifecycleRequest),
|
in1.(*UpdateLifecycleRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.UpdateLifecycle, true
|
}, DRPCAgentServer.UpdateLifecycle, true
|
||||||
case 5:
|
case 4:
|
||||||
return "/coder.agent.v2.Agent/BatchUpdateAppHealths", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/BatchUpdateAppHealths", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -311,7 +256,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*BatchUpdateAppHealthRequest),
|
in1.(*BatchUpdateAppHealthRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.BatchUpdateAppHealths, true
|
}, DRPCAgentServer.BatchUpdateAppHealths, true
|
||||||
case 6:
|
case 5:
|
||||||
return "/coder.agent.v2.Agent/UpdateStartup", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/UpdateStartup", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -320,7 +265,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*UpdateStartupRequest),
|
in1.(*UpdateStartupRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.UpdateStartup, true
|
}, DRPCAgentServer.UpdateStartup, true
|
||||||
case 7:
|
case 6:
|
||||||
return "/coder.agent.v2.Agent/BatchUpdateMetadata", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/BatchUpdateMetadata", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -329,7 +274,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*BatchUpdateMetadataRequest),
|
in1.(*BatchUpdateMetadataRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.BatchUpdateMetadata, true
|
}, DRPCAgentServer.BatchUpdateMetadata, true
|
||||||
case 8:
|
case 7:
|
||||||
return "/coder.agent.v2.Agent/BatchCreateLogs", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/BatchCreateLogs", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -338,7 +283,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*BatchCreateLogsRequest),
|
in1.(*BatchCreateLogsRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.BatchCreateLogs, true
|
}, DRPCAgentServer.BatchCreateLogs, true
|
||||||
case 9:
|
case 8:
|
||||||
return "/coder.agent.v2.Agent/GetAnnouncementBanners", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/GetAnnouncementBanners", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -347,7 +292,7 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
|
|||||||
in1.(*GetAnnouncementBannersRequest),
|
in1.(*GetAnnouncementBannersRequest),
|
||||||
)
|
)
|
||||||
}, DRPCAgentServer.GetAnnouncementBanners, true
|
}, DRPCAgentServer.GetAnnouncementBanners, true
|
||||||
case 10:
|
case 9:
|
||||||
return "/coder.agent.v2.Agent/ScriptCompleted", drpcEncoding_File_agent_proto_agent_proto{},
|
return "/coder.agent.v2.Agent/ScriptCompleted", drpcEncoding_File_agent_proto_agent_proto{},
|
||||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return srv.(DRPCAgentServer).
|
return srv.(DRPCAgentServer).
|
||||||
@ -381,19 +326,6 @@ func (x *drpcAgent_GetManifestStream) SendAndClose(m *Manifest) error {
|
|||||||
return x.CloseSend()
|
return x.CloseSend()
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCAgent_StreamManifestsStream interface {
|
|
||||||
drpc.Stream
|
|
||||||
Send(*Manifest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type drpcAgent_StreamManifestsStream struct {
|
|
||||||
drpc.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *drpcAgent_StreamManifestsStream) Send(m *Manifest) error {
|
|
||||||
return x.MsgSend(m, drpcEncoding_File_agent_proto_agent_proto{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type DRPCAgent_GetServiceBannerStream interface {
|
type DRPCAgent_GetServiceBannerStream interface {
|
||||||
drpc.Stream
|
drpc.Stream
|
||||||
SendAndClose(*ServiceBanner) error
|
SendAndClose(*ServiceBanner) error
|
||||||
|
@ -40,11 +40,3 @@ type DRPCAgentClient23 interface {
|
|||||||
DRPCAgentClient22
|
DRPCAgentClient22
|
||||||
ScriptCompleted(ctx context.Context, in *WorkspaceAgentScriptCompletedRequest) (*WorkspaceAgentScriptCompletedResponse, error)
|
ScriptCompleted(ctx context.Context, in *WorkspaceAgentScriptCompletedRequest) (*WorkspaceAgentScriptCompletedResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DRPCAgentClient24 is the Agent API at v2.4. It adds the StreamManifests RPC. Compatible with
|
|
||||||
// Coder v2.20+
|
|
||||||
//
|
|
||||||
type DRPCAgentClient24 interface {
|
|
||||||
DRPCAgentClient23
|
|
||||||
StreamManifests(ctx context.Context, in *GetManifestRequest) (DRPCAgent_StreamManifestsClient, error)
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package reaper
|
package reaper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -29,6 +30,10 @@ func catchSignals(pid int, sigs []os.Signal) {
|
|||||||
s := <-sc
|
s := <-sc
|
||||||
sig, ok := s.(syscall.Signal)
|
sig, ok := s.(syscall.Signal)
|
||||||
if ok {
|
if ok {
|
||||||
|
// TODO:
|
||||||
|
// Tried using a logger here but the I/O streams are already closed at this point...
|
||||||
|
// Why is os.Stderr still working then?
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "reaper caught %q signal, killing process %v\n", sig.String(), pid)
|
||||||
_ = syscall.Kill(pid, sig)
|
_ = syscall.Kill(pid, sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
118
cli/agent.go
118
cli/agent.go
@ -2,6 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"cloud.google.com/go/compute/metadata"
|
"cloud.google.com/go/compute/metadata"
|
||||||
|
"github.com/coder/retry"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
@ -24,6 +26,8 @@ import (
|
|||||||
"cdr.dev/slog/sloggers/sloghuman"
|
"cdr.dev/slog/sloggers/sloghuman"
|
||||||
"cdr.dev/slog/sloggers/slogjson"
|
"cdr.dev/slog/sloggers/slogjson"
|
||||||
"cdr.dev/slog/sloggers/slogstackdriver"
|
"cdr.dev/slog/sloggers/slogstackdriver"
|
||||||
|
"github.com/coder/serpent"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/agent"
|
"github.com/coder/coder/v2/agent"
|
||||||
"github.com/coder/coder/v2/agent/agentcontainers"
|
"github.com/coder/coder/v2/agent/agentcontainers"
|
||||||
"github.com/coder/coder/v2/agent/agentexec"
|
"github.com/coder/coder/v2/agent/agentexec"
|
||||||
@ -33,7 +37,6 @@ import (
|
|||||||
"github.com/coder/coder/v2/cli/clilog"
|
"github.com/coder/coder/v2/cli/clilog"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
"github.com/coder/serpent"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RootCmd) workspaceAgent() *serpent.Command {
|
func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||||
@ -61,8 +64,11 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
|||||||
// This command isn't useful to manually execute.
|
// This command isn't useful to manually execute.
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
Handler: func(inv *serpent.Invocation) error {
|
Handler: func(inv *serpent.Invocation) error {
|
||||||
ctx, cancel := context.WithCancel(inv.Context())
|
ctx, cancel := context.WithCancelCause(inv.Context())
|
||||||
defer cancel()
|
defer func() {
|
||||||
|
fmt.Printf(">>>>>CANCELING CONTEXT")
|
||||||
|
cancel(errors.New("defer"))
|
||||||
|
}()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ignorePorts = map[int]string{}
|
ignorePorts = map[int]string{}
|
||||||
@ -278,7 +284,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
|||||||
return xerrors.Errorf("add executable to $PATH: %w", err)
|
return xerrors.Errorf("add executable to $PATH: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
prometheusRegistry := prometheus.NewRegistry()
|
|
||||||
subsystemsRaw := inv.Environ.Get(agent.EnvAgentSubsystem)
|
subsystemsRaw := inv.Environ.Get(agent.EnvAgentSubsystem)
|
||||||
subsystems := []codersdk.AgentSubsystem{}
|
subsystems := []codersdk.AgentSubsystem{}
|
||||||
for _, s := range strings.Split(subsystemsRaw, ",") {
|
for _, s := range strings.Split(subsystemsRaw, ",") {
|
||||||
@ -325,43 +330,88 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
|||||||
containerLister = agentcontainers.NewDocker(execer)
|
containerLister = agentcontainers.NewDocker(execer)
|
||||||
}
|
}
|
||||||
|
|
||||||
agnt := agent.New(agent.Options{
|
// TODO: timeout ok?
|
||||||
Client: client,
|
reinitCtx, reinitCancel := context.WithTimeout(context.Background(), time.Hour*24)
|
||||||
Logger: logger,
|
defer reinitCancel()
|
||||||
LogDir: logDir,
|
reinitEvents := make(chan agentsdk.ReinitializationResponse)
|
||||||
ScriptDataDir: scriptDataDir,
|
|
||||||
TailnetListenPort: uint16(tailnetListenPort),
|
go func() {
|
||||||
ExchangeToken: func(ctx context.Context) (string, error) {
|
// Retry to wait for reinit, main context cancels the retrier.
|
||||||
if exchangeToken == nil {
|
for retrier := retry.New(100*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
|
||||||
return client.SDK.SessionToken(), nil
|
select {
|
||||||
|
case <-reinitCtx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
resp, err := exchangeToken(ctx)
|
|
||||||
|
err := client.WaitForReinit(reinitCtx, reinitEvents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
logger.Error(ctx, "failed to wait for reinit instructions, will retry", slog.Error(err))
|
||||||
}
|
}
|
||||||
client.SetSessionToken(resp.SessionToken)
|
}
|
||||||
return resp.SessionToken, nil
|
}()
|
||||||
},
|
|
||||||
EnvironmentVariables: environmentVariables,
|
|
||||||
IgnorePorts: ignorePorts,
|
|
||||||
SSHMaxTimeout: sshMaxTimeout,
|
|
||||||
Subsystems: subsystems,
|
|
||||||
|
|
||||||
PrometheusRegistry: prometheusRegistry,
|
var (
|
||||||
BlockFileTransfer: blockFileTransfer,
|
lastErr error
|
||||||
Execer: execer,
|
mustExit bool
|
||||||
ContainerLister: containerLister,
|
)
|
||||||
})
|
for {
|
||||||
|
prometheusRegistry := prometheus.NewRegistry()
|
||||||
|
|
||||||
promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
|
agnt := agent.New(agent.Options{
|
||||||
prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
|
Client: client,
|
||||||
defer prometheusSrvClose()
|
Logger: logger,
|
||||||
|
LogDir: logDir,
|
||||||
|
ScriptDataDir: scriptDataDir,
|
||||||
|
TailnetListenPort: uint16(tailnetListenPort),
|
||||||
|
ExchangeToken: func(ctx context.Context) (string, error) {
|
||||||
|
if exchangeToken == nil {
|
||||||
|
return client.SDK.SessionToken(), nil
|
||||||
|
}
|
||||||
|
resp, err := exchangeToken(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
client.SetSessionToken(resp.SessionToken)
|
||||||
|
return resp.SessionToken, nil
|
||||||
|
},
|
||||||
|
EnvironmentVariables: environmentVariables,
|
||||||
|
IgnorePorts: ignorePorts,
|
||||||
|
SSHMaxTimeout: sshMaxTimeout,
|
||||||
|
Subsystems: subsystems,
|
||||||
|
|
||||||
debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
|
PrometheusRegistry: prometheusRegistry,
|
||||||
defer debugSrvClose()
|
BlockFileTransfer: blockFileTransfer,
|
||||||
|
Execer: execer,
|
||||||
|
ContainerLister: containerLister,
|
||||||
|
})
|
||||||
|
|
||||||
<-ctx.Done()
|
promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
|
||||||
return agnt.Close()
|
prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
|
||||||
|
|
||||||
|
debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Warn(ctx, "agent shutting down", slog.Error(ctx.Err()), slog.F("cause", context.Cause(ctx)))
|
||||||
|
mustExit = true
|
||||||
|
case event := <-reinitEvents:
|
||||||
|
logger.Warn(ctx, "agent received instruction to reinitialize",
|
||||||
|
slog.F("message", event.Message), slog.F("reason", event.Reason))
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = agnt.Close()
|
||||||
|
debugSrvClose()
|
||||||
|
prometheusSrvClose()
|
||||||
|
|
||||||
|
if mustExit {
|
||||||
|
reinitCancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(ctx, "reinitializing...")
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package agentapi
|
package agentapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cdr.dev/slog"
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
|
||||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@ -41,61 +42,6 @@ type ManifestAPI struct {
|
|||||||
Log slog.Logger
|
Log slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ManifestAPI) StreamManifests(in *agentproto.GetManifestRequest, stream agentproto.DRPCAgent_StreamManifestsStream) error {
|
|
||||||
streamCtx := dbauthz.AsSystemRestricted(stream.Context()) // TODO:
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := stream.CloseSend(); err != nil {
|
|
||||||
a.Log.Error(streamCtx, "error closing stream: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
updates := make(chan struct{}, 1)
|
|
||||||
|
|
||||||
unsub, err := a.Pubsub.Subscribe(ManifestUpdateChannel(a.WorkspaceID), func(ctx context.Context, _ []byte) {
|
|
||||||
a.Log.Info(ctx, "received 'prebuild claimed' event for workspace, pushing down new manifest", slog.F("workspace_id", a.WorkspaceID.String()))
|
|
||||||
select {
|
|
||||||
case <-streamCtx.Done():
|
|
||||||
return
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case updates <- struct{}{}:
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("subscribe to 'prebuild claimed' event: %w", err)
|
|
||||||
}
|
|
||||||
defer unsub()
|
|
||||||
|
|
||||||
for {
|
|
||||||
manifest, err := a.GetManifest(streamCtx, in)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("receive manifest: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Log.Debug(streamCtx, "pushing manifest to workspace", slog.F("workspace_id", a.WorkspaceID))
|
|
||||||
|
|
||||||
// Send first retrieved manifest.
|
|
||||||
err = stream.Send(manifest)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("send manifest: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...then wait until triggered by prebuild claim completion.
|
|
||||||
// At this stage, a prebuild will have been claimed by a user and the agent will need to be reconfigured.
|
|
||||||
select {
|
|
||||||
case <-updates:
|
|
||||||
a.Log.Info(streamCtx, "received manifest update request", slog.F("workspace_id", a.WorkspaceID))
|
|
||||||
case <-streamCtx.Done():
|
|
||||||
return xerrors.Errorf("stream close: %w", streamCtx.Err())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ManifestUpdateChannel(id uuid.UUID) string {
|
|
||||||
return fmt.Sprintf("prebuild_claimed_%s", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifestRequest) (*agentproto.Manifest, error) {
|
func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifestRequest) (*agentproto.Manifest, error) {
|
||||||
workspaceAgent, err := a.AgentFn(ctx)
|
workspaceAgent, err := a.AgentFn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1193,6 +1193,7 @@ func New(options *Options) *API {
|
|||||||
r.Get("/external-auth", api.workspaceAgentsExternalAuth)
|
r.Get("/external-auth", api.workspaceAgentsExternalAuth)
|
||||||
r.Get("/gitsshkey", api.agentGitSSHKey)
|
r.Get("/gitsshkey", api.agentGitSSHKey)
|
||||||
r.Post("/log-source", api.workspaceAgentPostLogSource)
|
r.Post("/log-source", api.workspaceAgentPostLogSource)
|
||||||
|
r.Get("/reinit", api.workspaceAgentReinit)
|
||||||
})
|
})
|
||||||
r.Route("/{workspaceagent}", func(r chi.Router) {
|
r.Route("/{workspaceagent}", func(r chi.Router) {
|
||||||
r.Use(
|
r.Use(
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/agentapi"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sqlc-dev/pqtype"
|
"github.com/sqlc-dev/pqtype"
|
||||||
@ -1713,11 +1713,13 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
|||||||
|
|
||||||
// If this job was initiated by the prebuilds user and the job is not a prebuild, then it MUST be the claim run.
|
// If this job was initiated by the prebuilds user and the job is not a prebuild, then it MUST be the claim run.
|
||||||
// TODO: maybe add some specific metadata to indicate this rather than imputing it.
|
// TODO: maybe add some specific metadata to indicate this rather than imputing it.
|
||||||
if input.IsPrebuildClaimByUser != uuid.Nil {
|
if input.PrebuildClaimByUser != uuid.Nil {
|
||||||
|
channel := agentsdk.PrebuildClaimedChannel(workspace.ID)
|
||||||
s.Logger.Info(ctx, "workspace prebuild successfully claimed by user",
|
s.Logger.Info(ctx, "workspace prebuild successfully claimed by user",
|
||||||
slog.F("user", input.IsPrebuildClaimByUser.String()),
|
slog.F("user", input.PrebuildClaimByUser.String()),
|
||||||
slog.F("workspace_id", workspace.ID))
|
slog.F("workspace_id", workspace.ID),
|
||||||
if err := s.Pubsub.Publish(agentapi.ManifestUpdateChannel(workspace.ID), nil); err != nil {
|
slog.F("channel", channel))
|
||||||
|
if err := s.Pubsub.Publish(channel, []byte(input.PrebuildClaimByUser.String())); err != nil {
|
||||||
s.Logger.Error(ctx, "failed to publish message to workspace agent to pull new manifest", slog.Error(err))
|
s.Logger.Error(ctx, "failed to publish message to workspace agent to pull new manifest", slog.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2381,10 +2383,10 @@ type TemplateVersionImportJob struct {
|
|||||||
|
|
||||||
// WorkspaceProvisionJob is the payload for the "workspace_provision" job type.
|
// WorkspaceProvisionJob is the payload for the "workspace_provision" job type.
|
||||||
type WorkspaceProvisionJob struct {
|
type WorkspaceProvisionJob struct {
|
||||||
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
|
WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
|
||||||
DryRun bool `json:"dry_run"`
|
DryRun bool `json:"dry_run"`
|
||||||
IsPrebuild bool `json:"is_prebuild,omitempty"`
|
IsPrebuild bool `json:"is_prebuild,omitempty"`
|
||||||
IsPrebuildClaimByUser uuid.UUID `json:"is_prebuild_claim_by,omitempty"`
|
PrebuildClaimByUser uuid.UUID `json:"prebuild_claim_by,omitempty"`
|
||||||
// RunningWorkspaceAgentID is *only* used for prebuilds. We pass it down when we want to rebuild a prebuilt workspace
|
// RunningWorkspaceAgentID is *only* used for prebuilds. We pass it down when we want to rebuild a prebuilt workspace
|
||||||
// but not generate a new agent token. The provisionerdserver will retrieve this token and push it down to
|
// but not generate a new agent token. The provisionerdserver will retrieve this token and push it down to
|
||||||
// the provisioner (and ultimately to the `coder_agent` resource in the Terraform provider) where it will be
|
// the provisioner (and ultimately to the `coder_agent` resource in the Terraform provider) where it will be
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
"github.com/coder/websocket"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/agentapi"
|
"github.com/coder/coder/v2/coderd/agentapi"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||||
@ -42,7 +44,6 @@ import (
|
|||||||
"github.com/coder/coder/v2/codersdk/wsjson"
|
"github.com/coder/coder/v2/codersdk/wsjson"
|
||||||
"github.com/coder/coder/v2/tailnet"
|
"github.com/coder/coder/v2/tailnet"
|
||||||
"github.com/coder/coder/v2/tailnet/proto"
|
"github.com/coder/coder/v2/tailnet/proto"
|
||||||
"github.com/coder/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Summary Get workspace agent by ID
|
// @Summary Get workspace agent by ID
|
||||||
@ -1046,6 +1047,105 @@ func (api *API) workspaceAgentPostLogSource(rw http.ResponseWriter, r *http.Requ
|
|||||||
httpapi.Write(ctx, rw, http.StatusCreated, apiSource)
|
httpapi.Write(ctx, rw, http.StatusCreated, apiSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @Summary Post workspace agent log source
|
||||||
|
// TODO @ID post-workspace-agent-log-source
|
||||||
|
// TODO @Security CoderSessionToken
|
||||||
|
// TODO @Accept json
|
||||||
|
// TODO @Produce json
|
||||||
|
// TODO @Tags Agents
|
||||||
|
// TODO @Param request body agentsdk.PostLogSourceRequest true "Log source request"
|
||||||
|
// TODO @Success 200 {object} codersdk.WorkspaceAgentLogSource
|
||||||
|
// TODO @Router /workspaceagents/me/log-source [post]
|
||||||
|
func (api *API) workspaceAgentReinit(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// Allow us to interrupt watch via cancel.
|
||||||
|
ctx, cancel := context.WithCancel(r.Context())
|
||||||
|
defer cancel()
|
||||||
|
r = r.WithContext(ctx) // Rewire context for SSE cancellation.
|
||||||
|
|
||||||
|
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||||
|
log := api.Logger.Named("workspace_agent_reinit_watcher").With(
|
||||||
|
slog.F("workspace_agent_id", workspaceAgent.ID),
|
||||||
|
)
|
||||||
|
|
||||||
|
workspace, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(ctx, "failed to retrieve workspace from agent token", slog.Error(err))
|
||||||
|
httpapi.InternalServerError(rw, errors.New("failed to determine workspace from agent token"))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(ctx, "agent waiting for reinit instruction")
|
||||||
|
|
||||||
|
prebuildClaims := make(chan uuid.UUID, 1)
|
||||||
|
cancelSub, err := api.Pubsub.Subscribe(agentsdk.PrebuildClaimedChannel(workspace.ID), func(inner context.Context, id []byte) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-inner.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := uuid.ParseBytes(id)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(ctx, "invalid prebuild claimed channel payload", slog.F("input", string(id)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prebuildClaims <- parsed
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(ctx, "failed to subscribe to prebuild claimed channel", slog.Error(err))
|
||||||
|
httpapi.InternalServerError(rw, errors.New("failed to subscribe to prebuild claimed channel"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer cancelSub()
|
||||||
|
|
||||||
|
sseSendEvent, sseSenderClosed, err := httpapi.ServerSentEventSender(rw, r)
|
||||||
|
if err != nil {
|
||||||
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
|
Message: "Internal error setting up server-sent events.",
|
||||||
|
Detail: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Prevent handler from returning until the sender is closed.
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
<-sseSenderClosed
|
||||||
|
}()
|
||||||
|
// Synchronize cancellation from SSE -> context, this lets us simplify the
|
||||||
|
// cancellation logic.
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-sseSenderClosed:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// An initial ping signals to the request that the server is now ready
|
||||||
|
// and the client can begin servicing a channel with data.
|
||||||
|
_ = sseSendEvent(ctx, codersdk.ServerSentEvent{
|
||||||
|
Type: codersdk.ServerSentEventTypePing,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Expand with future use-cases for agent reinitialization.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case user := <-prebuildClaims:
|
||||||
|
err = sseSendEvent(ctx, codersdk.ServerSentEvent{
|
||||||
|
Type: codersdk.ServerSentEventTypeData,
|
||||||
|
Data: agentsdk.ReinitializationResponse{
|
||||||
|
Message: fmt.Sprintf("prebuild claimed by user: %s", user),
|
||||||
|
Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
log.Warn(ctx, "failed to send SSE response to trigger reinit", slog.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convertProvisionedApps converts applications that are in the middle of provisioning process.
|
// convertProvisionedApps converts applications that are in the middle of provisioning process.
|
||||||
// It means that they may not have an agent or workspace assigned (dry-run job).
|
// It means that they may not have an agent or workspace assigned (dry-run job).
|
||||||
func convertProvisionedApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
|
func convertProvisionedApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
|
||||||
|
@ -319,7 +319,7 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object
|
|||||||
WorkspaceBuildID: workspaceBuildID,
|
WorkspaceBuildID: workspaceBuildID,
|
||||||
LogLevel: b.logLevel,
|
LogLevel: b.logLevel,
|
||||||
IsPrebuild: b.prebuild,
|
IsPrebuild: b.prebuild,
|
||||||
IsPrebuildClaimByUser: b.prebuildClaimBy,
|
PrebuildClaimByUser: b.prebuildClaimBy,
|
||||||
RunningWorkspaceAgentID: b.runningWorkspaceAgentID,
|
RunningWorkspaceAgentID: b.runningWorkspaceAgentID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,12 +19,13 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
|
||||||
"cdr.dev/slog"
|
"cdr.dev/slog"
|
||||||
|
"github.com/coder/websocket"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/agent/proto"
|
"github.com/coder/coder/v2/agent/proto"
|
||||||
"github.com/coder/coder/v2/apiversion"
|
"github.com/coder/coder/v2/apiversion"
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
drpcsdk "github.com/coder/coder/v2/codersdk/drpc"
|
drpcsdk "github.com/coder/coder/v2/codersdk/drpc"
|
||||||
tailnetproto "github.com/coder/coder/v2/tailnet/proto"
|
tailnetproto "github.com/coder/coder/v2/tailnet/proto"
|
||||||
"github.com/coder/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalLogSourceID is the statically-defined ID of a log-source that
|
// ExternalLogSourceID is the statically-defined ID of a log-source that
|
||||||
@ -232,7 +233,7 @@ func (c *Client) ConnectRPC23(ctx context.Context) (
|
|||||||
// ConnectRPC24 returns a dRPC client to the Agent API v2.4. It is useful when you want to be
|
// ConnectRPC24 returns a dRPC client to the Agent API v2.4. It is useful when you want to be
|
||||||
// maximally compatible with Coderd Release Versions from 2.18+ // TODO update release
|
// maximally compatible with Coderd Release Versions from 2.18+ // TODO update release
|
||||||
func (c *Client) ConnectRPC24(ctx context.Context) (
|
func (c *Client) ConnectRPC24(ctx context.Context) (
|
||||||
proto.DRPCAgentClient24, tailnetproto.DRPCTailnetClient23, error,
|
proto.DRPCAgentClient23, tailnetproto.DRPCTailnetClient23, error,
|
||||||
) {
|
) {
|
||||||
conn, err := c.connectRPCVersion(ctx, apiversion.New(2, 4))
|
conn, err := c.connectRPCVersion(ctx, apiversion.New(2, 4))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -649,3 +650,72 @@ func LogsNotifyChannel(agentID uuid.UUID) string {
|
|||||||
type LogsNotifyMessage struct {
|
type LogsNotifyMessage struct {
|
||||||
CreatedAfter int64 `json:"created_after"`
|
CreatedAfter int64 `json:"created_after"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReinitializationReason string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReinitializeReasonPrebuildClaimed ReinitializationReason = "prebuild_claimed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReinitializationResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Reason ReinitializationReason `json:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe place this somewhere else?
|
||||||
|
func PrebuildClaimedChannel(id uuid.UUID) string {
|
||||||
|
return fmt.Sprintf("prebuild_claimed_%s", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForReinit polls a SSE endpoint, and receives an event back under the following conditions:
|
||||||
|
// - ping: ignored, keepalive
|
||||||
|
// - prebuild claimed: a prebuilt workspace is claimed, so the agent must reinitialize.
|
||||||
|
// NOTE: the caller is responsible for closing the events chan.
|
||||||
|
func (c *Client) WaitForReinit(ctx context.Context, events chan<- ReinitializationResponse) error {
|
||||||
|
// TODO: allow configuring httpclient
|
||||||
|
c.SDK.HTTPClient.Timeout = time.Hour * 24
|
||||||
|
|
||||||
|
res, err := c.SDK.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/me/reinit", nil)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("execute request: %w", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return codersdk.ReadBodyAsError(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextEvent := codersdk.ServerSentEventReader(ctx, res.Body)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
sse, err := nextEvent()
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to read server-sent event: %w", err)
|
||||||
|
}
|
||||||
|
// TODO: remove
|
||||||
|
fmt.Printf("RECEIVED SSE EVENT: %s\n", sse.Type)
|
||||||
|
if sse.Type != codersdk.ServerSentEventTypeData {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var reinitResp ReinitializationResponse
|
||||||
|
b, ok := sse.Data.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return xerrors.Errorf("expected data as []byte, got %T", sse.Data)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(b, &reinitResp)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("unmarshal reinit response: %w", err)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case events <- reinitResp:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user