mirror of https://github.com/coder/coder.git
feat: add agent metadata (#6614)
This commit is contained in:
parent
c191692751
commit
ca4fa81570
237
agent/agent.go
237
agent/agent.go
|
@ -2,12 +2,14 @@ package agent
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
@ -33,6 +35,7 @@ import (
|
|||
"go.uber.org/atomic"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/net/speedtest"
|
||||
"tailscale.com/tailcfg"
|
||||
|
@ -83,12 +86,13 @@ type Options struct {
|
|||
}
|
||||
|
||||
type Client interface {
|
||||
Metadata(ctx context.Context) (agentsdk.Metadata, error)
|
||||
Manifest(ctx context.Context) (agentsdk.Manifest, error)
|
||||
Listen(ctx context.Context) (net.Conn, error)
|
||||
ReportStats(ctx context.Context, log slog.Logger, statsChan <-chan *agentsdk.Stats, setInterval func(time.Duration)) (io.Closer, error)
|
||||
PostLifecycle(ctx context.Context, state agentsdk.PostLifecycleRequest) error
|
||||
PostAppHealth(ctx context.Context, req agentsdk.PostAppHealthsRequest) error
|
||||
PostStartup(ctx context.Context, req agentsdk.PostStartupRequest) error
|
||||
PostMetadata(ctx context.Context, key string, req agentsdk.PostMetadataRequest) error
|
||||
PatchStartupLogs(ctx context.Context, req agentsdk.PatchStartupLogs) error
|
||||
}
|
||||
|
||||
|
@ -156,8 +160,8 @@ type agent struct {
|
|||
closed chan struct{}
|
||||
|
||||
envVars map[string]string
|
||||
// metadata is atomic because values can change after reconnection.
|
||||
metadata atomic.Value
|
||||
// manifest is atomic because values can change after reconnection.
|
||||
manifest atomic.Pointer[agentsdk.Manifest]
|
||||
sessionToken atomic.Pointer[string]
|
||||
sshServer *ssh.Server
|
||||
sshMaxTimeout time.Duration
|
||||
|
@ -183,6 +187,7 @@ type agent struct {
|
|||
// failure, you'll want the agent to reconnect.
|
||||
func (a *agent) runLoop(ctx context.Context) {
|
||||
go a.reportLifecycleLoop(ctx)
|
||||
go a.reportMetadataLoop(ctx)
|
||||
|
||||
for retrier := retry.New(100*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
|
||||
a.logger.Info(ctx, "connecting to coderd")
|
||||
|
@ -205,6 +210,168 @@ func (a *agent) runLoop(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *agent) collectMetadata(ctx context.Context, md codersdk.WorkspaceAgentMetadataDescription) *codersdk.WorkspaceAgentMetadataResult {
|
||||
var out bytes.Buffer
|
||||
result := &codersdk.WorkspaceAgentMetadataResult{
|
||||
// CollectedAt is set here for testing purposes and overrode by
|
||||
// the server to the time the server received the result to protect
|
||||
// against clock skew.
|
||||
//
|
||||
// In the future, the server may accept the timestamp from the agent
|
||||
// if it is certain the clocks are in sync.
|
||||
CollectedAt: time.Now(),
|
||||
}
|
||||
cmd, err := a.createCommand(ctx, md.Script, nil)
|
||||
if err != nil {
|
||||
result.Error = err.Error()
|
||||
return result
|
||||
}
|
||||
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &out
|
||||
|
||||
// The error isn't mutually exclusive with useful output.
|
||||
err = cmd.Run()
|
||||
|
||||
const bufLimit = 10 << 10
|
||||
if out.Len() > bufLimit {
|
||||
err = errors.Join(
|
||||
err,
|
||||
xerrors.Errorf("output truncated from %v to %v bytes", out.Len(), bufLimit),
|
||||
)
|
||||
out.Truncate(bufLimit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
result.Error = err.Error()
|
||||
}
|
||||
result.Value = out.String()
|
||||
return result
|
||||
}
|
||||
|
||||
func adjustIntervalForTests(i int64) time.Duration {
|
||||
// In tests we want to set shorter intervals because engineers are
|
||||
// impatient.
|
||||
base := time.Second
|
||||
if flag.Lookup("test.v") != nil {
|
||||
base = time.Millisecond * 100
|
||||
}
|
||||
return time.Duration(i) * base
|
||||
}
|
||||
|
||||
type metadataResultAndKey struct {
|
||||
result *codersdk.WorkspaceAgentMetadataResult
|
||||
key string
|
||||
}
|
||||
|
||||
func (a *agent) reportMetadataLoop(ctx context.Context) {
|
||||
baseInterval := adjustIntervalForTests(1)
|
||||
|
||||
const metadataLimit = 128
|
||||
|
||||
var (
|
||||
baseTicker = time.NewTicker(baseInterval)
|
||||
lastCollectedAts = make(map[string]time.Time)
|
||||
metadataResults = make(chan metadataResultAndKey, metadataLimit)
|
||||
)
|
||||
defer baseTicker.Stop()
|
||||
|
||||
var flight singleflight.Group
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case mr := <-metadataResults:
|
||||
lastCollectedAts[mr.key] = mr.result.CollectedAt
|
||||
err := a.client.PostMetadata(ctx, mr.key, *mr.result)
|
||||
if err != nil {
|
||||
a.logger.Error(ctx, "report metadata", slog.Error(err))
|
||||
}
|
||||
case <-baseTicker.C:
|
||||
}
|
||||
|
||||
if len(metadataResults) > 0 {
|
||||
// The inner collection loop expects the channel is empty before spinning up
|
||||
// all the collection goroutines.
|
||||
a.logger.Debug(
|
||||
ctx, "metadata collection backpressured",
|
||||
slog.F("queue_len", len(metadataResults)),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
manifest := a.manifest.Load()
|
||||
if manifest == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(manifest.Metadata) > metadataLimit {
|
||||
a.logger.Error(
|
||||
ctx, "metadata limit exceeded",
|
||||
slog.F("limit", metadataLimit), slog.F("got", len(manifest.Metadata)),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// If the manifest changes (e.g. on agent reconnect) we need to
|
||||
// purge old cache values to prevent lastCollectedAt from growing
|
||||
// boundlessly.
|
||||
for key := range lastCollectedAts {
|
||||
if slices.IndexFunc(manifest.Metadata, func(md codersdk.WorkspaceAgentMetadataDescription) bool {
|
||||
return md.Key == key
|
||||
}) < 0 {
|
||||
delete(lastCollectedAts, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a goroutine for each metadata collection, and use a
|
||||
// channel to synchronize the results and avoid both messy
|
||||
// mutex logic and overloading the API.
|
||||
for _, md := range manifest.Metadata {
|
||||
collectedAt, ok := lastCollectedAts[md.Key]
|
||||
if ok {
|
||||
// If the interval is zero, we assume the user just wants
|
||||
// a single collection at startup, not a spinning loop.
|
||||
if md.Interval == 0 {
|
||||
continue
|
||||
}
|
||||
// The last collected value isn't quite stale yet, so we skip it.
|
||||
if collectedAt.Add(
|
||||
adjustIntervalForTests(md.Interval),
|
||||
).After(time.Now()) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
md := md
|
||||
// We send the result to the channel in the goroutine to avoid
|
||||
// sending the same result multiple times. So, we don't care about
|
||||
// the return values.
|
||||
flight.DoChan(md.Key, func() (interface{}, error) {
|
||||
timeout := md.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = md.Interval
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx,
|
||||
time.Duration(timeout)*time.Second,
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, nil
|
||||
case metadataResults <- metadataResultAndKey{
|
||||
key: md.Key,
|
||||
result: a.collectMetadata(ctx, md),
|
||||
}:
|
||||
}
|
||||
return 0, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reportLifecycleLoop reports the current lifecycle state once.
|
||||
// Only the latest state is reported, intermediate states may be
|
||||
// lost if the agent can't communicate with the API.
|
||||
|
@ -279,40 +446,40 @@ func (a *agent) run(ctx context.Context) error {
|
|||
}
|
||||
a.sessionToken.Store(&sessionToken)
|
||||
|
||||
metadata, err := a.client.Metadata(ctx)
|
||||
manifest, err := a.client.Manifest(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch metadata: %w", err)
|
||||
}
|
||||
a.logger.Info(ctx, "fetched metadata", slog.F("metadata", metadata))
|
||||
a.logger.Info(ctx, "fetched manifest", slog.F("manifest", manifest))
|
||||
|
||||
// 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.
|
||||
metadata.Directory, err = expandDirectory(metadata.Directory)
|
||||
manifest.Directory, err = expandDirectory(manifest.Directory)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expand directory: %w", err)
|
||||
}
|
||||
err = a.client.PostStartup(ctx, agentsdk.PostStartupRequest{
|
||||
Version: buildinfo.Version(),
|
||||
ExpandedDirectory: metadata.Directory,
|
||||
ExpandedDirectory: manifest.Directory,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update workspace agent version: %w", err)
|
||||
}
|
||||
|
||||
oldMetadata := a.metadata.Swap(metadata)
|
||||
oldManifest := a.manifest.Swap(&manifest)
|
||||
|
||||
// The startup script should only execute on the first run!
|
||||
if oldMetadata == nil {
|
||||
if oldManifest == nil {
|
||||
a.setLifecycle(ctx, 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 metadata.GitAuthConfigs > 0 {
|
||||
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.
|
||||
|
@ -327,7 +494,7 @@ func (a *agent) run(ctx context.Context) error {
|
|||
scriptStart := time.Now()
|
||||
err = a.trackConnGoroutine(func() {
|
||||
defer close(scriptDone)
|
||||
scriptDone <- a.runStartupScript(ctx, metadata.StartupScript)
|
||||
scriptDone <- a.runStartupScript(ctx, manifest.StartupScript)
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("track startup script: %w", err)
|
||||
|
@ -336,8 +503,8 @@ func (a *agent) run(ctx context.Context) error {
|
|||
var timeout <-chan time.Time
|
||||
// If timeout is zero, an older version of the coder
|
||||
// provider was used. Otherwise a timeout is always > 0.
|
||||
if metadata.StartupScriptTimeout > 0 {
|
||||
t := time.NewTimer(metadata.StartupScriptTimeout)
|
||||
if manifest.StartupScriptTimeout > 0 {
|
||||
t := time.NewTimer(manifest.StartupScriptTimeout)
|
||||
defer t.Stop()
|
||||
timeout = t.C
|
||||
}
|
||||
|
@ -354,7 +521,7 @@ func (a *agent) run(ctx context.Context) error {
|
|||
return
|
||||
}
|
||||
// Only log if there was a startup script.
|
||||
if metadata.StartupScript != "" {
|
||||
if manifest.StartupScript != "" {
|
||||
execTime := time.Since(scriptStart)
|
||||
if err != nil {
|
||||
a.logger.Warn(ctx, "startup script failed", slog.F("execution_time", execTime), slog.Error(err))
|
||||
|
@ -371,13 +538,13 @@ func (a *agent) run(ctx context.Context) error {
|
|||
appReporterCtx, appReporterCtxCancel := context.WithCancel(ctx)
|
||||
defer appReporterCtxCancel()
|
||||
go NewWorkspaceAppHealthReporter(
|
||||
a.logger, metadata.Apps, a.client.PostAppHealth)(appReporterCtx)
|
||||
a.logger, manifest.Apps, a.client.PostAppHealth)(appReporterCtx)
|
||||
|
||||
a.closeMutex.Lock()
|
||||
network := a.network
|
||||
a.closeMutex.Unlock()
|
||||
if network == nil {
|
||||
network, err = a.createTailnet(ctx, metadata.DERPMap)
|
||||
network, err = a.createTailnet(ctx, manifest.DERPMap)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create tailnet: %w", err)
|
||||
}
|
||||
|
@ -396,7 +563,7 @@ func (a *agent) run(ctx context.Context) error {
|
|||
a.startReportingConnectionStats(ctx)
|
||||
} else {
|
||||
// Update the DERP map!
|
||||
network.SetDERPMap(metadata.DERPMap)
|
||||
network.SetDERPMap(manifest.DERPMap)
|
||||
}
|
||||
|
||||
a.logger.Debug(ctx, "running tailnet connection coordinator")
|
||||
|
@ -926,9 +1093,9 @@ func (a *agent) init(ctx context.Context) {
|
|||
}
|
||||
|
||||
// createCommand processes raw command input with OpenSSH-like behavior.
|
||||
// If the rawCommand provided is empty, it will default to the users shell.
|
||||
// If the script provided is empty, it will default to the users shell.
|
||||
// This injects environment variables specified by the user at launch too.
|
||||
func (a *agent) createCommand(ctx context.Context, rawCommand string, env []string) (*exec.Cmd, error) {
|
||||
func (a *agent) createCommand(ctx context.Context, script string, env []string) (*exec.Cmd, error) {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get current user: %w", err)
|
||||
|
@ -940,14 +1107,10 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
|
|||
return nil, xerrors.Errorf("get user shell: %w", err)
|
||||
}
|
||||
|
||||
rawMetadata := a.metadata.Load()
|
||||
if rawMetadata == nil {
|
||||
manifest := a.manifest.Load()
|
||||
if manifest == nil {
|
||||
return nil, xerrors.Errorf("no metadata was provided")
|
||||
}
|
||||
metadata, valid := rawMetadata.(agentsdk.Metadata)
|
||||
if !valid {
|
||||
return nil, xerrors.Errorf("metadata is the wrong type: %T", metadata)
|
||||
}
|
||||
|
||||
// OpenSSH executes all commands with the users current shell.
|
||||
// We replicate that behavior for IDE support.
|
||||
|
@ -955,11 +1118,11 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
|
|||
if runtime.GOOS == "windows" {
|
||||
caller = "/c"
|
||||
}
|
||||
args := []string{caller, rawCommand}
|
||||
args := []string{caller, script}
|
||||
|
||||
// gliderlabs/ssh returns a command slice of zero
|
||||
// when a shell is requested.
|
||||
if len(rawCommand) == 0 {
|
||||
if len(script) == 0 {
|
||||
args = []string{}
|
||||
if runtime.GOOS != "windows" {
|
||||
// On Linux and macOS, we should start a login
|
||||
|
@ -969,7 +1132,7 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
|
|||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, shell, args...)
|
||||
cmd.Dir = metadata.Directory
|
||||
cmd.Dir = manifest.Directory
|
||||
|
||||
// If the metadata directory doesn't exist, we run the command
|
||||
// in the users home directory.
|
||||
|
@ -1010,14 +1173,14 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
|
|||
|
||||
// This adds the ports dialog to code-server that enables
|
||||
// proxying a port dynamically.
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("VSCODE_PROXY_URI=%s", metadata.VSCodePortProxyURI))
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("VSCODE_PROXY_URI=%s", manifest.VSCodePortProxyURI))
|
||||
|
||||
// Hide Coder message on code-server's "Getting Started" page
|
||||
cmd.Env = append(cmd.Env, "CS_DISABLE_GETTING_STARTED_OVERRIDE=true")
|
||||
|
||||
// Load environment variables passed via the agent.
|
||||
// These should override all variables we manually specify.
|
||||
for envKey, value := range metadata.EnvironmentVariables {
|
||||
for envKey, value := range manifest.EnvironmentVariables {
|
||||
// Expanding environment variables allows for customization
|
||||
// of the $PATH, among other variables. Customers can prepend
|
||||
// or append to the $PATH, so allowing expand is required!
|
||||
|
@ -1080,9 +1243,9 @@ func (a *agent) handleSSHSession(session ssh.Session) (retErr error) {
|
|||
session.DisablePTYEmulation()
|
||||
|
||||
if !isQuietLogin(session.RawCommand()) {
|
||||
metadata, ok := a.metadata.Load().(agentsdk.Metadata)
|
||||
if ok {
|
||||
err = showMOTD(session, metadata.MOTDFile)
|
||||
manifest := a.manifest.Load()
|
||||
if manifest != nil {
|
||||
err = showMOTD(session, manifest.MOTDFile)
|
||||
if err != nil {
|
||||
a.logger.Error(ctx, "show MOTD", slog.Error(err))
|
||||
}
|
||||
|
@ -1512,19 +1675,19 @@ func (a *agent) Close() error {
|
|||
a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleShuttingDown)
|
||||
|
||||
lifecycleState := codersdk.WorkspaceAgentLifecycleOff
|
||||
if metadata, ok := a.metadata.Load().(agentsdk.Metadata); ok && metadata.ShutdownScript != "" {
|
||||
if manifest := a.manifest.Load(); manifest != nil && manifest.ShutdownScript != "" {
|
||||
scriptDone := make(chan error, 1)
|
||||
scriptStart := time.Now()
|
||||
go func() {
|
||||
defer close(scriptDone)
|
||||
scriptDone <- a.runShutdownScript(ctx, metadata.ShutdownScript)
|
||||
scriptDone <- a.runShutdownScript(ctx, manifest.ShutdownScript)
|
||||
}()
|
||||
|
||||
var timeout <-chan time.Time
|
||||
// If timeout is zero, an older version of the coder
|
||||
// provider was used. Otherwise a timeout is always > 0.
|
||||
if metadata.ShutdownScriptTimeout > 0 {
|
||||
t := time.NewTimer(metadata.ShutdownScriptTimeout)
|
||||
if manifest.ShutdownScriptTimeout > 0 {
|
||||
t := time.NewTimer(manifest.ShutdownScriptTimeout)
|
||||
defer t.Stop()
|
||||
timeout = t.C
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/net/speedtest"
|
||||
"tailscale.com/tailcfg"
|
||||
|
@ -61,7 +62,7 @@ func TestAgent_Stats_SSH(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
|
@ -94,7 +95,7 @@ func TestAgent_Stats_ReconnectingPTY(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
|
||||
ptyConn, err := conn.ReconnectingPTY(ctx, uuid.New(), 128, 128, "/bin/bash")
|
||||
require.NoError(t, err)
|
||||
|
@ -124,7 +125,7 @@ func TestAgent_Stats_Magic(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -151,7 +152,7 @@ func TestAgent_Stats_Magic(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
//nolint:dogsled
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -186,7 +187,7 @@ func TestAgent_Stats_Magic(t *testing.T) {
|
|||
|
||||
func TestAgent_SessionExec(t *testing.T) {
|
||||
t.Parallel()
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
|
||||
command := "echo test"
|
||||
if runtime.GOOS == "windows" {
|
||||
|
@ -199,7 +200,7 @@ func TestAgent_SessionExec(t *testing.T) {
|
|||
|
||||
func TestAgent_GitSSH(t *testing.T) {
|
||||
t.Parallel()
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
command := "sh -c 'echo $GIT_SSH_COMMAND'"
|
||||
if runtime.GOOS == "windows" {
|
||||
command = "cmd.exe /c echo %GIT_SSH_COMMAND%"
|
||||
|
@ -219,7 +220,7 @@ func TestAgent_SessionTTYShell(t *testing.T) {
|
|||
// it seems like it could be either.
|
||||
t.Skip("ConPTY appears to be inconsistent on Windows.")
|
||||
}
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
command := "sh"
|
||||
if runtime.GOOS == "windows" {
|
||||
command = "cmd.exe"
|
||||
|
@ -242,7 +243,7 @@ func TestAgent_SessionTTYShell(t *testing.T) {
|
|||
|
||||
func TestAgent_SessionTTYExitCode(t *testing.T) {
|
||||
t.Parallel()
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
command := "areallynotrealcommand"
|
||||
err := session.RequestPty("xterm", 128, 128, ssh.TerminalModes{})
|
||||
require.NoError(t, err)
|
||||
|
@ -281,7 +282,7 @@ func TestAgent_Session_TTY_MOTD(t *testing.T) {
|
|||
// Set HOME so we can ensure no ~/.hushlogin is present.
|
||||
t.Setenv("HOME", tmpdir)
|
||||
|
||||
session := setupSSHSession(t, agentsdk.Metadata{
|
||||
session := setupSSHSession(t, agentsdk.Manifest{
|
||||
MOTDFile: name,
|
||||
})
|
||||
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{})
|
||||
|
@ -327,7 +328,7 @@ func TestAgent_Session_TTY_Hushlogin(t *testing.T) {
|
|||
// Set HOME so we can ensure ~/.hushlogin is present.
|
||||
t.Setenv("HOME", tmpdir)
|
||||
|
||||
session := setupSSHSession(t, agentsdk.Metadata{
|
||||
session := setupSSHSession(t, agentsdk.Manifest{
|
||||
MOTDFile: name,
|
||||
})
|
||||
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{})
|
||||
|
@ -357,7 +358,7 @@ func TestAgent_Session_TTY_FastCommandHasOutput(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -407,7 +408,7 @@ func TestAgent_Session_TTY_HugeOutputIsNotLost(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -706,7 +707,7 @@ func TestAgent_SFTP(t *testing.T) {
|
|||
home = "/" + strings.ReplaceAll(home, "\\", "/")
|
||||
}
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -738,7 +739,7 @@ func TestAgent_SCP(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
@ -757,7 +758,7 @@ func TestAgent_EnvironmentVariables(t *testing.T) {
|
|||
t.Parallel()
|
||||
key := "EXAMPLE"
|
||||
value := "value"
|
||||
session := setupSSHSession(t, agentsdk.Metadata{
|
||||
session := setupSSHSession(t, agentsdk.Manifest{
|
||||
EnvironmentVariables: map[string]string{
|
||||
key: value,
|
||||
},
|
||||
|
@ -774,7 +775,7 @@ func TestAgent_EnvironmentVariables(t *testing.T) {
|
|||
func TestAgent_EnvironmentVariableExpansion(t *testing.T) {
|
||||
t.Parallel()
|
||||
key := "EXAMPLE"
|
||||
session := setupSSHSession(t, agentsdk.Metadata{
|
||||
session := setupSSHSession(t, agentsdk.Manifest{
|
||||
EnvironmentVariables: map[string]string{
|
||||
key: "$SOMETHINGNOTSET",
|
||||
},
|
||||
|
@ -801,7 +802,7 @@ func TestAgent_CoderEnvVars(t *testing.T) {
|
|||
t.Run(key, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
command := "sh -c 'echo $" + key + "'"
|
||||
if runtime.GOOS == "windows" {
|
||||
command = "cmd.exe /c echo %" + key + "%"
|
||||
|
@ -824,7 +825,7 @@ func TestAgent_SSHConnectionEnvVars(t *testing.T) {
|
|||
t.Run(key, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
session := setupSSHSession(t, agentsdk.Metadata{})
|
||||
session := setupSSHSession(t, agentsdk.Manifest{})
|
||||
command := "sh -c 'echo $" + key + "'"
|
||||
if runtime.GOOS == "windows" {
|
||||
command = "cmd.exe /c echo %" + key + "%"
|
||||
|
@ -848,7 +849,7 @@ func TestAgent_StartupScript(t *testing.T) {
|
|||
client := &client{
|
||||
t: t,
|
||||
agentID: uuid.New(),
|
||||
metadata: agentsdk.Metadata{
|
||||
manifest: agentsdk.Manifest{
|
||||
StartupScript: command,
|
||||
DERPMap: &tailcfg.DERPMap{},
|
||||
},
|
||||
|
@ -879,7 +880,7 @@ func TestAgent_StartupScript(t *testing.T) {
|
|||
client := &client{
|
||||
t: t,
|
||||
agentID: uuid.New(),
|
||||
metadata: agentsdk.Metadata{
|
||||
manifest: agentsdk.Manifest{
|
||||
StartupScript: command,
|
||||
DERPMap: &tailcfg.DERPMap{},
|
||||
},
|
||||
|
@ -912,13 +913,125 @@ func TestAgent_StartupScript(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAgent_Metadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Once", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
script := "echo -n hello"
|
||||
if runtime.GOOS == "windows" {
|
||||
script = "powershell " + script
|
||||
}
|
||||
//nolint:dogsled
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
Metadata: []codersdk.WorkspaceAgentMetadataDescription{
|
||||
{
|
||||
Key: "greeting",
|
||||
Interval: 0,
|
||||
Script: script,
|
||||
},
|
||||
},
|
||||
}, 0)
|
||||
|
||||
var gotMd map[string]agentsdk.PostMetadataRequest
|
||||
require.Eventually(t, func() bool {
|
||||
gotMd = client.getMetadata()
|
||||
return len(gotMd) == 1
|
||||
}, testutil.WaitShort, testutil.IntervalMedium)
|
||||
|
||||
collectedAt := gotMd["greeting"].CollectedAt
|
||||
|
||||
require.Never(t, func() bool {
|
||||
gotMd = client.getMetadata()
|
||||
if len(gotMd) != 1 {
|
||||
panic("unexpected number of metadata")
|
||||
}
|
||||
return !gotMd["greeting"].CollectedAt.Equal(collectedAt)
|
||||
}, testutil.WaitShort, testutil.IntervalMedium)
|
||||
})
|
||||
|
||||
t.Run("Many", func(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Shell scripting in Windows is a pain, and we have already tested
|
||||
// that the OS logic works in the simpler "Once" test above.
|
||||
t.Skip()
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
const reportInterval = 2
|
||||
const intervalUnit = 100 * time.Millisecond
|
||||
var (
|
||||
greetingPath = filepath.Join(dir, "greeting")
|
||||
script = "echo hello | tee -a " + greetingPath
|
||||
)
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
Metadata: []codersdk.WorkspaceAgentMetadataDescription{
|
||||
{
|
||||
Key: "greeting",
|
||||
Interval: reportInterval,
|
||||
Script: script,
|
||||
},
|
||||
{
|
||||
Key: "bad",
|
||||
Interval: reportInterval,
|
||||
Script: "exit 1",
|
||||
},
|
||||
},
|
||||
}, 0)
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return len(client.getMetadata()) == 2
|
||||
}, testutil.WaitShort, testutil.IntervalMedium)
|
||||
|
||||
for start := time.Now(); time.Since(start) < testutil.WaitMedium; time.Sleep(testutil.IntervalMedium) {
|
||||
md := client.getMetadata()
|
||||
if len(md) != 2 {
|
||||
panic("unexpected number of metadata entries")
|
||||
}
|
||||
|
||||
require.Equal(t, "hello\n", md["greeting"].Value)
|
||||
require.Equal(t, "exit status 1", md["bad"].Error)
|
||||
|
||||
greetingByt, err := os.ReadFile(greetingPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var (
|
||||
numGreetings = bytes.Count(greetingByt, []byte("hello"))
|
||||
idealNumGreetings = time.Since(start) / (reportInterval * intervalUnit)
|
||||
// We allow a 50% error margin because the report loop may backlog
|
||||
// in CI and other toasters. In production, there is no hard
|
||||
// guarantee on timing either, and the frontend gives similar
|
||||
// wiggle room to the staleness of the value.
|
||||
upperBound = int(idealNumGreetings) + 1
|
||||
lowerBound = (int(idealNumGreetings) / 2)
|
||||
)
|
||||
|
||||
if idealNumGreetings < 50 {
|
||||
// There is an insufficient sample size.
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("numGreetings: %d, idealNumGreetings: %d", numGreetings, idealNumGreetings)
|
||||
// The report loop may slow down on load, but it should never, ever
|
||||
// speed up.
|
||||
if numGreetings > upperBound {
|
||||
t.Fatalf("too many greetings: %d > %d in %v", numGreetings, upperBound, time.Since(start))
|
||||
} else if numGreetings < lowerBound {
|
||||
t.Fatalf("too few greetings: %d < %d", numGreetings, lowerBound)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("StartTimeout", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "sleep 5",
|
||||
StartupScriptTimeout: time.Nanosecond,
|
||||
}, 0)
|
||||
|
@ -947,7 +1060,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
t.Run("StartError", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "false",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
}, 0)
|
||||
|
@ -976,7 +1089,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
t.Run("Ready", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
}, 0)
|
||||
|
@ -1005,7 +1118,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
t.Run("ShuttingDown", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "sleep 5",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
}, 0)
|
||||
|
@ -1043,7 +1156,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
t.Run("ShutdownTimeout", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "sleep 5",
|
||||
ShutdownScriptTimeout: time.Nanosecond,
|
||||
}, 0)
|
||||
|
@ -1090,7 +1203,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
t.Run("ShutdownError", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "false",
|
||||
ShutdownScriptTimeout: 30 * time.Second,
|
||||
}, 0)
|
||||
|
@ -1141,7 +1254,7 @@ func TestAgent_Lifecycle(t *testing.T) {
|
|||
client := &client{
|
||||
t: t,
|
||||
agentID: uuid.New(),
|
||||
metadata: agentsdk.Metadata{
|
||||
manifest: agentsdk.Manifest{
|
||||
DERPMap: tailnettest.RunDERPAndSTUN(t),
|
||||
StartupScript: "echo 1",
|
||||
ShutdownScript: "echo " + expected,
|
||||
|
@ -1194,7 +1307,7 @@ func TestAgent_Startup(t *testing.T) {
|
|||
t.Run("EmptyDirectory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "",
|
||||
|
@ -1208,7 +1321,7 @@ func TestAgent_Startup(t *testing.T) {
|
|||
t.Run("HomeDirectory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "~",
|
||||
|
@ -1224,7 +1337,7 @@ func TestAgent_Startup(t *testing.T) {
|
|||
t.Run("HomeEnvironmentVariable", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "$HOME",
|
||||
|
@ -1251,7 +1364,7 @@ func TestAgent_ReconnectingPTY(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
id := uuid.New()
|
||||
netConn, err := conn.ReconnectingPTY(ctx, id, 100, 100, "/bin/bash")
|
||||
require.NoError(t, err)
|
||||
|
@ -1353,7 +1466,7 @@ func TestAgent_Dial(t *testing.T) {
|
|||
}()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
require.True(t, conn.AwaitReachable(context.Background()))
|
||||
conn1, err := conn.DialContext(context.Background(), l.Addr().Network(), l.Addr().String())
|
||||
require.NoError(t, err)
|
||||
|
@ -1375,7 +1488,7 @@ func TestAgent_Speedtest(t *testing.T) {
|
|||
defer cancel()
|
||||
derpMap := tailnettest.RunDERPAndSTUN(t)
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
DERPMap: derpMap,
|
||||
}, 0)
|
||||
defer conn.Close()
|
||||
|
@ -1397,7 +1510,7 @@ func TestAgent_Reconnect(t *testing.T) {
|
|||
client := &client{
|
||||
t: t,
|
||||
agentID: agentID,
|
||||
metadata: agentsdk.Metadata{
|
||||
manifest: agentsdk.Manifest{
|
||||
DERPMap: derpMap,
|
||||
},
|
||||
statsChan: statsCh,
|
||||
|
@ -1432,7 +1545,7 @@ func TestAgent_WriteVSCodeConfigs(t *testing.T) {
|
|||
client := &client{
|
||||
t: t,
|
||||
agentID: uuid.New(),
|
||||
metadata: agentsdk.Metadata{
|
||||
manifest: agentsdk.Manifest{
|
||||
GitAuthConfigs: 1,
|
||||
DERPMap: &tailcfg.DERPMap{},
|
||||
},
|
||||
|
@ -1461,7 +1574,7 @@ func TestAgent_WriteVSCodeConfigs(t *testing.T) {
|
|||
|
||||
func setupSSHCommand(t *testing.T, beforeArgs []string, afterArgs []string) *exec.Cmd {
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
waitGroup := sync.WaitGroup{}
|
||||
|
@ -1504,7 +1617,7 @@ func setupSSHCommand(t *testing.T, beforeArgs []string, afterArgs []string) *exe
|
|||
return exec.Command("ssh", args...)
|
||||
}
|
||||
|
||||
func setupSSHSession(t *testing.T, options agentsdk.Metadata) *ssh.Session {
|
||||
func setupSSHSession(t *testing.T, options agentsdk.Manifest) *ssh.Session {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
//nolint:dogsled
|
||||
|
@ -1528,7 +1641,7 @@ func (c closeFunc) Close() error {
|
|||
return c()
|
||||
}
|
||||
|
||||
func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Duration) (
|
||||
func setupAgent(t *testing.T, metadata agentsdk.Manifest, ptyTimeout time.Duration) (
|
||||
*codersdk.WorkspaceAgentConn,
|
||||
*client,
|
||||
<-chan *agentsdk.Stats,
|
||||
|
@ -1548,7 +1661,7 @@ func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Durati
|
|||
c := &client{
|
||||
t: t,
|
||||
agentID: agentID,
|
||||
metadata: metadata,
|
||||
manifest: metadata,
|
||||
statsChan: statsCh,
|
||||
coordinator: coordinator,
|
||||
}
|
||||
|
@ -1631,7 +1744,8 @@ func assertWritePayload(t *testing.T, w io.Writer, payload []byte) {
|
|||
type client struct {
|
||||
t *testing.T
|
||||
agentID uuid.UUID
|
||||
metadata agentsdk.Metadata
|
||||
manifest agentsdk.Manifest
|
||||
metadata map[string]agentsdk.PostMetadataRequest
|
||||
statsChan chan *agentsdk.Stats
|
||||
coordinator tailnet.Coordinator
|
||||
lastWorkspaceAgent func()
|
||||
|
@ -1643,8 +1757,8 @@ type client struct {
|
|||
logs []agentsdk.StartupLog
|
||||
}
|
||||
|
||||
func (c *client) Metadata(_ context.Context) (agentsdk.Metadata, error) {
|
||||
return c.metadata, nil
|
||||
func (c *client) Manifest(_ context.Context) (agentsdk.Manifest, error) {
|
||||
return c.manifest, nil
|
||||
}
|
||||
|
||||
func (c *client) Listen(_ context.Context) (net.Conn, error) {
|
||||
|
@ -1718,6 +1832,22 @@ func (c *client) getStartup() agentsdk.PostStartupRequest {
|
|||
return c.startup
|
||||
}
|
||||
|
||||
func (c *client) getMetadata() map[string]agentsdk.PostMetadataRequest {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return maps.Clone(c.metadata)
|
||||
}
|
||||
|
||||
func (c *client) PostMetadata(_ context.Context, key string, req agentsdk.PostMetadataRequest) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.metadata == nil {
|
||||
c.metadata = make(map[string]agentsdk.PostMetadataRequest)
|
||||
}
|
||||
c.metadata[key] = req
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) PostStartup(_ context.Context, startup agentsdk.PostStartupRequest) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
|
|
@ -4263,7 +4263,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata": {
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
|
@ -4276,18 +4276,62 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get authorized workspace agent metadata",
|
||||
"operationId": "get-authorized-workspace-agent-metadata",
|
||||
"summary": "Get authorized workspace agent manifest",
|
||||
"operationId": "get-authorized-workspace-agent-manifest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Metadata"
|
||||
"$ref": "#/definitions/agentsdk.Manifest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata/{key}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Submit workspace agent metadata",
|
||||
"operationId": "submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "string",
|
||||
"description": "metadata key",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-lifecycle": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -4663,6 +4707,38 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/watch-metadata": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Watch for workspace agent metadata updates",
|
||||
"operationId": "watch-for-workspace-agent-metadata-updates",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspacebuilds/{workspacebuild}": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -5397,7 +5473,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Metadata": {
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apps": {
|
||||
|
@ -5422,6 +5498,12 @@ const docTemplate = `{
|
|||
"description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.",
|
||||
"type": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentMetadataDescription"
|
||||
}
|
||||
},
|
||||
"motd_file": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -5473,6 +5555,25 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostStartupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -8915,6 +9016,26 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"interval": {
|
||||
"type": "integer"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"script": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -3747,7 +3747,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata": {
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
|
@ -3756,18 +3756,58 @@
|
|||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Get authorized workspace agent metadata",
|
||||
"operationId": "get-authorized-workspace-agent-metadata",
|
||||
"summary": "Get authorized workspace agent manifest",
|
||||
"operationId": "get-authorized-workspace-agent-manifest",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.Metadata"
|
||||
"$ref": "#/definitions/agentsdk.Manifest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/metadata/{key}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Submit workspace agent metadata",
|
||||
"operationId": "submit-workspace-agent-metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Workspace agent metadata request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PostMetadataRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "string",
|
||||
"description": "metadata key",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/report-lifecycle": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -4101,6 +4141,36 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/watch-metadata": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Watch for workspace agent metadata updates",
|
||||
"operationId": "watch-for-workspace-agent-metadata-updates",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
}
|
||||
},
|
||||
"x-apidocgen": {
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspacebuilds/{workspacebuild}": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -4758,7 +4828,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Metadata": {
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apps": {
|
||||
|
@ -4783,6 +4853,12 @@
|
|||
"description": "GitAuthConfigs stores the number of Git configurations\nthe Coder deployment has. If this number is \u003e0, we\nset up special configuration in the workspace.",
|
||||
"type": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentMetadataDescription"
|
||||
}
|
||||
},
|
||||
"motd_file": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -4834,6 +4910,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostMetadataRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"description": "Age is the number of seconds since the metadata was collected.\nIt is provided in addition to CollectedAt to protect against clock skew.",
|
||||
"type": "integer"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PostStartupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -8043,6 +8138,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"interval": {
|
||||
"type": "integer"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"script": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -608,7 +608,10 @@ func New(options *Options) *API {
|
|||
r.Post("/google-instance-identity", api.postWorkspaceAuthGoogleInstanceIdentity)
|
||||
r.Route("/me", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractWorkspaceAgent(options.Database))
|
||||
r.Get("/metadata", api.workspaceAgentMetadata)
|
||||
r.Get("/manifest", api.workspaceAgentManifest)
|
||||
// This route is deprecated and will be removed in a future release.
|
||||
// New agents will use /me/manifest instead.
|
||||
r.Get("/metadata", api.workspaceAgentManifest)
|
||||
r.Post("/startup", api.postWorkspaceAgentStartup)
|
||||
r.Patch("/startup-logs", api.patchWorkspaceAgentStartupLogs)
|
||||
r.Post("/app-health", api.postWorkspaceAppHealth)
|
||||
|
@ -617,6 +620,7 @@ func New(options *Options) *API {
|
|||
r.Get("/coordinate", api.workspaceAgentCoordinate)
|
||||
r.Post("/report-stats", api.workspaceAgentReportStats)
|
||||
r.Post("/report-lifecycle", api.workspaceAgentReportLifecycle)
|
||||
r.Post("/metadata/{key}", api.workspaceAgentPostMetadata)
|
||||
})
|
||||
// No middleware on the PTY endpoint since it uses workspace
|
||||
// application auth and tickets.
|
||||
|
@ -628,6 +632,8 @@ func New(options *Options) *API {
|
|||
httpmw.ExtractWorkspaceParam(options.Database),
|
||||
)
|
||||
r.Get("/", api.workspaceAgent)
|
||||
r.Get("/watch-metadata", api.watchWorkspaceAgentMetadata)
|
||||
r.Get("/pty", api.workspaceAgentPTY)
|
||||
r.Get("/startup-logs", api.workspaceAgentStartupLogs)
|
||||
r.Get("/listening-ports", api.workspaceAgentListeningPorts)
|
||||
r.Get("/connection", api.workspaceAgentConnection)
|
||||
|
|
|
@ -160,6 +160,11 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [
|
|||
t.Run(method+" "+route, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This route is for compatibility purposes and is not documented.
|
||||
if route == "/workspaceagents/me/metadata" {
|
||||
return
|
||||
}
|
||||
|
||||
c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route)
|
||||
assert.NotNil(t, c, "Missing @Router annotation")
|
||||
if c == nil {
|
||||
|
|
|
@ -1564,6 +1564,44 @@ func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.Ins
|
|||
return q.db.InsertWorkspaceAgentStat(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentMetadata(ctx context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
|
||||
// We don't check for workspace ownership here since the agent metadata may
|
||||
// be associated with an orphaned agent used by a dry run build.
|
||||
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.db.InsertWorkspaceAgentMetadata(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAgentMetadata(ctx context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, arg.WorkspaceAgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.db.UpdateWorkspaceAgentMetadata(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, workspaceAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = q.authorizeContext(ctx, rbac.ActionRead, workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return q.db.GetWorkspaceAgentMetadata(ctx, workspaceAgentID)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||
// TODO: This is a workspace agent operation. Should users be able to query this?
|
||||
workspace, err := q.db.GetWorkspaceByWorkspaceAppID(ctx, arg.ID)
|
||||
|
|
|
@ -124,6 +124,7 @@ type data struct {
|
|||
templateVersionVariables []database.TemplateVersionVariable
|
||||
templates []database.Template
|
||||
workspaceAgents []database.WorkspaceAgent
|
||||
workspaceAgentMetadata []database.WorkspaceAgentMetadatum
|
||||
workspaceAgentLogs []database.WorkspaceAgentStartupLog
|
||||
workspaceApps []database.WorkspaceApp
|
||||
workspaceBuilds []database.WorkspaceBuild
|
||||
|
@ -2741,6 +2742,60 @@ func (q *fakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyP
|
|||
return key, nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateWorkspaceAgentMetadata(_ context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
//nolint:gosimple
|
||||
updated := database.WorkspaceAgentMetadatum{
|
||||
WorkspaceAgentID: arg.WorkspaceAgentID,
|
||||
Key: arg.Key,
|
||||
Value: arg.Value,
|
||||
Error: arg.Error,
|
||||
CollectedAt: arg.CollectedAt,
|
||||
}
|
||||
|
||||
for i, m := range q.workspaceAgentMetadata {
|
||||
if m.WorkspaceAgentID == arg.WorkspaceAgentID && m.Key == arg.Key {
|
||||
q.workspaceAgentMetadata[i] = updated
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
//nolint:gosimple
|
||||
metadatum := database.WorkspaceAgentMetadatum{
|
||||
WorkspaceAgentID: arg.WorkspaceAgentID,
|
||||
Script: arg.Script,
|
||||
DisplayName: arg.DisplayName,
|
||||
Key: arg.Key,
|
||||
Timeout: arg.Timeout,
|
||||
Interval: arg.Interval,
|
||||
}
|
||||
|
||||
q.workspaceAgentMetadata = append(q.workspaceAgentMetadata, metadatum)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
metadata := make([]database.WorkspaceAgentMetadatum, 0)
|
||||
for _, m := range q.workspaceAgentMetadata {
|
||||
if m.WorkspaceAgentID == workspaceAgentID {
|
||||
metadata = append(metadata, m)
|
||||
}
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) InsertFile(_ context.Context, arg database.InsertFileParams) (database.File, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return database.File{}, err
|
||||
|
|
|
@ -475,6 +475,18 @@ CREATE TABLE users (
|
|||
last_seen_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNLOGGED TABLE workspace_agent_metadata (
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
display_name character varying(127) NOT NULL,
|
||||
key character varying(127) NOT NULL,
|
||||
script character varying(65535) NOT NULL,
|
||||
value character varying(65535) DEFAULT ''::character varying NOT NULL,
|
||||
error character varying(65535) DEFAULT ''::character varying NOT NULL,
|
||||
timeout bigint NOT NULL,
|
||||
"interval" bigint NOT NULL,
|
||||
collected_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_agent_startup_logs (
|
||||
agent_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
|
@ -756,6 +768,9 @@ ALTER TABLE ONLY user_links
|
|||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_metadata
|
||||
ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_startup_logs
|
||||
ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -894,6 +909,9 @@ ALTER TABLE ONLY templates
|
|||
ALTER TABLE ONLY user_links
|
||||
ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_metadata
|
||||
ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_startup_logs
|
||||
ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE workspace_agent_metadata;
|
|
@ -0,0 +1,16 @@
|
|||
-- This table is UNLOGGED because it is very update-heavy and the the data
|
||||
-- is not valuable enough to justify the overhead of WAL logging. This should
|
||||
-- give us a ~70% improvement in write throughput.
|
||||
CREATE UNLOGGED TABLE workspace_agent_metadata (
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
display_name varchar(127) NOT NULL,
|
||||
key varchar(127) NOT NULL,
|
||||
script varchar(65535) NOT NULL,
|
||||
value varchar(65535) NOT NULL DEFAULT '',
|
||||
error varchar(65535) NOT NULL DEFAULT '',
|
||||
timeout bigint NOT NULL,
|
||||
interval bigint NOT NULL,
|
||||
collected_at timestamp with time zone NOT NULL DEFAULT '0001-01-01 00:00:00+00',
|
||||
PRIMARY KEY (workspace_agent_id, key),
|
||||
FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE
|
||||
);
|
18
coderd/database/migrations/testdata/fixtures/000111_workspace_agent_metadata.up.sql
vendored
Normal file
18
coderd/database/migrations/testdata/fixtures/000111_workspace_agent_metadata.up.sql
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
INSERT INTO
|
||||
workspace_agent_metadata (
|
||||
workspace_agent_id,
|
||||
display_name,
|
||||
key,
|
||||
script,
|
||||
timeout,
|
||||
interval
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'45e89705-e09d-4850-bcec-f9a937f5d78d',
|
||||
'a h e m',
|
||||
'ahem',
|
||||
'rm -rf',
|
||||
3,
|
||||
1
|
||||
);
|
|
@ -1575,6 +1575,18 @@ type WorkspaceAgent struct {
|
|||
StartupLogsOverflowed bool `db:"startup_logs_overflowed" json:"startup_logs_overflowed"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentMetadatum struct {
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Key string `db:"key" json:"key"`
|
||||
Script string `db:"script" json:"script"`
|
||||
Value string `db:"value" json:"value"`
|
||||
Error string `db:"error" json:"error"`
|
||||
Timeout int64 `db:"timeout" json:"timeout"`
|
||||
Interval int64 `db:"interval" json:"interval"`
|
||||
CollectedAt time.Time `db:"collected_at" json:"collected_at"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentStartupLog struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
|
|
|
@ -126,6 +126,7 @@ type sqlcQuerier interface {
|
|||
GetWorkspaceAgentByAuthToken(ctx context.Context, authToken uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentMetadatum, error)
|
||||
GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg GetWorkspaceAgentStartupLogsAfterParams) ([]WorkspaceAgentStartupLog, error)
|
||||
GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsRow, error)
|
||||
GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error)
|
||||
|
@ -185,6 +186,7 @@ type sqlcQuerier interface {
|
|||
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
|
||||
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error)
|
||||
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
|
||||
InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error
|
||||
InsertWorkspaceAgentStartupLogs(ctx context.Context, arg InsertWorkspaceAgentStartupLogsParams) ([]WorkspaceAgentStartupLog, error)
|
||||
InsertWorkspaceAgentStat(ctx context.Context, arg InsertWorkspaceAgentStatParams) (WorkspaceAgentStat, error)
|
||||
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
|
||||
|
@ -229,6 +231,7 @@ type sqlcQuerier interface {
|
|||
UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error)
|
||||
UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error
|
||||
UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg UpdateWorkspaceAgentLifecycleStateByIDParams) error
|
||||
UpdateWorkspaceAgentMetadata(ctx context.Context, arg UpdateWorkspaceAgentMetadataParams) error
|
||||
UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error
|
||||
UpdateWorkspaceAgentStartupLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentStartupLogOverflowByIDParams) error
|
||||
UpdateWorkspaceAppHealthByID(ctx context.Context, arg UpdateWorkspaceAppHealthByIDParams) error
|
||||
|
|
|
@ -5297,6 +5297,48 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
|
|||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceAgentMetadata = `-- name: GetWorkspaceAgentMetadata :many
|
||||
SELECT
|
||||
workspace_agent_id, display_name, key, script, value, error, timeout, interval, collected_at
|
||||
FROM
|
||||
workspace_agent_metadata
|
||||
WHERE
|
||||
workspace_agent_id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentMetadatum, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentMetadata, workspaceAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentMetadatum
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentMetadatum
|
||||
if err := rows.Scan(
|
||||
&i.WorkspaceAgentID,
|
||||
&i.DisplayName,
|
||||
&i.Key,
|
||||
&i.Script,
|
||||
&i.Value,
|
||||
&i.Error,
|
||||
&i.Timeout,
|
||||
&i.Interval,
|
||||
&i.CollectedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAgentStartupLogsAfter = `-- name: GetWorkspaceAgentStartupLogsAfter :many
|
||||
SELECT
|
||||
agent_id, created_at, output, id
|
||||
|
@ -5651,6 +5693,41 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
|
|||
return i, err
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentMetadata = `-- name: InsertWorkspaceAgentMetadata :exec
|
||||
INSERT INTO
|
||||
workspace_agent_metadata (
|
||||
workspace_agent_id,
|
||||
display_name,
|
||||
key,
|
||||
script,
|
||||
timeout,
|
||||
interval
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6)
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentMetadataParams struct {
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Key string `db:"key" json:"key"`
|
||||
Script string `db:"script" json:"script"`
|
||||
Timeout int64 `db:"timeout" json:"timeout"`
|
||||
Interval int64 `db:"interval" json:"interval"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error {
|
||||
_, err := q.db.ExecContext(ctx, insertWorkspaceAgentMetadata,
|
||||
arg.WorkspaceAgentID,
|
||||
arg.DisplayName,
|
||||
arg.Key,
|
||||
arg.Script,
|
||||
arg.Timeout,
|
||||
arg.Interval,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentStartupLogs = `-- name: InsertWorkspaceAgentStartupLogs :many
|
||||
WITH new_length AS (
|
||||
UPDATE workspace_agents SET
|
||||
|
@ -5758,6 +5835,37 @@ func (q *sqlQuerier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentMetadata = `-- name: UpdateWorkspaceAgentMetadata :exec
|
||||
UPDATE
|
||||
workspace_agent_metadata
|
||||
SET
|
||||
value = $3,
|
||||
error = $4,
|
||||
collected_at = $5
|
||||
WHERE
|
||||
workspace_agent_id = $1
|
||||
AND key = $2
|
||||
`
|
||||
|
||||
type UpdateWorkspaceAgentMetadataParams struct {
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
Key string `db:"key" json:"key"`
|
||||
Value string `db:"value" json:"value"`
|
||||
Error string `db:"error" json:"error"`
|
||||
CollectedAt time.Time `db:"collected_at" json:"collected_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateWorkspaceAgentMetadata(ctx context.Context, arg UpdateWorkspaceAgentMetadataParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentMetadata,
|
||||
arg.WorkspaceAgentID,
|
||||
arg.Key,
|
||||
arg.Value,
|
||||
arg.Error,
|
||||
arg.CollectedAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentStartupByID = `-- name: UpdateWorkspaceAgentStartupByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
|
|
|
@ -94,6 +94,38 @@ SET
|
|||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: InsertWorkspaceAgentMetadata :exec
|
||||
INSERT INTO
|
||||
workspace_agent_metadata (
|
||||
workspace_agent_id,
|
||||
display_name,
|
||||
key,
|
||||
script,
|
||||
timeout,
|
||||
interval
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6);
|
||||
|
||||
-- name: UpdateWorkspaceAgentMetadata :exec
|
||||
UPDATE
|
||||
workspace_agent_metadata
|
||||
SET
|
||||
value = $3,
|
||||
error = $4,
|
||||
collected_at = $5
|
||||
WHERE
|
||||
workspace_agent_id = $1
|
||||
AND key = $2;
|
||||
|
||||
-- name: GetWorkspaceAgentMetadata :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_agent_metadata
|
||||
WHERE
|
||||
workspace_agent_id = $1;
|
||||
|
||||
-- name: UpdateWorkspaceAgentStartupLogOverflowByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
|
|
|
@ -1277,6 +1277,21 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
|||
}
|
||||
snapshot.WorkspaceAgents = append(snapshot.WorkspaceAgents, telemetry.ConvertWorkspaceAgent(dbAgent))
|
||||
|
||||
for _, md := range prAgent.Metadata {
|
||||
p := database.InsertWorkspaceAgentMetadataParams{
|
||||
WorkspaceAgentID: agentID,
|
||||
DisplayName: md.DisplayName,
|
||||
Script: md.Script,
|
||||
Key: md.Key,
|
||||
Timeout: md.Timeout,
|
||||
Interval: md.Interval,
|
||||
}
|
||||
err := db.InsertWorkspaceAgentMetadata(ctx, p)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert agent metadata: %w, params: %+v", err, p)
|
||||
}
|
||||
}
|
||||
|
||||
for _, app := range prAgent.Apps {
|
||||
slug := app.Slug
|
||||
if slug == "" {
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/xerrors"
|
||||
"nhooyr.io/websocket"
|
||||
|
@ -76,14 +77,14 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
|||
httpapi.Write(ctx, rw, http.StatusOK, apiAgent)
|
||||
}
|
||||
|
||||
// @Summary Get authorized workspace agent metadata
|
||||
// @ID get-authorized-workspace-agent-metadata
|
||||
// @Summary Get authorized workspace agent manifest
|
||||
// @ID get-authorized-workspace-agent-manifest
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Success 200 {object} agentsdk.Metadata
|
||||
// @Router /workspaceagents/me/metadata [get]
|
||||
func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Success 200 {object} agentsdk.Manifest
|
||||
// @Router /workspaceagents/me/manifest [get]
|
||||
func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
apiAgent, err := convertWorkspaceAgent(
|
||||
|
@ -105,6 +106,16 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
|||
})
|
||||
return
|
||||
}
|
||||
|
||||
metadata, err := api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace agent metadata.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
|
@ -149,7 +160,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
|||
vscodeProxyURI += fmt.Sprintf(":%s", api.AccessURL.Port())
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, agentsdk.Metadata{
|
||||
httpapi.Write(ctx, rw, http.StatusOK, agentsdk.Manifest{
|
||||
Apps: convertApps(dbApps),
|
||||
DERPMap: api.DERPMap,
|
||||
GitAuthConfigs: len(api.GitAuthConfigs),
|
||||
|
@ -161,6 +172,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
|||
StartupScriptTimeout: time.Duration(apiAgent.StartupScriptTimeoutSeconds) * time.Second,
|
||||
ShutdownScript: apiAgent.ShutdownScript,
|
||||
ShutdownScriptTimeout: time.Duration(apiAgent.ShutdownScriptTimeoutSeconds) * time.Second,
|
||||
Metadata: convertWorkspaceAgentMetadataDesc(metadata),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1133,6 +1145,20 @@ func convertApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
|
|||
return apps
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentMetadataDesc(mds []database.WorkspaceAgentMetadatum) []codersdk.WorkspaceAgentMetadataDescription {
|
||||
metadata := make([]codersdk.WorkspaceAgentMetadataDescription, 0)
|
||||
for _, datum := range mds {
|
||||
metadata = append(metadata, codersdk.WorkspaceAgentMetadataDescription{
|
||||
DisplayName: datum.DisplayName,
|
||||
Key: datum.Key,
|
||||
Script: datum.Script,
|
||||
Interval: datum.Interval,
|
||||
Timeout: datum.Timeout,
|
||||
})
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) {
|
||||
var envs map[string]string
|
||||
if dbAgent.EnvironmentVariables.Valid {
|
||||
|
@ -1298,6 +1324,219 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques
|
|||
})
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent metadata
|
||||
// @ID submit-workspace-agent-metadata
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PostMetadataRequest true "Workspace agent metadata request"
|
||||
// @Param key path string true "metadata key" format(string)
|
||||
// @Success 204 "Success"
|
||||
// @Router /workspaceagents/me/metadata/{key} [post]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) workspaceAgentPostMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var req agentsdk.PostMetadataRequest
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
workspace, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to get workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
key := chi.URLParam(r, "key")
|
||||
|
||||
const (
|
||||
maxValueLen = 32 << 10
|
||||
maxErrorLen = maxValueLen
|
||||
)
|
||||
|
||||
metadataError := req.Error
|
||||
|
||||
// We overwrite the error if the provided payload is too long.
|
||||
if len(req.Value) > maxValueLen {
|
||||
metadataError = fmt.Sprintf("value of %d bytes exceeded %d bytes", len(req.Value), maxValueLen)
|
||||
req.Value = req.Value[:maxValueLen]
|
||||
}
|
||||
|
||||
if len(req.Error) > maxErrorLen {
|
||||
metadataError = fmt.Sprintf("error of %d bytes exceeded %d bytes", len(req.Error), maxErrorLen)
|
||||
req.Error = req.Error[:maxErrorLen]
|
||||
}
|
||||
|
||||
datum := database.UpdateWorkspaceAgentMetadataParams{
|
||||
WorkspaceAgentID: workspaceAgent.ID,
|
||||
// We don't want a misconfigured agent to fill the database.
|
||||
Key: key,
|
||||
Value: req.Value,
|
||||
Error: metadataError,
|
||||
// We ignore the CollectedAt from the agent to avoid bugs caused by
|
||||
// clock skew.
|
||||
CollectedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = api.Database.UpdateWorkspaceAgentMetadata(ctx, datum)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Logger.Debug(
|
||||
ctx, "accepted metadata report",
|
||||
slog.F("agent", workspaceAgent.ID),
|
||||
slog.F("workspace", workspace.ID),
|
||||
slog.F("collected_at", datum.CollectedAt),
|
||||
slog.F("key", datum.Key),
|
||||
)
|
||||
|
||||
err = api.Pubsub.Publish(watchWorkspaceAgentMetadataChannel(workspaceAgent.ID), []byte(datum.Key))
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// @Summary Watch for workspace agent metadata updates
|
||||
// @ID watch-for-workspace-agent-metadata-updates
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Agents
|
||||
// @Success 200 "Success"
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Router /workspaceagents/{workspaceagent}/watch-metadata [get]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) watchWorkspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
workspaceAgent = httpmw.WorkspaceAgentParam(r)
|
||||
)
|
||||
|
||||
sendEvent, senderClosed, 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() {
|
||||
<-senderClosed
|
||||
}()
|
||||
|
||||
// We don't want this intentionally long request to skew our tracing
|
||||
// reports.
|
||||
ctx = trace.ContextWithSpan(ctx, tracing.NoopSpan)
|
||||
|
||||
const refreshInterval = time.Second * 5
|
||||
refreshTicker := time.NewTicker(refreshInterval)
|
||||
defer refreshTicker.Stop()
|
||||
|
||||
var (
|
||||
lastDBMetaMu sync.Mutex
|
||||
lastDBMeta []database.WorkspaceAgentMetadatum
|
||||
)
|
||||
|
||||
sendMetadata := func(pull bool) {
|
||||
lastDBMetaMu.Lock()
|
||||
defer lastDBMetaMu.Unlock()
|
||||
|
||||
var err error
|
||||
if pull {
|
||||
// We always use the original Request context because it contains
|
||||
// the RBAC actor.
|
||||
lastDBMeta, err = api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
_ = sendEvent(ctx, codersdk.ServerSentEvent{
|
||||
Type: codersdk.ServerSentEventTypeError,
|
||||
Data: codersdk.Response{
|
||||
Message: "Internal error getting metadata.",
|
||||
Detail: err.Error(),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
slices.SortFunc(lastDBMeta, func(i, j database.WorkspaceAgentMetadatum) bool {
|
||||
return i.Key < j.Key
|
||||
})
|
||||
|
||||
// Avoid sending refresh if the client is about to get a
|
||||
// fresh update.
|
||||
refreshTicker.Reset(refreshInterval)
|
||||
}
|
||||
|
||||
_ = sendEvent(ctx, codersdk.ServerSentEvent{
|
||||
Type: codersdk.ServerSentEventTypeData,
|
||||
Data: convertWorkspaceAgentMetadata(lastDBMeta),
|
||||
})
|
||||
}
|
||||
|
||||
// Send initial metadata.
|
||||
sendMetadata(true)
|
||||
|
||||
// Send metadata on updates.
|
||||
cancelSub, err := api.Pubsub.Subscribe(watchWorkspaceAgentMetadataChannel(workspaceAgent.ID), func(_ context.Context, _ []byte) {
|
||||
sendMetadata(true)
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
defer cancelSub()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-senderClosed:
|
||||
return
|
||||
case <-refreshTicker.C:
|
||||
break
|
||||
}
|
||||
|
||||
// Avoid spamming the DB with reads we know there are no updates. We want
|
||||
// to continue sending updates to the frontend so that "Result.Age"
|
||||
// is always accurate. This way, the frontend doesn't need to
|
||||
// sync its own clock with the backend.
|
||||
sendMetadata(false)
|
||||
}
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentMetadata(db []database.WorkspaceAgentMetadatum) []codersdk.WorkspaceAgentMetadata {
|
||||
// An empty array is easier for clients to handle than a null.
|
||||
result := []codersdk.WorkspaceAgentMetadata{}
|
||||
for _, datum := range db {
|
||||
result = append(result, codersdk.WorkspaceAgentMetadata{
|
||||
Result: codersdk.WorkspaceAgentMetadataResult{
|
||||
Value: datum.Value,
|
||||
Error: datum.Error,
|
||||
CollectedAt: datum.CollectedAt,
|
||||
Age: int64(time.Since(datum.CollectedAt).Seconds()),
|
||||
},
|
||||
Description: codersdk.WorkspaceAgentMetadataDescription{
|
||||
DisplayName: datum.DisplayName,
|
||||
Key: datum.Key,
|
||||
Script: datum.Script,
|
||||
Interval: datum.Interval,
|
||||
Timeout: datum.Timeout,
|
||||
},
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func watchWorkspaceAgentMetadataChannel(id uuid.UUID) string {
|
||||
return "workspace_agent_metadata:" + id.String()
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent lifecycle state
|
||||
// @ID submit-workspace-agent-lifecycle-state
|
||||
// @Security CoderSessionToken
|
||||
|
|
|
@ -831,10 +831,10 @@ func TestWorkspaceAgentAppHealth(t *testing.T) {
|
|||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
|
||||
metadata, err := agentClient.Metadata(ctx)
|
||||
manifest, err := agentClient.Manifest(ctx)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthDisabled, metadata.Apps[0].Health)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthInitializing, metadata.Apps[1].Health)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthDisabled, manifest.Apps[0].Health)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthInitializing, manifest.Apps[1].Health)
|
||||
err = agentClient.PostAppHealth(ctx, agentsdk.PostAppHealthsRequest{})
|
||||
require.Error(t, err)
|
||||
// empty
|
||||
|
@ -843,37 +843,37 @@ func TestWorkspaceAgentAppHealth(t *testing.T) {
|
|||
// healthcheck disabled
|
||||
err = agentClient.PostAppHealth(ctx, agentsdk.PostAppHealthsRequest{
|
||||
Healths: map[uuid.UUID]codersdk.WorkspaceAppHealth{
|
||||
metadata.Apps[0].ID: codersdk.WorkspaceAppHealthInitializing,
|
||||
manifest.Apps[0].ID: codersdk.WorkspaceAppHealthInitializing,
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
// invalid value
|
||||
err = agentClient.PostAppHealth(ctx, agentsdk.PostAppHealthsRequest{
|
||||
Healths: map[uuid.UUID]codersdk.WorkspaceAppHealth{
|
||||
metadata.Apps[1].ID: codersdk.WorkspaceAppHealth("bad-value"),
|
||||
manifest.Apps[1].ID: codersdk.WorkspaceAppHealth("bad-value"),
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
// update to healthy
|
||||
err = agentClient.PostAppHealth(ctx, agentsdk.PostAppHealthsRequest{
|
||||
Healths: map[uuid.UUID]codersdk.WorkspaceAppHealth{
|
||||
metadata.Apps[1].ID: codersdk.WorkspaceAppHealthHealthy,
|
||||
manifest.Apps[1].ID: codersdk.WorkspaceAppHealthHealthy,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
metadata, err = agentClient.Metadata(ctx)
|
||||
manifest, err = agentClient.Manifest(ctx)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthHealthy, metadata.Apps[1].Health)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthHealthy, manifest.Apps[1].Health)
|
||||
// update to unhealthy
|
||||
err = agentClient.PostAppHealth(ctx, agentsdk.PostAppHealthsRequest{
|
||||
Healths: map[uuid.UUID]codersdk.WorkspaceAppHealth{
|
||||
metadata.Apps[1].ID: codersdk.WorkspaceAppHealthUnhealthy,
|
||||
manifest.Apps[1].ID: codersdk.WorkspaceAppHealthUnhealthy,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
metadata, err = agentClient.Metadata(ctx)
|
||||
manifest, err = agentClient.Manifest(ctx)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthUnhealthy, metadata.Apps[1].Health)
|
||||
require.EqualValues(t, codersdk.WorkspaceAppHealthUnhealthy, manifest.Apps[1].Health)
|
||||
}
|
||||
|
||||
// nolint:bodyclose
|
||||
|
@ -1262,3 +1262,155 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgent_Metadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
Agents: []*proto.Agent{{
|
||||
Metadata: []*proto.Agent_Metadata{
|
||||
{
|
||||
DisplayName: "First Meta",
|
||||
Key: "foo1",
|
||||
Script: "echo hi",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
},
|
||||
{
|
||||
DisplayName: "Second Meta",
|
||||
Key: "foo2",
|
||||
Script: "echo howdy",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
},
|
||||
{
|
||||
DisplayName: "TooLong",
|
||||
Key: "foo3",
|
||||
Script: "echo howdy",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
},
|
||||
},
|
||||
Id: uuid.NewString(),
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
for _, res := range workspace.LatestBuild.Resources {
|
||||
for _, a := range res.Agents {
|
||||
require.Equal(t, codersdk.WorkspaceAgentLifecycleCreated, a.LifecycleState)
|
||||
}
|
||||
}
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
|
||||
manifest, err := agentClient.Manifest(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify manifest API response.
|
||||
require.Equal(t, "First Meta", manifest.Metadata[0].DisplayName)
|
||||
require.Equal(t, "foo1", manifest.Metadata[0].Key)
|
||||
require.Equal(t, "echo hi", manifest.Metadata[0].Script)
|
||||
require.EqualValues(t, 10, manifest.Metadata[0].Interval)
|
||||
require.EqualValues(t, 3, manifest.Metadata[0].Timeout)
|
||||
|
||||
post := func(key string, mr codersdk.WorkspaceAgentMetadataResult) {
|
||||
err := agentClient.PostMetadata(ctx, key, mr)
|
||||
require.NoError(t, err, "post metadata", t)
|
||||
}
|
||||
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err, "get workspace")
|
||||
|
||||
agentID := workspace.LatestBuild.Resources[0].Agents[0].ID
|
||||
|
||||
var update []codersdk.WorkspaceAgentMetadata
|
||||
|
||||
check := func(want codersdk.WorkspaceAgentMetadataResult, got codersdk.WorkspaceAgentMetadata) {
|
||||
require.WithinDuration(t, want.CollectedAt, got.Result.CollectedAt, time.Second)
|
||||
require.WithinDuration(
|
||||
t, time.Now(), got.Result.CollectedAt.Add(time.Duration(got.Result.Age)*time.Second), time.Millisecond*500,
|
||||
)
|
||||
require.Equal(t, want.Value, got.Result.Value)
|
||||
require.Equal(t, want.Error, got.Result.Error)
|
||||
}
|
||||
|
||||
wantMetadata1 := codersdk.WorkspaceAgentMetadataResult{
|
||||
CollectedAt: time.Now(),
|
||||
Value: "bar",
|
||||
}
|
||||
|
||||
// Initial post must come before the Watch is established.
|
||||
post("foo1", wantMetadata1)
|
||||
|
||||
updates, errors := client.WatchWorkspaceAgentMetadata(ctx, agentID)
|
||||
|
||||
recvUpdate := func() []codersdk.WorkspaceAgentMetadata {
|
||||
select {
|
||||
case err := <-errors:
|
||||
t.Fatalf("error watching metadata: %v", err)
|
||||
return nil
|
||||
case update := <-updates:
|
||||
return update
|
||||
}
|
||||
}
|
||||
|
||||
update = recvUpdate()
|
||||
require.Len(t, update, 3)
|
||||
check(wantMetadata1, update[0])
|
||||
// The second metadata result is not yet posted.
|
||||
require.Zero(t, update[1].Result.CollectedAt)
|
||||
|
||||
wantMetadata2 := wantMetadata1
|
||||
post("foo2", wantMetadata2)
|
||||
update = recvUpdate()
|
||||
require.Len(t, update, 3)
|
||||
check(wantMetadata1, update[0])
|
||||
check(wantMetadata2, update[1])
|
||||
|
||||
wantMetadata1.Error = "error"
|
||||
post("foo1", wantMetadata1)
|
||||
update = recvUpdate()
|
||||
require.Len(t, update, 3)
|
||||
check(wantMetadata1, update[0])
|
||||
|
||||
const maxValueLen = 32 << 10
|
||||
tooLongValueMetadata := wantMetadata1
|
||||
tooLongValueMetadata.Value = strings.Repeat("a", maxValueLen*2)
|
||||
tooLongValueMetadata.Error = ""
|
||||
tooLongValueMetadata.CollectedAt = time.Now()
|
||||
post("foo3", tooLongValueMetadata)
|
||||
got := recvUpdate()[2]
|
||||
require.Len(t, got.Result.Value, maxValueLen)
|
||||
require.NotEmpty(t, got.Result.Error)
|
||||
|
||||
unknownKeyMetadata := wantMetadata1
|
||||
err = agentClient.PostMetadata(ctx, "unknown", unknownKeyMetadata)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -273,7 +273,7 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U
|
|||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
if appHost != "" {
|
||||
metadata, err := agentClient.Metadata(context.Background())
|
||||
manifest, err := agentClient.Manifest(context.Background())
|
||||
require.NoError(t, err)
|
||||
proxyURL := fmt.Sprintf(
|
||||
"http://{{port}}--%s--%s--%s%s",
|
||||
|
@ -285,7 +285,7 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U
|
|||
if client.URL.Port() != "" {
|
||||
proxyURL += fmt.Sprintf(":%s", client.URL.Port())
|
||||
}
|
||||
require.Equal(t, proxyURL, metadata.VSCodePortProxyURI)
|
||||
require.Equal(t, proxyURL, manifest.VSCodePortProxyURI)
|
||||
}
|
||||
agentCloser := agent.New(agent.Options{
|
||||
Client: agentClient,
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestCache(t *testing.T) {
|
|||
t.Run("Same", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cache := wsconncache.New(func(id uuid.UUID) (*codersdk.WorkspaceAgentConn, error) {
|
||||
return setupAgent(t, agentsdk.Metadata{}, 0), nil
|
||||
return setupAgent(t, agentsdk.Manifest{}, 0), nil
|
||||
}, 0)
|
||||
defer func() {
|
||||
_ = cache.Close()
|
||||
|
@ -57,7 +57,7 @@ func TestCache(t *testing.T) {
|
|||
called := atomic.NewInt32(0)
|
||||
cache := wsconncache.New(func(id uuid.UUID) (*codersdk.WorkspaceAgentConn, error) {
|
||||
called.Add(1)
|
||||
return setupAgent(t, agentsdk.Metadata{}, 0), nil
|
||||
return setupAgent(t, agentsdk.Manifest{}, 0), nil
|
||||
}, time.Microsecond)
|
||||
defer func() {
|
||||
_ = cache.Close()
|
||||
|
@ -75,7 +75,7 @@ func TestCache(t *testing.T) {
|
|||
t.Run("NoExpireWhenLocked", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cache := wsconncache.New(func(id uuid.UUID) (*codersdk.WorkspaceAgentConn, error) {
|
||||
return setupAgent(t, agentsdk.Metadata{}, 0), nil
|
||||
return setupAgent(t, agentsdk.Manifest{}, 0), nil
|
||||
}, time.Microsecond)
|
||||
defer func() {
|
||||
_ = cache.Close()
|
||||
|
@ -108,7 +108,7 @@ func TestCache(t *testing.T) {
|
|||
go server.Serve(random)
|
||||
|
||||
cache := wsconncache.New(func(id uuid.UUID) (*codersdk.WorkspaceAgentConn, error) {
|
||||
return setupAgent(t, agentsdk.Metadata{}, 0), nil
|
||||
return setupAgent(t, agentsdk.Manifest{}, 0), nil
|
||||
}, time.Microsecond)
|
||||
defer func() {
|
||||
_ = cache.Close()
|
||||
|
@ -154,10 +154,10 @@ func TestCache(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Duration) *codersdk.WorkspaceAgentConn {
|
||||
func setupAgent(t *testing.T, manifest agentsdk.Manifest, ptyTimeout time.Duration) *codersdk.WorkspaceAgentConn {
|
||||
t.Helper()
|
||||
|
||||
metadata.DERPMap = tailnettest.RunDERPAndSTUN(t)
|
||||
manifest.DERPMap = tailnettest.RunDERPAndSTUN(t)
|
||||
|
||||
coordinator := tailnet.NewCoordinator()
|
||||
t.Cleanup(func() {
|
||||
|
@ -168,7 +168,7 @@ func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Durati
|
|||
Client: &client{
|
||||
t: t,
|
||||
agentID: agentID,
|
||||
metadata: metadata,
|
||||
manifest: manifest,
|
||||
coordinator: coordinator,
|
||||
},
|
||||
Logger: slogtest.Make(t, nil).Named("agent").Leveled(slog.LevelInfo),
|
||||
|
@ -179,7 +179,7 @@ func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Durati
|
|||
})
|
||||
conn, err := tailnet.NewConn(&tailnet.Options{
|
||||
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
|
||||
DERPMap: metadata.DERPMap,
|
||||
DERPMap: manifest.DERPMap,
|
||||
Logger: slogtest.Make(t, nil).Named("tailnet").Leveled(slog.LevelDebug),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
@ -211,12 +211,12 @@ func setupAgent(t *testing.T, metadata agentsdk.Metadata, ptyTimeout time.Durati
|
|||
type client struct {
|
||||
t *testing.T
|
||||
agentID uuid.UUID
|
||||
metadata agentsdk.Metadata
|
||||
manifest agentsdk.Manifest
|
||||
coordinator tailnet.Coordinator
|
||||
}
|
||||
|
||||
func (c *client) Metadata(_ context.Context) (agentsdk.Metadata, error) {
|
||||
return c.metadata, nil
|
||||
func (c *client) Manifest(_ context.Context) (agentsdk.Manifest, error) {
|
||||
return c.manifest, nil
|
||||
}
|
||||
|
||||
func (c *client) Listen(_ context.Context) (net.Conn, error) {
|
||||
|
@ -246,6 +246,10 @@ func (*client) PostAppHealth(_ context.Context, _ agentsdk.PostAppHealthsRequest
|
|||
return nil
|
||||
}
|
||||
|
||||
func (*client) PostMetadata(_ context.Context, _ string, _ agentsdk.PostMetadataRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*client) PostStartup(_ context.Context, _ agentsdk.PostStartupRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -65,37 +65,56 @@ func (c *Client) GitSSHKey(ctx context.Context) (GitSSHKey, error) {
|
|||
return gitSSHKey, json.NewDecoder(res.Body).Decode(&gitSSHKey)
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
// In the future, we may want to support sending back multiple values for
|
||||
// performance.
|
||||
type PostMetadataRequest = codersdk.WorkspaceAgentMetadataResult
|
||||
|
||||
func (c *Client) PostMetadata(ctx context.Context, key string, req PostMetadataRequest) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/metadata/"+key, req)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("execute request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
return codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
// GitAuthConfigs stores the number of Git configurations
|
||||
// the Coder deployment has. If this number is >0, we
|
||||
// set up special configuration in the workspace.
|
||||
GitAuthConfigs int `json:"git_auth_configs"`
|
||||
VSCodePortProxyURI string `json:"vscode_port_proxy_uri"`
|
||||
Apps []codersdk.WorkspaceApp `json:"apps"`
|
||||
DERPMap *tailcfg.DERPMap `json:"derpmap"`
|
||||
EnvironmentVariables map[string]string `json:"environment_variables"`
|
||||
StartupScript string `json:"startup_script"`
|
||||
StartupScriptTimeout time.Duration `json:"startup_script_timeout"`
|
||||
Directory string `json:"directory"`
|
||||
MOTDFile string `json:"motd_file"`
|
||||
ShutdownScript string `json:"shutdown_script"`
|
||||
ShutdownScriptTimeout time.Duration `json:"shutdown_script_timeout"`
|
||||
GitAuthConfigs int `json:"git_auth_configs"`
|
||||
VSCodePortProxyURI string `json:"vscode_port_proxy_uri"`
|
||||
Apps []codersdk.WorkspaceApp `json:"apps"`
|
||||
DERPMap *tailcfg.DERPMap `json:"derpmap"`
|
||||
EnvironmentVariables map[string]string `json:"environment_variables"`
|
||||
StartupScript string `json:"startup_script"`
|
||||
StartupScriptTimeout time.Duration `json:"startup_script_timeout"`
|
||||
Directory string `json:"directory"`
|
||||
MOTDFile string `json:"motd_file"`
|
||||
ShutdownScript string `json:"shutdown_script"`
|
||||
ShutdownScriptTimeout time.Duration `json:"shutdown_script_timeout"`
|
||||
Metadata []codersdk.WorkspaceAgentMetadataDescription `json:"metadata"`
|
||||
}
|
||||
|
||||
// Metadata fetches metadata for the currently authenticated workspace agent.
|
||||
func (c *Client) Metadata(ctx context.Context) (Metadata, error) {
|
||||
res, err := c.SDK.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/me/metadata", nil)
|
||||
// Manifest fetches manifest for the currently authenticated workspace agent.
|
||||
func (c *Client) Manifest(ctx context.Context) (Manifest, error) {
|
||||
res, err := c.SDK.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/me/manifest", nil)
|
||||
if err != nil {
|
||||
return Metadata{}, err
|
||||
return Manifest{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return Metadata{}, codersdk.ReadBodyAsError(res)
|
||||
return Manifest{}, codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
var agentMeta Metadata
|
||||
var agentMeta Manifest
|
||||
err = json.NewDecoder(res.Body).Decode(&agentMeta)
|
||||
if err != nil {
|
||||
return Metadata{}, err
|
||||
return Manifest{}, err
|
||||
}
|
||||
accessingPort := c.SDK.URL.Port()
|
||||
if accessingPort == "" {
|
||||
|
@ -106,14 +125,14 @@ func (c *Client) Metadata(ctx context.Context) (Metadata, error) {
|
|||
}
|
||||
accessPort, err := strconv.Atoi(accessingPort)
|
||||
if err != nil {
|
||||
return Metadata{}, xerrors.Errorf("convert accessing port %q: %w", accessingPort, err)
|
||||
return Manifest{}, xerrors.Errorf("convert accessing port %q: %w", accessingPort, err)
|
||||
}
|
||||
// Agents can provide an arbitrary access URL that may be different
|
||||
// that the globally configured one. This breaks the built-in DERP,
|
||||
// which would continue to reference the global access URL.
|
||||
//
|
||||
// This converts all built-in DERPs to use the access URL that the
|
||||
// metadata request was performed with.
|
||||
// manifest request was performed with.
|
||||
for _, region := range agentMeta.DERPMap.Regions {
|
||||
if !region.EmbeddedRelay {
|
||||
continue
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"tailscale.com/tailcfg"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/coderd/tracing"
|
||||
"github.com/coder/coder/tailnet"
|
||||
"github.com/coder/retry"
|
||||
)
|
||||
|
@ -75,6 +76,31 @@ var WorkspaceAgentLifecycleOrder = []WorkspaceAgentLifecycle{
|
|||
WorkspaceAgentLifecycleOff,
|
||||
}
|
||||
|
||||
type WorkspaceAgentMetadataResult struct {
|
||||
CollectedAt time.Time `json:"collected_at" format:"date-time"`
|
||||
// Age is the number of seconds since the metadata was collected.
|
||||
// It is provided in addition to CollectedAt to protect against clock skew.
|
||||
Age int64 `json:"age"`
|
||||
Value string `json:"value"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// WorkspaceAgentMetadataDescription is a description of dynamic metadata the agent should report
|
||||
// back to coderd. It is provided via the `metadata` list in the `coder_agent`
|
||||
// block.
|
||||
type WorkspaceAgentMetadataDescription struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Key string `json:"key"`
|
||||
Script string `json:"script"`
|
||||
Interval int64 `json:"interval"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentMetadata struct {
|
||||
Result WorkspaceAgentMetadataResult `json:"result"`
|
||||
Description WorkspaceAgentMetadataDescription `json:"description"`
|
||||
}
|
||||
|
||||
type WorkspaceAgent struct {
|
||||
ID uuid.UUID `json:"id" format:"uuid"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
|
@ -258,6 +284,75 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
|
|||
return agentConn, nil
|
||||
}
|
||||
|
||||
// WatchWorkspaceAgentMetadata watches the metadata of a workspace agent.
|
||||
// The returned channel will be closed when the context is canceled. Exactly
|
||||
// one error will be sent on the error channel. The metadata channel is never closed.
|
||||
func (c *Client) WatchWorkspaceAgentMetadata(ctx context.Context, id uuid.UUID) (<-chan []WorkspaceAgentMetadata, <-chan error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
metadataChan := make(chan []WorkspaceAgentMetadata, 256)
|
||||
|
||||
watch := func() error {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaceagents/%s/watch-metadata", id), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
nextEvent := ServerSentEventReader(ctx, res.Body)
|
||||
defer res.Body.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
sse, err := nextEvent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, ok := sse.Data.([]byte)
|
||||
if !ok {
|
||||
return xerrors.Errorf("unexpected data type: %T", sse.Data)
|
||||
}
|
||||
|
||||
switch sse.Type {
|
||||
case ServerSentEventTypeData:
|
||||
var met []WorkspaceAgentMetadata
|
||||
err = json.Unmarshal(b, &met)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unmarshal metadata: %w", err)
|
||||
}
|
||||
metadataChan <- met
|
||||
case ServerSentEventTypeError:
|
||||
var r Response
|
||||
err = json.Unmarshal(b, &r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unmarshal error: %w", err)
|
||||
}
|
||||
return xerrors.Errorf("%+v", r)
|
||||
default:
|
||||
return xerrors.Errorf("unexpected event type: %s", sse.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorChan := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errorChan)
|
||||
errorChan <- watch()
|
||||
}()
|
||||
|
||||
return metadataChan, errorChan
|
||||
}
|
||||
|
||||
// WorkspaceAgent returns an agent by ID.
|
||||
func (c *Client) WorkspaceAgent(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaceagents/%s", id), nil)
|
||||
|
|
|
@ -25,7 +25,7 @@ func TestWorkspaceAgentMetadata(t *testing.T) {
|
|||
// This test ensures that the DERP map returned properly
|
||||
// mutates built-in DERPs with the client access URL.
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(context.Background(), w, http.StatusOK, agentsdk.Metadata{
|
||||
httpapi.Write(context.Background(), w, http.StatusOK, agentsdk.Manifest{
|
||||
DERPMap: &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
1: {
|
||||
|
@ -43,9 +43,9 @@ func TestWorkspaceAgentMetadata(t *testing.T) {
|
|||
parsed, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
client := agentsdk.New(parsed)
|
||||
metadata, err := client.Metadata(context.Background())
|
||||
manifest, err := client.Manifest(context.Background())
|
||||
require.NoError(t, err)
|
||||
region := metadata.DERPMap.Regions[1]
|
||||
region := manifest.DERPMap.Regions[1]
|
||||
require.True(t, region.EmbeddedRelay)
|
||||
require.Len(t, region.Nodes, 1)
|
||||
node := region.Nodes[0]
|
||||
|
|
|
@ -273,18 +273,18 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/gitsshkey \
|
|||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get authorized workspace agent metadata
|
||||
## Get authorized workspace agent manifest
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/metadata \
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/manifest \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /workspaceagents/me/metadata`
|
||||
`GET /workspaceagents/me/manifest`
|
||||
|
||||
### Example responses
|
||||
|
||||
|
@ -368,6 +368,15 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/metadata \
|
|||
"property2": "string"
|
||||
},
|
||||
"git_auth_configs": 0,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"motd_file": "string",
|
||||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout": 0,
|
||||
|
@ -381,7 +390,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/metadata \
|
|||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [agentsdk.Metadata](schemas.md#agentsdkmetadata) |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [agentsdk.Manifest](schemas.md#agentsdkmanifest) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
| ---------------- | ------ | -------- | ------------ | ----------- |
|
||||
| `json_web_token` | string | true | | |
|
||||
|
||||
## agentsdk.Metadata
|
||||
## agentsdk.Manifest
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -174,6 +174,15 @@
|
|||
"property2": "string"
|
||||
},
|
||||
"git_auth_configs": 0,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
],
|
||||
"motd_file": "string",
|
||||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout": 0,
|
||||
|
@ -185,20 +194,21 @@
|
|||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------------- | ------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | |
|
||||
| `derpmap` | [tailcfg.DERPMap](#tailcfgderpmap) | false | | |
|
||||
| `directory` | string | false | | |
|
||||
| `environment_variables` | object | false | | |
|
||||
| » `[any property]` | string | false | | |
|
||||
| `git_auth_configs` | integer | false | | Git auth configs stores the number of Git configurations the Coder deployment has. If this number is >0, we set up special configuration in the workspace. |
|
||||
| `motd_file` | string | false | | |
|
||||
| `shutdown_script` | string | false | | |
|
||||
| `shutdown_script_timeout` | integer | false | | |
|
||||
| `startup_script` | string | false | | |
|
||||
| `startup_script_timeout` | integer | false | | |
|
||||
| `vscode_port_proxy_uri` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | |
|
||||
| `derpmap` | [tailcfg.DERPMap](#tailcfgderpmap) | false | | |
|
||||
| `directory` | string | false | | |
|
||||
| `environment_variables` | object | false | | |
|
||||
| » `[any property]` | string | false | | |
|
||||
| `git_auth_configs` | integer | false | | Git auth configs stores the number of Git configurations the Coder deployment has. If this number is >0, we set up special configuration in the workspace. |
|
||||
| `metadata` | array of [codersdk.WorkspaceAgentMetadataDescription](#codersdkworkspaceagentmetadatadescription) | false | | |
|
||||
| `motd_file` | string | false | | |
|
||||
| `shutdown_script` | string | false | | |
|
||||
| `shutdown_script_timeout` | integer | false | | |
|
||||
| `startup_script` | string | false | | |
|
||||
| `startup_script_timeout` | integer | false | | |
|
||||
| `vscode_port_proxy_uri` | string | false | | |
|
||||
|
||||
## agentsdk.PatchStartupLogs
|
||||
|
||||
|
@ -251,6 +261,26 @@
|
|||
| ------- | -------------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
|
||||
|
||||
## agentsdk.PostMetadataRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"age": 0,
|
||||
"collected_at": "2019-08-24T14:15:22Z",
|
||||
"error": "string",
|
||||
"value": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `age` | integer | false | | Age is the number of seconds since the metadata was collected. It is provided in addition to CollectedAt to protect against clock skew. |
|
||||
| `collected_at` | string | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `value` | string | false | | |
|
||||
|
||||
## agentsdk.PostStartupRequest
|
||||
|
||||
```json
|
||||
|
@ -4680,6 +4710,28 @@ Parameter represents a set value for the scope.
|
|||
| ------- | ------------------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `ports` | array of [codersdk.WorkspaceAgentListeningPort](#codersdkworkspaceagentlisteningport) | false | | If there are no ports in the list, nothing should be displayed in the UI. There must not be a "no ports available" message or anything similar, as there will always be no ports displayed on platforms where our port detection logic is unsupported. |
|
||||
|
||||
## codersdk.WorkspaceAgentMetadataDescription
|
||||
|
||||
```json
|
||||
{
|
||||
"display_name": "string",
|
||||
"interval": 0,
|
||||
"key": "string",
|
||||
"script": "string",
|
||||
"timeout": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------- | -------- | ------------ | ----------- |
|
||||
| `display_name` | string | false | | |
|
||||
| `interval` | integer | false | | |
|
||||
| `key` | string | false | | |
|
||||
| `script` | string | false | | |
|
||||
| `timeout` | integer | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentStartupLog
|
||||
|
||||
```json
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
|
@ -135,6 +135,13 @@
|
|||
"path": "./templates/resource-metadata.md",
|
||||
"icon_path": "./images/icons/table-rows.svg"
|
||||
},
|
||||
{
|
||||
"title": "Agent Metadata",
|
||||
"description": "Learn how to expose live agent information to users",
|
||||
"path": "./templates/agent-metadata.md",
|
||||
"icon_path": "./images/icons/table-rows.svg",
|
||||
"state": "alpha"
|
||||
},
|
||||
{
|
||||
"title": "Docker in Docker",
|
||||
"description": "Use docker inside containerized templates",
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# Agent Metadata (alpha)
|
||||
|
||||
<blockquote class="warning">
|
||||
Agent metadata is in an alpha state and may break or disappear at any time.
|
||||
</blockquote>
|
||||
|
||||
![agent-metadata](../images/agent-metadata.png)
|
||||
|
||||
With Agent Metadata, template admin can expose operational metrics from
|
||||
their workspaces to their users. It is a sibling of [Resource Metadata](./resource-metadata.md).
|
||||
|
||||
See the [Terraform reference](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent#metadata).
|
||||
|
||||
## Examples
|
||||
|
||||
All of these examples use [heredoc strings](https://developer.hashicorp.com/terraform/language/expressions/strings#heredoc-strings) for the script declaration. With heredoc strings you
|
||||
can script without messy escape codes, just as if you were working in your terminal.
|
||||
|
||||
Here are useful agent metadata snippets for Linux agents:
|
||||
|
||||
```hcl
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
...
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "cpu"
|
||||
# calculates CPU usage by summing the "us", "sy" and "id" columns of
|
||||
# vmstat.
|
||||
script = <<EOT
|
||||
vmstat | awk 'FNR==3 {printf "%2.0f%%", $13+$14+$16}'
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Disk Usage"
|
||||
key = "cpu"
|
||||
script = <<EOT
|
||||
df -h | awk -v mount="/" '$6 == mount { print $5 }'
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage"
|
||||
key = "mem"
|
||||
script = <<EOT
|
||||
free | awk '/^Mem/ { printf("%.0f%%", $4/$2 * 100.0) }'
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Load Average"
|
||||
key = "load"
|
||||
script = <<EOT
|
||||
awk '{print $1,$2,$3}' /proc/loadavg
|
||||
>>
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Utilities
|
||||
|
||||
[vmstat](https://linux.die.net/man/8/vmstat) is available in most Linux
|
||||
distributions and contains virtual memory, CPU and IO statistics. Running `vmstat`
|
||||
produces output that looks like:
|
||||
|
||||
```
|
||||
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
|
||||
r b swpd free buff cache si so bi bo in cs us sy id wa st
|
||||
0 0 19580 4781680 12133692 217646944 0 2 4 32 1 0 1 1 98 0 0
|
||||
```
|
||||
|
||||
[dstat](https://linux.die.net/man/1/dstat) is considerably more parseable
|
||||
than `vmstat` but often not included in base images. It is easily installed by
|
||||
most package managers under the name `dstat`. The output of running `dstat 1 1` looks
|
||||
like:
|
||||
|
||||
```
|
||||
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
|
||||
usr sys idl wai stl| read writ| recv send| in out | int csw
|
||||
1 1 98 0 0|3422k 25M| 0 0 | 153k 904k| 123k 174k
|
||||
```
|
|
@ -97,6 +97,28 @@ To make easier for you to customize your resource we added some built-in icons:
|
|||
|
||||
We also have other icons related to the IDEs. You can see all the icons [here](https://github.com/coder/coder/tree/main/site/static/icon).
|
||||
|
||||
## Agent Metadata
|
||||
|
||||
In cases where you want to present automatically updating, dynamic values. You
|
||||
can use the `metadata` block in the `coder_agent` resource. For example:
|
||||
|
||||
```hcl
|
||||
resource "coder_agent" "dev" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/workspace"
|
||||
metadata {
|
||||
name = "Process Count"
|
||||
script = "ps aux | wc -l"
|
||||
interval = 1
|
||||
timeout = 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Read more [here](./agent-metadata.md).
|
||||
|
||||
## Up next
|
||||
|
||||
- Learn about [secrets](../secrets.md)
|
||||
- Learn about [Agent Metadata](../agent-metadata.md)
|
||||
|
|
|
@ -14,6 +14,14 @@ import (
|
|||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
type agentMetadata struct {
|
||||
Key string `mapstructure:"key"`
|
||||
DisplayName string `mapstructure:"display_name"`
|
||||
Script string `mapstructure:"script"`
|
||||
Interval int64 `mapstructure:"interval"`
|
||||
Timeout int64 `mapstructure:"timeout"`
|
||||
}
|
||||
|
||||
// A mapping of attributes on the "coder_agent" resource.
|
||||
type agentAttributes struct {
|
||||
Auth string `mapstructure:"auth"`
|
||||
|
@ -31,6 +39,7 @@ type agentAttributes struct {
|
|||
StartupScriptTimeoutSeconds int32 `mapstructure:"startup_script_timeout"`
|
||||
ShutdownScript string `mapstructure:"shutdown_script"`
|
||||
ShutdownScriptTimeoutSeconds int32 `mapstructure:"shutdown_script_timeout"`
|
||||
Metadata []agentMetadata `mapstructure:"metadata"`
|
||||
}
|
||||
|
||||
// A mapping of attributes on the "coder_app" resource.
|
||||
|
@ -59,15 +68,15 @@ type appHealthcheckAttributes struct {
|
|||
}
|
||||
|
||||
// A mapping of attributes on the "coder_metadata" resource.
|
||||
type metadataAttributes struct {
|
||||
ResourceID string `mapstructure:"resource_id"`
|
||||
Hide bool `mapstructure:"hide"`
|
||||
Icon string `mapstructure:"icon"`
|
||||
DailyCost int32 `mapstructure:"daily_cost"`
|
||||
Items []metadataItem `mapstructure:"item"`
|
||||
type resourceMetadataAttributes struct {
|
||||
ResourceID string `mapstructure:"resource_id"`
|
||||
Hide bool `mapstructure:"hide"`
|
||||
Icon string `mapstructure:"icon"`
|
||||
DailyCost int32 `mapstructure:"daily_cost"`
|
||||
Items []resourceMetadataItem `mapstructure:"item"`
|
||||
}
|
||||
|
||||
type metadataItem struct {
|
||||
type resourceMetadataItem struct {
|
||||
Key string `mapstructure:"key"`
|
||||
Value string `mapstructure:"value"`
|
||||
Sensitive bool `mapstructure:"sensitive"`
|
||||
|
@ -148,6 +157,17 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNa
|
|||
loginBeforeReady = attrs.LoginBeforeReady
|
||||
}
|
||||
|
||||
var metadata []*proto.Agent_Metadata
|
||||
for _, item := range attrs.Metadata {
|
||||
metadata = append(metadata, &proto.Agent_Metadata{
|
||||
Key: item.Key,
|
||||
DisplayName: item.DisplayName,
|
||||
Script: item.Script,
|
||||
Interval: item.Interval,
|
||||
Timeout: item.Timeout,
|
||||
})
|
||||
}
|
||||
|
||||
agent := &proto.Agent{
|
||||
Name: tfResource.Name,
|
||||
Id: attrs.ID,
|
||||
|
@ -163,6 +183,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNa
|
|||
StartupScriptTimeoutSeconds: attrs.StartupScriptTimeoutSeconds,
|
||||
ShutdownScript: attrs.ShutdownScript,
|
||||
ShutdownScriptTimeoutSeconds: attrs.ShutdownScriptTimeoutSeconds,
|
||||
Metadata: metadata,
|
||||
}
|
||||
switch attrs.Auth {
|
||||
case "token":
|
||||
|
@ -356,7 +377,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNa
|
|||
continue
|
||||
}
|
||||
|
||||
var attrs metadataAttributes
|
||||
var attrs resourceMetadataAttributes
|
||||
err = mapstructure.Decode(resource.AttributeValues, &attrs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("decode metadata attributes: %w", err)
|
||||
|
|
|
@ -221,6 +221,23 @@ func TestConvertResources(t *testing.T) {
|
|||
Value: "squirrel",
|
||||
Sensitive: true,
|
||||
}},
|
||||
Agents: []*proto.Agent{{
|
||||
Name: "main",
|
||||
Auth: &proto.Agent_Token{},
|
||||
OperatingSystem: "linux",
|
||||
Architecture: "amd64",
|
||||
Metadata: []*proto.Agent_Metadata{{
|
||||
Key: "process_count",
|
||||
DisplayName: "Process Count",
|
||||
Script: "ps -ef | wc -l",
|
||||
Interval: 5,
|
||||
Timeout: 1,
|
||||
}},
|
||||
ShutdownScriptTimeoutSeconds: 300,
|
||||
StartupScriptTimeoutSeconds: 300,
|
||||
LoginBeforeReady: true,
|
||||
ConnectionTimeoutSeconds: 120,
|
||||
}},
|
||||
}},
|
||||
},
|
||||
// Tests that resources with the same id correctly get metadata applied
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "411bdd93-0ea4-4376-a032-52b1fbf44ca5",
|
||||
"id": "2d84db09-c3c3-4272-8119-1847e68e2dbe",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "eeac85aa-19f9-4a50-8002-dfd11556081b",
|
||||
"token": "d5d46d6a-5e4b-493f-9b68-dd8c78727cac",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -46,7 +46,7 @@
|
|||
"outputs": {
|
||||
"script": ""
|
||||
},
|
||||
"random": "5816533441722838433"
|
||||
"random": "6336115403873146745"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"inputs": {},
|
||||
|
@ -61,7 +61,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "5594550025354402054",
|
||||
"id": "3671991160538447687",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
2
provisioner/terraform/testdata/chaining-resources/chaining-resources.tfplan.json
generated
vendored
2
provisioner/terraform/testdata/chaining-resources/chaining-resources.tfplan.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
|
10
provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json
generated
vendored
10
provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "4dc52ff5-b270-47a2-8b6a-695b4872f07b",
|
||||
"id": "36c5629c-8ac4-4e6b-ae6e-94b8f1bbb0bc",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "c5c8378e-66df-4f3f-94a2-84bff1dc6fc9",
|
||||
"token": "9ed706cc-8b11-4b05-b6c3-ea943af2c60c",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -34,7 +34,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "7372487656283423086",
|
||||
"id": "4876594853562919335",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
@ -51,7 +51,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "2553224683756509362",
|
||||
"id": "3012307200839131913",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
|
10
provisioner/terraform/testdata/conflicting-resources/conflicting-resources.tfstate.json
generated
vendored
10
provisioner/terraform/testdata/conflicting-resources/conflicting-resources.tfstate.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "3cd9cbba-31f7-482c-a8a0-bf39dfe42dc2",
|
||||
"id": "a79d53f2-f616-4be2-b1bf-6f6ce7409e23",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "8b063f22-9e66-4dbf-9f13-7b09ac2a470f",
|
||||
"token": "bb333041-5225-490d-86a0-69d1b5afd0db",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -34,7 +34,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "3370347998754925285",
|
||||
"id": "3651093174168180448",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
@ -50,7 +50,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "4707694957868093590",
|
||||
"id": "3820948743929828676",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
6
provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json
generated
vendored
6
provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json
generated
vendored
|
@ -17,7 +17,7 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "78b29f93-097d-403b-ab56-0bc943d427cc",
|
||||
"id": "7060319c-a8bf-44b7-8fb9-5a4dc3bd35fe",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"motd_file": null,
|
||||
|
@ -26,7 +26,7 @@
|
|||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"token": "a57838e5-355c-471a-9a85-f81314fbaec6",
|
||||
"token": "9ea68982-fd51-4510-9c23-9e9ad1ce1ef7",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -65,7 +65,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "1416347524569828366",
|
||||
"id": "4918728983070449527",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.6",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "36189f12-6eed-4094-9179-6584a8659219",
|
||||
"id": "facfb2ad-417d-412c-8304-ce50ff8a886e",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "907fa482-fd3b-44be-8cfb-4515e3122e78",
|
||||
"token": "f1b3312a-af1c-45a8-9695-23d92c4cb68d",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -34,8 +34,8 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "36189f12-6eed-4094-9179-6584a8659219",
|
||||
"id": "c9bd849e-ac37-440b-9c5b-a288344be41c",
|
||||
"agent_id": "facfb2ad-417d-412c-8304-ce50ff8a886e",
|
||||
"id": "6ce4c6f4-1b93-4b22-9b17-1dcb50a36e77",
|
||||
"instance_id": "example"
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
@ -51,7 +51,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "4399071137990404376",
|
||||
"id": "2761080386857837319",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "5cb3c105-1a70-4c60-bd32-b75d9a60a98d",
|
||||
"id": "441cfb02-3044-4d2b-ac2c-1ac150ec8c58",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "b477690f-0a2d-4d9b-818e-7b60c845c44f",
|
||||
"token": "75ee44b6-4cbb-4615-acb2-19c53f82dc58",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -35,12 +35,12 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "5cb3c105-1a70-4c60-bd32-b75d9a60a98d",
|
||||
"agent_id": "441cfb02-3044-4d2b-ac2c-1ac150ec8c58",
|
||||
"command": null,
|
||||
"display_name": "app1",
|
||||
"healthcheck": [],
|
||||
"icon": null,
|
||||
"id": "72d41f36-d775-424c-9a05-b633da43cd58",
|
||||
"id": "abbc4449-5dd1-4ee0-ac58-3055a6da206b",
|
||||
"name": null,
|
||||
"relative_path": null,
|
||||
"share": "owner",
|
||||
|
@ -64,12 +64,12 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "5cb3c105-1a70-4c60-bd32-b75d9a60a98d",
|
||||
"agent_id": "441cfb02-3044-4d2b-ac2c-1ac150ec8c58",
|
||||
"command": null,
|
||||
"display_name": "app2",
|
||||
"healthcheck": [],
|
||||
"icon": null,
|
||||
"id": "810dbeda-3041-403d-86e8-146354ad2657",
|
||||
"id": "ff87411e-4edb-48ce-b62f-6f792d02b25c",
|
||||
"name": null,
|
||||
"relative_path": null,
|
||||
"share": "owner",
|
||||
|
@ -92,7 +92,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "1955786418433284816",
|
||||
"id": "3464468981645058362",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "6b912abe-50d4-48b2-be7c-1464ca69b5b9",
|
||||
"id": "7fd8bb3f-704e-4d85-aaaf-1928a9a4df83",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"motd_file": null,
|
||||
|
@ -26,7 +26,7 @@
|
|||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"token": "d296a9cd-6f7c-4c6b-b2f3-7a647512efe8",
|
||||
"token": "ebdd904c-a277-49ec-97cc-e29c7326b475",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -44,7 +44,7 @@
|
|||
"connection_timeout": 1,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "8a2956f7-d37b-441e-bf62-bd9a45316f6a",
|
||||
"id": "cd35b6c2-3f81-4857-ac8c-9cc0d1d0f0ee",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"motd_file": "/etc/motd",
|
||||
|
@ -53,7 +53,7 @@
|
|||
"shutdown_script_timeout": 30,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 30,
|
||||
"token": "b1e0fba4-5bba-439f-b3ea-3f6a8ba4d301",
|
||||
"token": "bbfc3bb1-31c8-42a2-bc5a-3f4ee95eea7b",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -71,7 +71,7 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "819b1b19-a709-463e-9aeb-5e1321b7af23",
|
||||
"id": "7407c159-30e7-4c7c-8187-3bf0f6805515",
|
||||
"init_script": "",
|
||||
"login_before_ready": false,
|
||||
"motd_file": null,
|
||||
|
@ -80,7 +80,7 @@
|
|||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"token": "238ff017-12ae-403f-b3f8-4dea4dc87a7d",
|
||||
"token": "080070d7-cb08-4634-aa05-7ee07a193441",
|
||||
"troubleshooting_url": "https://coder.com/troubleshoot"
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -93,7 +93,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "5288433022262248914",
|
||||
"id": "235602507221507275",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.3",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.3",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,11 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "f911bd98-54fc-476a-aec1-df6e525630a9",
|
||||
"id": "54519a12-e34b-4c4f-aef9-7dfac5f4949b",
|
||||
"init_script": "",
|
||||
"os": "linux",
|
||||
"startup_script": null,
|
||||
"token": "fa05ad9c-2062-4707-a27f-12364c89641e",
|
||||
"token": "bf339e89-0594-4f44-83f0-fc7cde9ceb0c",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
|
@ -34,12 +34,12 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "f911bd98-54fc-476a-aec1-df6e525630a9",
|
||||
"agent_id": "54519a12-e34b-4c4f-aef9-7dfac5f4949b",
|
||||
"command": null,
|
||||
"display_name": null,
|
||||
"healthcheck": [],
|
||||
"icon": null,
|
||||
"id": "038d0f6c-90b7-465b-915a-8a9f0cf21757",
|
||||
"id": "13101247-bdf1-409e-81e2-51a4ff45576b",
|
||||
"name": null,
|
||||
"relative_path": null,
|
||||
"share": "owner",
|
||||
|
@ -62,7 +62,7 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "f911bd98-54fc-476a-aec1-df6e525630a9",
|
||||
"agent_id": "54519a12-e34b-4c4f-aef9-7dfac5f4949b",
|
||||
"command": null,
|
||||
"display_name": null,
|
||||
"healthcheck": [
|
||||
|
@ -73,7 +73,7 @@
|
|||
}
|
||||
],
|
||||
"icon": null,
|
||||
"id": "c00ec121-a167-4418-8c4e-2ccae0a0cd6e",
|
||||
"id": "ef508497-0437-43eb-b773-c0622582ab5d",
|
||||
"name": null,
|
||||
"relative_path": null,
|
||||
"share": "owner",
|
||||
|
@ -98,12 +98,12 @@
|
|||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"agent_id": "f911bd98-54fc-476a-aec1-df6e525630a9",
|
||||
"agent_id": "54519a12-e34b-4c4f-aef9-7dfac5f4949b",
|
||||
"command": null,
|
||||
"display_name": null,
|
||||
"healthcheck": [],
|
||||
"icon": null,
|
||||
"id": "e9226aa6-a1a6-42a7-8557-64620cbf3dc2",
|
||||
"id": "2c187306-80cc-46ba-a75c-42d4648ff94a",
|
||||
"name": null,
|
||||
"relative_path": null,
|
||||
"share": "owner",
|
||||
|
@ -126,7 +126,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "5577006791947779410",
|
||||
"id": "1264552698255765246",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -2,7 +2,7 @@ terraform {
|
|||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,20 @@ terraform {
|
|||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
metadata {
|
||||
key = "process_count"
|
||||
display_name = "Process Count"
|
||||
script = "ps -ef | wc -l"
|
||||
interval = 5
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
|
||||
resource "null_resource" "about" {}
|
||||
resource "null_resource" "about" {
|
||||
depends_on = [
|
||||
coder_agent.main,
|
||||
]
|
||||
}
|
||||
|
||||
resource "coder_metadata" "about_info" {
|
||||
resource_id = null_resource.about.id
|
||||
|
|
|
@ -9,9 +9,8 @@ digraph {
|
|||
"[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"]
|
||||
"[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
|
||||
"[root] coder_metadata.about_info (expand)" -> "[root] null_resource.about (expand)"
|
||||
"[root] coder_metadata.about_info (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
|
||||
"[root] null_resource.about (expand)" -> "[root] coder_agent.main (expand)"
|
||||
"[root] null_resource.about (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]"
|
||||
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.main (expand)"
|
||||
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_metadata.about_info (expand)"
|
||||
"[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.about (expand)"
|
||||
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
|
||||
|
|
68
provisioner/terraform/testdata/resource-metadata/resource-metadata.tfplan.json
generated
vendored
68
provisioner/terraform/testdata/resource-metadata/resource-metadata.tfplan.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.3.3",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,11 +17,29 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"login_before_ready": true,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "Process Count",
|
||||
"interval": 5,
|
||||
"key": "process_count",
|
||||
"script": "ps -ef | wc -l",
|
||||
"timeout": 1
|
||||
}
|
||||
],
|
||||
"motd_file": null,
|
||||
"os": "linux",
|
||||
"shutdown_script": null,
|
||||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
"sensitive_values": {
|
||||
"metadata": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "coder_metadata.about_info",
|
||||
|
@ -99,17 +117,37 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"login_before_ready": true,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "Process Count",
|
||||
"interval": 5,
|
||||
"key": "process_count",
|
||||
"script": "ps -ef | wc -l",
|
||||
"timeout": 1
|
||||
}
|
||||
],
|
||||
"motd_file": null,
|
||||
"os": "linux",
|
||||
"shutdown_script": null,
|
||||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"after_unknown": {
|
||||
"id": true,
|
||||
"init_script": true,
|
||||
"metadata": [
|
||||
{}
|
||||
],
|
||||
"token": true
|
||||
},
|
||||
"before_sensitive": false,
|
||||
"after_sensitive": {
|
||||
"metadata": [
|
||||
{}
|
||||
],
|
||||
"token": true
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +246,7 @@
|
|||
"coder": {
|
||||
"name": "coder",
|
||||
"full_name": "registry.terraform.io/coder/coder",
|
||||
"version_constraint": "0.6.3"
|
||||
"version_constraint": "0.7.0-rc0"
|
||||
},
|
||||
"null": {
|
||||
"name": "null",
|
||||
|
@ -227,6 +265,25 @@
|
|||
"arch": {
|
||||
"constant_value": "amd64"
|
||||
},
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": {
|
||||
"constant_value": "Process Count"
|
||||
},
|
||||
"interval": {
|
||||
"constant_value": 5
|
||||
},
|
||||
"key": {
|
||||
"constant_value": "process_count"
|
||||
},
|
||||
"script": {
|
||||
"constant_value": "ps -ef | wc -l"
|
||||
},
|
||||
"timeout": {
|
||||
"constant_value": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"os": {
|
||||
"constant_value": "linux"
|
||||
}
|
||||
|
@ -298,7 +355,10 @@
|
|||
"type": "null_resource",
|
||||
"name": "about",
|
||||
"provider_config_key": "null",
|
||||
"schema_version": 0
|
||||
"schema_version": 0,
|
||||
"depends_on": [
|
||||
"coder_agent.main"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -9,9 +9,8 @@ digraph {
|
|||
"[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"]
|
||||
"[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
|
||||
"[root] coder_metadata.about_info (expand)" -> "[root] null_resource.about (expand)"
|
||||
"[root] coder_metadata.about_info (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
|
||||
"[root] null_resource.about (expand)" -> "[root] coder_agent.main (expand)"
|
||||
"[root] null_resource.about (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]"
|
||||
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.main (expand)"
|
||||
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_metadata.about_info (expand)"
|
||||
"[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.about (expand)"
|
||||
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
|
||||
|
|
38
provisioner/terraform/testdata/resource-metadata/resource-metadata.tfstate.json
generated
vendored
38
provisioner/terraform/testdata/resource-metadata/resource-metadata.tfstate.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.3.3",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -17,14 +17,32 @@
|
|||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "7766b2a9-c00f-4cde-9acc-1fc05651dbdf",
|
||||
"id": "2090d6c6-c4c1-4219-b8f8-9df6db6d5864",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"metadata": [
|
||||
{
|
||||
"display_name": "Process Count",
|
||||
"interval": 5,
|
||||
"key": "process_count",
|
||||
"script": "ps -ef | wc -l",
|
||||
"timeout": 1
|
||||
}
|
||||
],
|
||||
"motd_file": null,
|
||||
"os": "linux",
|
||||
"shutdown_script": null,
|
||||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"token": "5e54c173-a813-4df0-b87d-0617082769dc",
|
||||
"startup_script_timeout": 300,
|
||||
"token": "a984d85d-eff6-4366-9658-9719fb3dd82e",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
"sensitive_values": {
|
||||
"metadata": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "coder_metadata.about_info",
|
||||
|
@ -37,7 +55,7 @@
|
|||
"daily_cost": 29,
|
||||
"hide": true,
|
||||
"icon": "/icon/server.svg",
|
||||
"id": "e43f1cd6-5dbb-4d6b-8942-37f914b37be5",
|
||||
"id": "9e239cb2-e381-423a-bf16-74ece8254eff",
|
||||
"item": [
|
||||
{
|
||||
"is_null": false,
|
||||
|
@ -64,7 +82,7 @@
|
|||
"value": "squirrel"
|
||||
}
|
||||
],
|
||||
"resource_id": "5577006791947779410"
|
||||
"resource_id": "3104684855633455084"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"item": [
|
||||
|
@ -75,6 +93,7 @@
|
|||
]
|
||||
},
|
||||
"depends_on": [
|
||||
"coder_agent.main",
|
||||
"null_resource.about"
|
||||
]
|
||||
},
|
||||
|
@ -86,10 +105,13 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "5577006791947779410",
|
||||
"id": "3104684855633455084",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
"sensitive_values": {},
|
||||
"depends_on": [
|
||||
"coder_agent.main"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"format_version": "1.1",
|
||||
"terraform_version": "1.4.0",
|
||||
"terraform_version": "1.3.7",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -105,7 +105,7 @@
|
|||
],
|
||||
"prior_state": {
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.4.0",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
|
@ -120,7 +120,7 @@
|
|||
"default": null,
|
||||
"description": null,
|
||||
"icon": null,
|
||||
"id": "5820e575-2637-4830-b6a3-75f4c664b447",
|
||||
"id": "857b1591-ee42-4ded-9804-783ffd1eb180",
|
||||
"legacy_variable": null,
|
||||
"legacy_variable_name": null,
|
||||
"mutable": false,
|
||||
|
@ -162,7 +162,7 @@
|
|||
"default": "ok",
|
||||
"description": "blah blah",
|
||||
"icon": null,
|
||||
"id": "9cac2300-0618-45f6-97d9-2f0395b1a0b4",
|
||||
"id": "1477c44d-b36a-48cd-9942-0b532f1791db",
|
||||
"legacy_variable": null,
|
||||
"legacy_variable_name": null,
|
||||
"mutable": false,
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
{
|
||||
"format_version": "1.0",
|
||||
"terraform_version": "1.4.0",
|
||||
"terraform_version": "1.3.7",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "coder_agent.dev",
|
||||
"mode": "managed",
|
||||
"type": "coder_agent",
|
||||
"name": "dev",
|
||||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"arch": "arm64",
|
||||
"auth": "token",
|
||||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "c2221717-e813-49f0-a655-4cb7aa5265e2",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"motd_file": null,
|
||||
"os": "windows",
|
||||
"shutdown_script": null,
|
||||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"token": "fdb94db8-fca1-4a13-bbcb-73bfaec95b77",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "data.coder_parameter.example",
|
||||
"mode": "data",
|
||||
|
@ -15,7 +42,7 @@
|
|||
"default": null,
|
||||
"description": null,
|
||||
"icon": null,
|
||||
"id": "30b8b963-684d-4c11-9230-a06b81473f6f",
|
||||
"id": "f5f644c9-cb0c-47b1-8e02-d9f6fa99b935",
|
||||
"legacy_variable": null,
|
||||
"legacy_variable_name": null,
|
||||
"mutable": false,
|
||||
|
@ -57,7 +84,7 @@
|
|||
"default": "ok",
|
||||
"description": "blah blah",
|
||||
"icon": null,
|
||||
"id": "c40e87d2-7694-40f7-8b7d-30dbf14dd0c0",
|
||||
"id": "e2944252-1c30-43c8-9ce3-53a9755030dc",
|
||||
"legacy_variable": null,
|
||||
"legacy_variable_name": null,
|
||||
"mutable": false,
|
||||
|
@ -70,33 +97,6 @@
|
|||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "coder_agent.dev",
|
||||
"mode": "managed",
|
||||
"type": "coder_agent",
|
||||
"name": "dev",
|
||||
"provider_name": "registry.terraform.io/coder/coder",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"arch": "arm64",
|
||||
"auth": "token",
|
||||
"connection_timeout": 120,
|
||||
"dir": null,
|
||||
"env": null,
|
||||
"id": "775b9977-421e-4d4d-8c02-dc38958259e3",
|
||||
"init_script": "",
|
||||
"login_before_ready": true,
|
||||
"motd_file": null,
|
||||
"os": "windows",
|
||||
"shutdown_script": null,
|
||||
"shutdown_script_timeout": 300,
|
||||
"startup_script": null,
|
||||
"startup_script_timeout": 300,
|
||||
"token": "927e1872-90d0-43a2-9a55-a8438ead0ad3",
|
||||
"troubleshooting_url": null
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "null_resource.dev",
|
||||
"mode": "managed",
|
||||
|
@ -105,7 +105,7 @@
|
|||
"provider_name": "registry.terraform.io/hashicorp/null",
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"id": "3727779938861599093",
|
||||
"id": "5032149403215603103",
|
||||
"triggers": null
|
||||
},
|
||||
"sensitive_values": {},
|
||||
|
|
|
@ -1252,14 +1252,15 @@ type Agent struct {
|
|||
//
|
||||
// *Agent_Token
|
||||
// *Agent_InstanceId
|
||||
Auth isAgent_Auth `protobuf_oneof:"auth"`
|
||||
ConnectionTimeoutSeconds int32 `protobuf:"varint,11,opt,name=connection_timeout_seconds,json=connectionTimeoutSeconds,proto3" json:"connection_timeout_seconds,omitempty"`
|
||||
TroubleshootingUrl string `protobuf:"bytes,12,opt,name=troubleshooting_url,json=troubleshootingUrl,proto3" json:"troubleshooting_url,omitempty"`
|
||||
MotdFile string `protobuf:"bytes,13,opt,name=motd_file,json=motdFile,proto3" json:"motd_file,omitempty"`
|
||||
LoginBeforeReady bool `protobuf:"varint,14,opt,name=login_before_ready,json=loginBeforeReady,proto3" json:"login_before_ready,omitempty"`
|
||||
StartupScriptTimeoutSeconds int32 `protobuf:"varint,15,opt,name=startup_script_timeout_seconds,json=startupScriptTimeoutSeconds,proto3" json:"startup_script_timeout_seconds,omitempty"`
|
||||
ShutdownScript string `protobuf:"bytes,16,opt,name=shutdown_script,json=shutdownScript,proto3" json:"shutdown_script,omitempty"`
|
||||
ShutdownScriptTimeoutSeconds int32 `protobuf:"varint,17,opt,name=shutdown_script_timeout_seconds,json=shutdownScriptTimeoutSeconds,proto3" json:"shutdown_script_timeout_seconds,omitempty"`
|
||||
Auth isAgent_Auth `protobuf_oneof:"auth"`
|
||||
ConnectionTimeoutSeconds int32 `protobuf:"varint,11,opt,name=connection_timeout_seconds,json=connectionTimeoutSeconds,proto3" json:"connection_timeout_seconds,omitempty"`
|
||||
TroubleshootingUrl string `protobuf:"bytes,12,opt,name=troubleshooting_url,json=troubleshootingUrl,proto3" json:"troubleshooting_url,omitempty"`
|
||||
MotdFile string `protobuf:"bytes,13,opt,name=motd_file,json=motdFile,proto3" json:"motd_file,omitempty"`
|
||||
LoginBeforeReady bool `protobuf:"varint,14,opt,name=login_before_ready,json=loginBeforeReady,proto3" json:"login_before_ready,omitempty"`
|
||||
StartupScriptTimeoutSeconds int32 `protobuf:"varint,15,opt,name=startup_script_timeout_seconds,json=startupScriptTimeoutSeconds,proto3" json:"startup_script_timeout_seconds,omitempty"`
|
||||
ShutdownScript string `protobuf:"bytes,16,opt,name=shutdown_script,json=shutdownScript,proto3" json:"shutdown_script,omitempty"`
|
||||
ShutdownScriptTimeoutSeconds int32 `protobuf:"varint,17,opt,name=shutdown_script_timeout_seconds,json=shutdownScriptTimeoutSeconds,proto3" json:"shutdown_script_timeout_seconds,omitempty"`
|
||||
Metadata []*Agent_Metadata `protobuf:"bytes,18,rep,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Agent) Reset() {
|
||||
|
@ -1420,6 +1421,13 @@ func (x *Agent) GetShutdownScriptTimeoutSeconds() int32 {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (x *Agent) GetMetadata() []*Agent_Metadata {
|
||||
if x != nil {
|
||||
return x.Metadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isAgent_Auth interface {
|
||||
isAgent_Auth()
|
||||
}
|
||||
|
@ -1797,6 +1805,85 @@ func (*Provision) Descriptor() ([]byte, []int) {
|
|||
return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
type Agent_Metadata struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
|
||||
Script string `protobuf:"bytes,3,opt,name=script,proto3" json:"script,omitempty"`
|
||||
Interval int64 `protobuf:"varint,4,opt,name=interval,proto3" json:"interval,omitempty"`
|
||||
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) Reset() {
|
||||
*x = Agent_Metadata{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Agent_Metadata) ProtoMessage() {}
|
||||
|
||||
func (x *Agent_Metadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Agent_Metadata.ProtoReflect.Descriptor instead.
|
||||
func (*Agent_Metadata) Descriptor() ([]byte, []int) {
|
||||
return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13, 0}
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) GetDisplayName() string {
|
||||
if x != nil {
|
||||
return x.DisplayName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) GetScript() string {
|
||||
if x != nil {
|
||||
return x.Script
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) GetInterval() int64 {
|
||||
if x != nil {
|
||||
return x.Interval
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Agent_Metadata) GetTimeout() int64 {
|
||||
if x != nil {
|
||||
return x.Timeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Resource_Metadata struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
@ -1811,7 +1898,7 @@ type Resource_Metadata struct {
|
|||
func (x *Resource_Metadata) Reset() {
|
||||
*x = Resource_Metadata{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -1824,7 +1911,7 @@ func (x *Resource_Metadata) String() string {
|
|||
func (*Resource_Metadata) ProtoMessage() {}
|
||||
|
||||
func (x *Resource_Metadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -1879,7 +1966,7 @@ type Parse_Request struct {
|
|||
func (x *Parse_Request) Reset() {
|
||||
*x = Parse_Request{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -1892,7 +1979,7 @@ func (x *Parse_Request) String() string {
|
|||
func (*Parse_Request) ProtoMessage() {}
|
||||
|
||||
func (x *Parse_Request) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -1927,7 +2014,7 @@ type Parse_Complete struct {
|
|||
func (x *Parse_Complete) Reset() {
|
||||
*x = Parse_Complete{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -1940,7 +2027,7 @@ func (x *Parse_Complete) String() string {
|
|||
func (*Parse_Complete) ProtoMessage() {}
|
||||
|
||||
func (x *Parse_Complete) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -1985,7 +2072,7 @@ type Parse_Response struct {
|
|||
func (x *Parse_Response) Reset() {
|
||||
*x = Parse_Response{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -1998,7 +2085,7 @@ func (x *Parse_Response) String() string {
|
|||
func (*Parse_Response) ProtoMessage() {}
|
||||
|
||||
func (x *Parse_Response) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2071,7 +2158,7 @@ type Provision_Metadata struct {
|
|||
func (x *Provision_Metadata) Reset() {
|
||||
*x = Provision_Metadata{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2084,7 +2171,7 @@ func (x *Provision_Metadata) String() string {
|
|||
func (*Provision_Metadata) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Metadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2186,7 +2273,7 @@ type Provision_Config struct {
|
|||
func (x *Provision_Config) Reset() {
|
||||
*x = Provision_Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2199,7 +2286,7 @@ func (x *Provision_Config) String() string {
|
|||
func (*Provision_Config) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2258,7 +2345,7 @@ type Provision_Plan struct {
|
|||
func (x *Provision_Plan) Reset() {
|
||||
*x = Provision_Plan{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2271,7 +2358,7 @@ func (x *Provision_Plan) String() string {
|
|||
func (*Provision_Plan) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Plan) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2334,7 +2421,7 @@ type Provision_Apply struct {
|
|||
func (x *Provision_Apply) Reset() {
|
||||
*x = Provision_Apply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2347,7 +2434,7 @@ func (x *Provision_Apply) String() string {
|
|||
func (*Provision_Apply) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Apply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2386,7 +2473,7 @@ type Provision_Cancel struct {
|
|||
func (x *Provision_Cancel) Reset() {
|
||||
*x = Provision_Cancel{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2399,7 +2486,7 @@ func (x *Provision_Cancel) String() string {
|
|||
func (*Provision_Cancel) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Cancel) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2431,7 +2518,7 @@ type Provision_Request struct {
|
|||
func (x *Provision_Request) Reset() {
|
||||
*x = Provision_Request{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2444,7 +2531,7 @@ func (x *Provision_Request) String() string {
|
|||
func (*Provision_Request) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Request) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2526,7 +2613,7 @@ type Provision_Complete struct {
|
|||
func (x *Provision_Complete) Reset() {
|
||||
*x = Provision_Complete{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2539,7 +2626,7 @@ func (x *Provision_Complete) String() string {
|
|||
func (*Provision_Complete) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Complete) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2612,7 +2699,7 @@ type Provision_Response struct {
|
|||
func (x *Provision_Response) Reset() {
|
||||
*x = Provision_Response{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -2625,7 +2712,7 @@ func (x *Provision_Response) String() string {
|
|||
func (*Provision_Response) ProtoMessage() {}
|
||||
|
||||
func (x *Provision_Response) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
|
||||
mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -2827,7 +2914,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
|
|||
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
||||
0x22, 0xfe, 0x05, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x22, 0xc7, 0x07, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d,
|
||||
0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72,
|
||||
|
@ -2871,210 +2958,223 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
|
|||
0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x11, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x1c, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x53, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
|
||||
0x73, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74,
|
||||
0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75,
|
||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
|
||||
0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e,
|
||||
0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a,
|
||||
0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68,
|
||||
0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68,
|
||||
0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
|
||||
0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61,
|
||||
0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68,
|
||||
0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73,
|
||||
0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73,
|
||||
0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d,
|
||||
0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a,
|
||||
0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12,
|
||||
0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0xcb, 0x02, 0x0a, 0x05, 0x50, 0x61, 0x72,
|
||||
0x73, 0x65, 0x1a, 0x27, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
|
||||
0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0xa3, 0x01, 0x0a, 0x08,
|
||||
0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72,
|
||||
0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52,
|
||||
0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
|
||||
0x73, 0x1a, 0x73, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a,
|
||||
0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03,
|
||||
0x6c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x90, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x03, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53,
|
||||
0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13,
|
||||
0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
|
||||
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72,
|
||||
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77,
|
||||
0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
|
||||
0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e,
|
||||
0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
|
||||
0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77,
|
||||
0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a,
|
||||
0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63,
|
||||
0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77,
|
||||
0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b,
|
||||
0x65, 0x6e, 0x1a, 0xad, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a,
|
||||
0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73,
|
||||
0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
|
||||
0x65, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32,
|
||||
0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f,
|
||||
0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76,
|
||||
0x65, 0x6c, 0x1a, 0xeb, 0x02, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69,
|
||||
0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12,
|
||||
0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47,
|
||||
0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10,
|
||||
0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73,
|
||||
0x1a, 0x52, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
|
||||
0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x1a, 0xb3,
|
||||
0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x6c,
|
||||
0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x34, 0x0a,
|
||||
0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70,
|
||||
0x73, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x8d, 0x01, 0x0a, 0x08, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73,
|
||||
0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e,
|
||||
0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||
0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41,
|
||||
0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
|
||||
0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69,
|
||||
0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75,
|
||||
0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74,
|
||||
0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||
0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68,
|
||||
0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c,
|
||||
0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72,
|
||||
0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e,
|
||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||
0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02,
|
||||
0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79,
|
||||
0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a,
|
||||
0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69,
|
||||
0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63,
|
||||
0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61,
|
||||
0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79,
|
||||
0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69,
|
||||
0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65,
|
||||
0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73,
|
||||
0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e,
|
||||
0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c,
|
||||
0x6c, 0x22, 0xcb, 0x02, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x1a, 0x27, 0x0a, 0x07, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61,
|
||||
0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65,
|
||||
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12,
|
||||
0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68,
|
||||
0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
|
||||
0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x1a, 0x73, 0x0a, 0x08, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x08,
|
||||
0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72,
|
||||
0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63,
|
||||
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
|
||||
0x90, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x03,
|
||||
0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
|
||||
0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e,
|
||||
0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
|
||||
0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12,
|
||||
0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e,
|
||||
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72,
|
||||
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a,
|
||||
0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72,
|
||||
0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69,
|
||||
0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f,
|
||||
0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73,
|
||||
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63,
|
||||
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0xad, 0x01, 0x0a, 0x06,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70,
|
||||
0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a,
|
||||
0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
|
||||
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
|
||||
0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c,
|
||||
0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41,
|
||||
0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x70, 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e,
|
||||
0x1a, 0x77, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03,
|
||||
0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c,
|
||||
0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67,
|
||||
0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00,
|
||||
0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49,
|
||||
0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70,
|
||||
0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a,
|
||||
0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48,
|
||||
0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50,
|
||||
0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09,
|
||||
0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f,
|
||||
0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02,
|
||||
0x32, 0xa3, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x12, 0x42, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0xeb, 0x02, 0x0a, 0x04,
|
||||
0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x46, 0x0a, 0x10, 0x70,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18,
|
||||
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x73, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
|
||||
0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76,
|
||||
0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a,
|
||||
0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
|
||||
0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x52, 0x0a, 0x05, 0x41, 0x70, 0x70,
|
||||
0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61,
|
||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x08, 0x0a,
|
||||
0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x1a, 0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x48, 0x00,
|
||||
0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06,
|
||||
0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63,
|
||||
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01,
|
||||
0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69,
|
||||
0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61,
|
||||
0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x77, 0x0a, 0x08, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x08, 0x63,
|
||||
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00,
|
||||
0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79,
|
||||
0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09,
|
||||
0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42,
|
||||
0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08,
|
||||
0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f,
|
||||
0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e,
|
||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10,
|
||||
0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54,
|
||||
0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02,
|
||||
0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54,
|
||||
0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
|
||||
0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0xa3, 0x01, 0x0a, 0x0b, 0x50, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x05, 0x50, 0x61, 0x72,
|
||||
0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72,
|
||||
0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x50, 0x0a,
|
||||
0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42,
|
||||
0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -3090,7 +3190,7 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte {
|
|||
}
|
||||
|
||||
var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
|
||||
var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 32)
|
||||
var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 33)
|
||||
var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{
|
||||
(LogLevel)(0), // 0: provisioner.LogLevel
|
||||
(AppSharingLevel)(0), // 1: provisioner.AppSharingLevel
|
||||
|
@ -3117,19 +3217,20 @@ var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{
|
|||
(*Resource)(nil), // 22: provisioner.Resource
|
||||
(*Parse)(nil), // 23: provisioner.Parse
|
||||
(*Provision)(nil), // 24: provisioner.Provision
|
||||
nil, // 25: provisioner.Agent.EnvEntry
|
||||
(*Resource_Metadata)(nil), // 26: provisioner.Resource.Metadata
|
||||
(*Parse_Request)(nil), // 27: provisioner.Parse.Request
|
||||
(*Parse_Complete)(nil), // 28: provisioner.Parse.Complete
|
||||
(*Parse_Response)(nil), // 29: provisioner.Parse.Response
|
||||
(*Provision_Metadata)(nil), // 30: provisioner.Provision.Metadata
|
||||
(*Provision_Config)(nil), // 31: provisioner.Provision.Config
|
||||
(*Provision_Plan)(nil), // 32: provisioner.Provision.Plan
|
||||
(*Provision_Apply)(nil), // 33: provisioner.Provision.Apply
|
||||
(*Provision_Cancel)(nil), // 34: provisioner.Provision.Cancel
|
||||
(*Provision_Request)(nil), // 35: provisioner.Provision.Request
|
||||
(*Provision_Complete)(nil), // 36: provisioner.Provision.Complete
|
||||
(*Provision_Response)(nil), // 37: provisioner.Provision.Response
|
||||
(*Agent_Metadata)(nil), // 25: provisioner.Agent.Metadata
|
||||
nil, // 26: provisioner.Agent.EnvEntry
|
||||
(*Resource_Metadata)(nil), // 27: provisioner.Resource.Metadata
|
||||
(*Parse_Request)(nil), // 28: provisioner.Parse.Request
|
||||
(*Parse_Complete)(nil), // 29: provisioner.Parse.Complete
|
||||
(*Parse_Response)(nil), // 30: provisioner.Parse.Response
|
||||
(*Provision_Metadata)(nil), // 31: provisioner.Provision.Metadata
|
||||
(*Provision_Config)(nil), // 32: provisioner.Provision.Config
|
||||
(*Provision_Plan)(nil), // 33: provisioner.Provision.Plan
|
||||
(*Provision_Apply)(nil), // 34: provisioner.Provision.Apply
|
||||
(*Provision_Cancel)(nil), // 35: provisioner.Provision.Cancel
|
||||
(*Provision_Request)(nil), // 36: provisioner.Provision.Request
|
||||
(*Provision_Complete)(nil), // 37: provisioner.Provision.Complete
|
||||
(*Provision_Response)(nil), // 38: provisioner.Provision.Response
|
||||
}
|
||||
var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{
|
||||
3, // 0: provisioner.ParameterSource.scheme:type_name -> provisioner.ParameterSource.Scheme
|
||||
|
@ -3140,40 +3241,41 @@ var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{
|
|||
5, // 5: provisioner.ParameterSchema.validation_type_system:type_name -> provisioner.ParameterSchema.TypeSystem
|
||||
12, // 6: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption
|
||||
0, // 7: provisioner.Log.level:type_name -> provisioner.LogLevel
|
||||
25, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
|
||||
26, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
|
||||
20, // 9: provisioner.Agent.apps:type_name -> provisioner.App
|
||||
21, // 10: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
|
||||
1, // 11: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel
|
||||
19, // 12: provisioner.Resource.agents:type_name -> provisioner.Agent
|
||||
26, // 13: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
|
||||
11, // 14: provisioner.Parse.Complete.template_variables:type_name -> provisioner.TemplateVariable
|
||||
10, // 15: provisioner.Parse.Complete.parameter_schemas:type_name -> provisioner.ParameterSchema
|
||||
16, // 16: provisioner.Parse.Response.log:type_name -> provisioner.Log
|
||||
28, // 17: provisioner.Parse.Response.complete:type_name -> provisioner.Parse.Complete
|
||||
2, // 18: provisioner.Provision.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition
|
||||
30, // 19: provisioner.Provision.Config.metadata:type_name -> provisioner.Provision.Metadata
|
||||
31, // 20: provisioner.Provision.Plan.config:type_name -> provisioner.Provision.Config
|
||||
9, // 21: provisioner.Provision.Plan.parameter_values:type_name -> provisioner.ParameterValue
|
||||
14, // 22: provisioner.Provision.Plan.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||
15, // 23: provisioner.Provision.Plan.variable_values:type_name -> provisioner.VariableValue
|
||||
18, // 24: provisioner.Provision.Plan.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
||||
31, // 25: provisioner.Provision.Apply.config:type_name -> provisioner.Provision.Config
|
||||
32, // 26: provisioner.Provision.Request.plan:type_name -> provisioner.Provision.Plan
|
||||
33, // 27: provisioner.Provision.Request.apply:type_name -> provisioner.Provision.Apply
|
||||
34, // 28: provisioner.Provision.Request.cancel:type_name -> provisioner.Provision.Cancel
|
||||
22, // 29: provisioner.Provision.Complete.resources:type_name -> provisioner.Resource
|
||||
13, // 30: provisioner.Provision.Complete.parameters:type_name -> provisioner.RichParameter
|
||||
16, // 31: provisioner.Provision.Response.log:type_name -> provisioner.Log
|
||||
36, // 32: provisioner.Provision.Response.complete:type_name -> provisioner.Provision.Complete
|
||||
27, // 33: provisioner.Provisioner.Parse:input_type -> provisioner.Parse.Request
|
||||
35, // 34: provisioner.Provisioner.Provision:input_type -> provisioner.Provision.Request
|
||||
29, // 35: provisioner.Provisioner.Parse:output_type -> provisioner.Parse.Response
|
||||
37, // 36: provisioner.Provisioner.Provision:output_type -> provisioner.Provision.Response
|
||||
35, // [35:37] is the sub-list for method output_type
|
||||
33, // [33:35] is the sub-list for method input_type
|
||||
33, // [33:33] is the sub-list for extension type_name
|
||||
33, // [33:33] is the sub-list for extension extendee
|
||||
0, // [0:33] is the sub-list for field type_name
|
||||
25, // 10: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata
|
||||
21, // 11: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
|
||||
1, // 12: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel
|
||||
19, // 13: provisioner.Resource.agents:type_name -> provisioner.Agent
|
||||
27, // 14: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
|
||||
11, // 15: provisioner.Parse.Complete.template_variables:type_name -> provisioner.TemplateVariable
|
||||
10, // 16: provisioner.Parse.Complete.parameter_schemas:type_name -> provisioner.ParameterSchema
|
||||
16, // 17: provisioner.Parse.Response.log:type_name -> provisioner.Log
|
||||
29, // 18: provisioner.Parse.Response.complete:type_name -> provisioner.Parse.Complete
|
||||
2, // 19: provisioner.Provision.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition
|
||||
31, // 20: provisioner.Provision.Config.metadata:type_name -> provisioner.Provision.Metadata
|
||||
32, // 21: provisioner.Provision.Plan.config:type_name -> provisioner.Provision.Config
|
||||
9, // 22: provisioner.Provision.Plan.parameter_values:type_name -> provisioner.ParameterValue
|
||||
14, // 23: provisioner.Provision.Plan.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||
15, // 24: provisioner.Provision.Plan.variable_values:type_name -> provisioner.VariableValue
|
||||
18, // 25: provisioner.Provision.Plan.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
||||
32, // 26: provisioner.Provision.Apply.config:type_name -> provisioner.Provision.Config
|
||||
33, // 27: provisioner.Provision.Request.plan:type_name -> provisioner.Provision.Plan
|
||||
34, // 28: provisioner.Provision.Request.apply:type_name -> provisioner.Provision.Apply
|
||||
35, // 29: provisioner.Provision.Request.cancel:type_name -> provisioner.Provision.Cancel
|
||||
22, // 30: provisioner.Provision.Complete.resources:type_name -> provisioner.Resource
|
||||
13, // 31: provisioner.Provision.Complete.parameters:type_name -> provisioner.RichParameter
|
||||
16, // 32: provisioner.Provision.Response.log:type_name -> provisioner.Log
|
||||
37, // 33: provisioner.Provision.Response.complete:type_name -> provisioner.Provision.Complete
|
||||
28, // 34: provisioner.Provisioner.Parse:input_type -> provisioner.Parse.Request
|
||||
36, // 35: provisioner.Provisioner.Provision:input_type -> provisioner.Provision.Request
|
||||
30, // 36: provisioner.Provisioner.Parse:output_type -> provisioner.Parse.Response
|
||||
38, // 37: provisioner.Provisioner.Provision:output_type -> provisioner.Provision.Response
|
||||
36, // [36:38] is the sub-list for method output_type
|
||||
34, // [34:36] is the sub-list for method input_type
|
||||
34, // [34:34] is the sub-list for extension type_name
|
||||
34, // [34:34] is the sub-list for extension extendee
|
||||
0, // [0:34] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_provisionersdk_proto_provisioner_proto_init() }
|
||||
|
@ -3410,8 +3512,8 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Resource_Metadata); i {
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Agent_Metadata); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3423,7 +3525,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Parse_Request); i {
|
||||
switch v := v.(*Resource_Metadata); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3435,7 +3537,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Parse_Complete); i {
|
||||
switch v := v.(*Parse_Request); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3447,7 +3549,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Parse_Response); i {
|
||||
switch v := v.(*Parse_Complete); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3459,7 +3561,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Metadata); i {
|
||||
switch v := v.(*Parse_Response); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3471,7 +3573,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Config); i {
|
||||
switch v := v.(*Provision_Metadata); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3483,7 +3585,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Plan); i {
|
||||
switch v := v.(*Provision_Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3495,7 +3597,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Apply); i {
|
||||
switch v := v.(*Provision_Plan); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3507,7 +3609,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Cancel); i {
|
||||
switch v := v.(*Provision_Apply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3519,7 +3621,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Request); i {
|
||||
switch v := v.(*Provision_Cancel); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3531,7 +3633,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Complete); i {
|
||||
switch v := v.(*Provision_Request); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
@ -3543,6 +3645,18 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Complete); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Provision_Response); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
|
@ -3559,16 +3673,16 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
(*Agent_Token)(nil),
|
||||
(*Agent_InstanceId)(nil),
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[23].OneofWrappers = []interface{}{
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[24].OneofWrappers = []interface{}{
|
||||
(*Parse_Response_Log)(nil),
|
||||
(*Parse_Response_Complete)(nil),
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[29].OneofWrappers = []interface{}{
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[30].OneofWrappers = []interface{}{
|
||||
(*Provision_Request_Plan)(nil),
|
||||
(*Provision_Request_Apply)(nil),
|
||||
(*Provision_Request_Cancel)(nil),
|
||||
}
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[31].OneofWrappers = []interface{}{
|
||||
file_provisionersdk_proto_provisioner_proto_msgTypes[32].OneofWrappers = []interface{}{
|
||||
(*Provision_Response_Log)(nil),
|
||||
(*Provision_Response_Complete)(nil),
|
||||
}
|
||||
|
@ -3578,7 +3692,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc,
|
||||
NumEnums: 6,
|
||||
NumMessages: 32,
|
||||
NumMessages: 33,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
|
|
@ -127,6 +127,13 @@ message GitAuthProvider {
|
|||
|
||||
// Agent represents a running agent on the workspace.
|
||||
message Agent {
|
||||
message Metadata {
|
||||
string key = 1;
|
||||
string display_name = 2;
|
||||
string script = 3;
|
||||
int64 interval = 4;
|
||||
int64 timeout = 5;
|
||||
}
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
map<string, string> env = 3;
|
||||
|
@ -146,6 +153,7 @@ message Agent {
|
|||
int32 startup_script_timeout_seconds = 15;
|
||||
string shutdown_script = 16;
|
||||
int32 shutdown_script_timeout_seconds = 17;
|
||||
repeated Metadata metadata = 18;
|
||||
}
|
||||
|
||||
enum AppSharingLevel {
|
||||
|
|
|
@ -138,6 +138,8 @@
|
|||
"prettier": "2.8.1",
|
||||
"resize-observer": "1.0.4",
|
||||
"semver": "7.3.7",
|
||||
"storybook-addon-mock": "^3.2.0",
|
||||
"storybook-react-context": "^0.6.0",
|
||||
"typescript": "4.8.2"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
@ -1046,6 +1046,18 @@ const getMissingParameters = (
|
|||
return missingParameters
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param agentId
|
||||
* @returns An EventSource that emits agent metadata event objects (ServerSentEvent)
|
||||
*/
|
||||
export const watchAgentMetadata = (agentId: string): EventSource => {
|
||||
return new EventSource(
|
||||
`${location.protocol}//${location.host}/api/v2/workspaceagents/${agentId}/watch-metadata`,
|
||||
{ withCredentials: true },
|
||||
)
|
||||
}
|
||||
|
||||
export const watchBuildLogs = (
|
||||
versionId: string,
|
||||
onMessage: (log: TypesGen.ProvisionerJobLog) => void,
|
||||
|
|
|
@ -1086,6 +1086,29 @@ export interface WorkspaceAgentListeningPortsResponse {
|
|||
readonly ports: WorkspaceAgentListeningPort[]
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentMetadata {
|
||||
readonly result: WorkspaceAgentMetadataResult
|
||||
readonly description: WorkspaceAgentMetadataDescription
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentMetadataDescription {
|
||||
readonly display_name: string
|
||||
readonly key: string
|
||||
readonly script: string
|
||||
readonly interval: number
|
||||
readonly timeout: number
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentMetadataResult {
|
||||
readonly collected_at: string
|
||||
readonly age: number
|
||||
readonly value: string
|
||||
readonly error: string
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentStartupLog {
|
||||
readonly id: number
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
import { Story } from "@storybook/react"
|
||||
import {
|
||||
WorkspaceAgentMetadataDescription,
|
||||
WorkspaceAgentMetadataResult,
|
||||
} from "api/typesGenerated"
|
||||
import { AgentMetadataView, AgentMetadataViewProps } from "./AgentMetadata"
|
||||
|
||||
export default {
|
||||
title: "components/AgentMetadata",
|
||||
component: AgentMetadataView,
|
||||
}
|
||||
|
||||
const Template: Story<AgentMetadataViewProps> = (args) => (
|
||||
<AgentMetadataView {...args} />
|
||||
)
|
||||
|
||||
const resultDefaults: WorkspaceAgentMetadataResult = {
|
||||
collected_at: "2021-05-05T00:00:00Z",
|
||||
error: "",
|
||||
value: "defvalue",
|
||||
age: 5,
|
||||
}
|
||||
|
||||
const descriptionDefaults: WorkspaceAgentMetadataDescription = {
|
||||
display_name: "DisPlay",
|
||||
key: "defkey",
|
||||
interval: 10,
|
||||
timeout: 10,
|
||||
script: "some command",
|
||||
}
|
||||
|
||||
export const Example = Template.bind({})
|
||||
Example.args = {
|
||||
metadata: [
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "110%",
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
display_name: "CPU",
|
||||
key: "CPU",
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "50GB",
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
display_name: "Memory",
|
||||
key: "Memory",
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "cant see it",
|
||||
age: 300,
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
interval: 5,
|
||||
display_name: "Stale",
|
||||
key: "stale",
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "oops",
|
||||
error: "fatal error",
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
display_name: "Error",
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "oops",
|
||||
error: "fatal error",
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
display_name: "Error",
|
||||
key: "stale",
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
...resultDefaults,
|
||||
value: "",
|
||||
collected_at: "0001-01-01T00:00:00Z",
|
||||
age: 1000000,
|
||||
},
|
||||
description: {
|
||||
...descriptionDefaults,
|
||||
display_name: "Never loads",
|
||||
key: "nloads",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
import Popover from "@material-ui/core/Popover"
|
||||
import CircularProgress from "@material-ui/core/CircularProgress"
|
||||
import makeStyles from "@material-ui/core/styles/makeStyles"
|
||||
import { watchAgentMetadata } from "api/api"
|
||||
import { WorkspaceAgent, WorkspaceAgentMetadata } from "api/typesGenerated"
|
||||
import { CodeExample } from "components/CodeExample/CodeExample"
|
||||
import { Stack } from "components/Stack/Stack"
|
||||
import {
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
} from "components/Tooltips/HelpTooltip"
|
||||
import dayjs from "dayjs"
|
||||
import {
|
||||
createContext,
|
||||
FC,
|
||||
PropsWithChildren,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react"
|
||||
|
||||
export const WatchAgentMetadataContext = createContext(watchAgentMetadata)
|
||||
|
||||
const MetadataItemValue: FC<
|
||||
PropsWithChildren<{ item: WorkspaceAgentMetadata }>
|
||||
> = ({ item, children }) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const anchorRef = useRef<HTMLDivElement>(null)
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={anchorRef}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
role="presentation"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<Popover
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
open={isOpen}
|
||||
anchorEl={anchorRef.current}
|
||||
onClose={() => setIsOpen(false)}
|
||||
PaperProps={{
|
||||
onMouseEnter: () => setIsOpen(true),
|
||||
onMouseLeave: () => setIsOpen(false),
|
||||
}}
|
||||
classes={{ paper: styles.metadataPopover }}
|
||||
>
|
||||
<HelpTooltipTitle>{item.description.display_name}</HelpTooltipTitle>
|
||||
{item.result.value.length > 0 && (
|
||||
<>
|
||||
<HelpTooltipText>Last result:</HelpTooltipText>
|
||||
<HelpTooltipText>
|
||||
<CodeExample code={item.result.value} />
|
||||
</HelpTooltipText>
|
||||
</>
|
||||
)}
|
||||
{item.result.error.length > 0 && (
|
||||
<>
|
||||
<HelpTooltipText>Last error:</HelpTooltipText>
|
||||
<HelpTooltipText>
|
||||
<CodeExample code={item.result.error} />
|
||||
</HelpTooltipText>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const MetadataItem: FC<{ item: WorkspaceAgentMetadata }> = ({ item }) => {
|
||||
const styles = useStyles()
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const labelAnchorRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
if (item.result === undefined) {
|
||||
throw new Error("Metadata item result is undefined")
|
||||
}
|
||||
if (item.description === undefined) {
|
||||
throw new Error("Metadata item description is undefined")
|
||||
}
|
||||
|
||||
const staleThreshold = Math.max(
|
||||
item.description.interval + item.description.timeout * 2,
|
||||
5,
|
||||
)
|
||||
|
||||
const status: "stale" | "valid" | "loading" = (() => {
|
||||
const year = dayjs(item.result.collected_at).year()
|
||||
if (year <= 1970 || isNaN(year)) {
|
||||
return "loading"
|
||||
}
|
||||
if (item.result.age > staleThreshold) {
|
||||
return "stale"
|
||||
}
|
||||
return "valid"
|
||||
})()
|
||||
|
||||
// Stale data is as good as no data. Plus, we want to build confidence in our
|
||||
// users that what's shown is real. If times aren't correctly synced this
|
||||
// could be buggy. But, how common is that anyways?
|
||||
const value =
|
||||
status === "stale" || status === "loading" ? (
|
||||
<CircularProgress size={12} />
|
||||
) : (
|
||||
<div
|
||||
className={
|
||||
styles.metadataValue +
|
||||
" " +
|
||||
(item.result.error.length === 0
|
||||
? styles.metadataValueSuccess
|
||||
: styles.metadataValueError)
|
||||
}
|
||||
>
|
||||
{item.result.value}
|
||||
</div>
|
||||
)
|
||||
|
||||
const updatesInSeconds = -(item.description.interval - item.result.age)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.metadata}>
|
||||
<div
|
||||
className={styles.metadataLabel}
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
// onMouseLeave={() => setIsOpen(false)}
|
||||
role="presentation"
|
||||
ref={labelAnchorRef}
|
||||
>
|
||||
{item.description.display_name}
|
||||
</div>
|
||||
<MetadataItemValue item={item}>{value}</MetadataItemValue>
|
||||
</div>
|
||||
<Popover
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
open={isOpen}
|
||||
anchorEl={labelAnchorRef.current}
|
||||
onClose={() => setIsOpen(false)}
|
||||
PaperProps={{
|
||||
onMouseEnter: () => setIsOpen(true),
|
||||
onMouseLeave: () => setIsOpen(false),
|
||||
}}
|
||||
classes={{ paper: styles.metadataPopover }}
|
||||
>
|
||||
<HelpTooltipTitle>{item.description.display_name}</HelpTooltipTitle>
|
||||
{status === "stale" ? (
|
||||
<HelpTooltipText>
|
||||
This item is now stale because the agent hasn{"'"}t reported a new
|
||||
value in {dayjs.duration(item.result.age, "s").humanize()}.
|
||||
</HelpTooltipText>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{status === "valid" ? (
|
||||
<HelpTooltipText>
|
||||
The agent collected this value{" "}
|
||||
{dayjs.duration(item.result.age, "s").humanize()} ago and will
|
||||
update it in{" "}
|
||||
{dayjs.duration(Math.min(updatesInSeconds, 0), "s").humanize()}.
|
||||
</HelpTooltipText>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{status === "loading" ? (
|
||||
<HelpTooltipText>
|
||||
This value is loading for the first time...
|
||||
</HelpTooltipText>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<HelpTooltipText>
|
||||
This value is produced by the following script:
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipText>
|
||||
<CodeExample code={item.description.script}></CodeExample>
|
||||
</HelpTooltipText>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export interface AgentMetadataViewProps {
|
||||
metadata: WorkspaceAgentMetadata[]
|
||||
}
|
||||
|
||||
export const AgentMetadataView: FC<AgentMetadataViewProps> = ({ metadata }) => {
|
||||
const styles = useStyles()
|
||||
if (metadata.length === 0) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<Stack
|
||||
alignItems="flex-start"
|
||||
direction="row"
|
||||
spacing={5}
|
||||
className={styles.metadataStack}
|
||||
>
|
||||
<div className={styles.metadataHeader}>
|
||||
{metadata.map((m) => {
|
||||
if (m.description === undefined) {
|
||||
throw new Error("Metadata item description is undefined")
|
||||
}
|
||||
return <MetadataItem key={m.description.key} item={m} />
|
||||
})}
|
||||
</div>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export const AgentMetadata: FC<{
|
||||
agent: WorkspaceAgent
|
||||
}> = ({ agent }) => {
|
||||
const [metadata, setMetadata] = useState<
|
||||
WorkspaceAgentMetadata[] | undefined
|
||||
>(undefined)
|
||||
|
||||
const watchAgentMetadata = useContext(WatchAgentMetadataContext)
|
||||
|
||||
useEffect(() => {
|
||||
const source = watchAgentMetadata(agent.id)
|
||||
|
||||
source.onerror = (e) => {
|
||||
console.error("received error in watch stream", e)
|
||||
}
|
||||
source.addEventListener("data", (e) => {
|
||||
const data = JSON.parse(e.data)
|
||||
setMetadata(data)
|
||||
})
|
||||
return () => {
|
||||
source.close()
|
||||
}
|
||||
}, [agent.id, watchAgentMetadata])
|
||||
|
||||
if (metadata === undefined) {
|
||||
return <CircularProgress size={16} />
|
||||
}
|
||||
|
||||
return <AgentMetadataView metadata={metadata} />
|
||||
}
|
||||
|
||||
// These are more or less copied from
|
||||
// site/src/components/Resources/ResourceCard.tsx
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
metadataStack: {
|
||||
border: `2px dashed ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
width: "100%",
|
||||
},
|
||||
metadataHeader: {
|
||||
padding: "8px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(4, minmax(0, 1fr))",
|
||||
gap: theme.spacing(5),
|
||||
rowGap: theme.spacing(3),
|
||||
},
|
||||
|
||||
metadata: {
|
||||
fontSize: 16,
|
||||
},
|
||||
|
||||
metadataLabel: {
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
|
||||
metadataValue: {
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
|
||||
metadataValueSuccess: {
|
||||
color: theme.palette.success.light,
|
||||
},
|
||||
metadataValueError: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
|
||||
metadataPopover: {
|
||||
marginTop: theme.spacing(0.5),
|
||||
padding: theme.spacing(2.5),
|
||||
color: theme.palette.text.secondary,
|
||||
pointerEvents: "auto",
|
||||
maxWidth: "480px",
|
||||
|
||||
"& .MuiButton-root": {
|
||||
padding: theme.spacing(1, 2),
|
||||
borderRadius: 0,
|
||||
border: 0,
|
||||
|
||||
"&:hover": {
|
||||
background: theme.palette.action.hover,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
|
@ -35,6 +35,7 @@ import { SSHButton } from "../SSHButton/SSHButton"
|
|||
import { Stack } from "../Stack/Stack"
|
||||
import { TerminalLink } from "../TerminalLink/TerminalLink"
|
||||
import { AgentLatency } from "./AgentLatency"
|
||||
import { AgentMetadata } from "./AgentMetadata"
|
||||
import { AgentStatus } from "./AgentStatus"
|
||||
import { AgentVersion } from "./AgentVersion"
|
||||
|
||||
|
@ -169,178 +170,202 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
className={styles.agentRow}
|
||||
spacing={4}
|
||||
style={{
|
||||
justifyContent: "none",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" alignItems="baseline">
|
||||
<div className={styles.agentStatusWrapper}>
|
||||
<AgentStatus agent={agent} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
<div className={styles.agentStatusWrapper}>
|
||||
<AgentStatus agent={agent} />
|
||||
</div>
|
||||
<Stack
|
||||
direction="column"
|
||||
alignItems="flex-start"
|
||||
key={agent.id}
|
||||
spacing={2}
|
||||
style={{
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" alignItems="baseline">
|
||||
<div>
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="baseline"
|
||||
className={styles.agentData}
|
||||
spacing={1}
|
||||
>
|
||||
<span className={styles.agentOS}>
|
||||
{agent.operating_system}
|
||||
</span>
|
||||
|
||||
<Maybe condition={agent.status === "connected"}>
|
||||
<AgentVersion
|
||||
agent={agent}
|
||||
serverVersion={serverVersion}
|
||||
onUpdate={onUpdateAgent}
|
||||
/>
|
||||
</Maybe>
|
||||
|
||||
<AgentLatency agent={agent} />
|
||||
|
||||
<Maybe condition={agent.status === "connecting"}>
|
||||
<Skeleton width={160} variant="text" />
|
||||
<Skeleton width={36} variant="text" />
|
||||
</Maybe>
|
||||
|
||||
<Maybe condition={agent.status === "timeout"}>
|
||||
{t("unableToConnect")}
|
||||
</Maybe>
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
wrap="wrap"
|
||||
maxWidth="750px"
|
||||
>
|
||||
{showApps && agent.status === "connected" && (
|
||||
<>
|
||||
{agent.apps.map((app) => (
|
||||
<AppLink
|
||||
key={app.slug}
|
||||
appsHost={applicationsHost}
|
||||
app={app}
|
||||
agent={agent}
|
||||
workspace={workspace}
|
||||
/>
|
||||
))}
|
||||
|
||||
<TerminalLink
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
userName={workspace.owner_name}
|
||||
/>
|
||||
{!hideSSHButton && (
|
||||
<SSHButton
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
sshPrefix={sshPrefix}
|
||||
/>
|
||||
)}
|
||||
{!hideVSCodeDesktopButton && (
|
||||
<VSCodeDesktopButton
|
||||
userName={workspace.owner_name}
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
folderPath={agent.expanded_directory}
|
||||
/>
|
||||
)}
|
||||
{applicationsHost !== undefined &&
|
||||
applicationsHost !== "" && (
|
||||
<PortForwardButton
|
||||
host={applicationsHost}
|
||||
workspaceName={workspace.name}
|
||||
agentId={agent.id}
|
||||
agentName={agent.name}
|
||||
username={workspace.owner_name}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{showApps && agent.status === "connecting" && (
|
||||
<>
|
||||
<AppLinkSkeleton width={84} />
|
||||
<AppLinkSkeleton width={112} />
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<AgentMetadata agent={agent} />
|
||||
{hasStartupFeatures && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="baseline"
|
||||
className={styles.agentData}
|
||||
spacing={1}
|
||||
className={styles.startupLinks}
|
||||
>
|
||||
<span className={styles.agentOS}>{agent.operating_system}</span>
|
||||
|
||||
<Maybe condition={agent.status === "connected"}>
|
||||
<AgentVersion
|
||||
agent={agent}
|
||||
serverVersion={serverVersion}
|
||||
onUpdate={onUpdateAgent}
|
||||
/>
|
||||
</Maybe>
|
||||
|
||||
<AgentLatency agent={agent} />
|
||||
|
||||
<Maybe condition={agent.status === "connecting"}>
|
||||
<Skeleton width={160} variant="text" />
|
||||
<Skeleton width={36} variant="text" />
|
||||
</Maybe>
|
||||
|
||||
<Maybe condition={agent.status === "timeout"}>
|
||||
{t("unableToConnect")}
|
||||
</Maybe>
|
||||
</Stack>
|
||||
|
||||
{hasStartupFeatures && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="baseline"
|
||||
spacing={1}
|
||||
className={styles.startupLinks}
|
||||
<Link
|
||||
className={styles.startupLink}
|
||||
variant="body2"
|
||||
onClick={() => {
|
||||
setShowStartupLogs(!showStartupLogs)
|
||||
}}
|
||||
>
|
||||
{showStartupLogs ? (
|
||||
<VisibilityOffOutlined />
|
||||
) : (
|
||||
<VisibilityOutlined />
|
||||
)}
|
||||
{showStartupLogs ? "Hide" : "Show"} Startup Logs
|
||||
</Link>
|
||||
|
||||
{agent.startup_script && (
|
||||
<Link
|
||||
className={styles.startupLink}
|
||||
variant="body2"
|
||||
ref={startupScriptAnchorRef}
|
||||
onClick={() => {
|
||||
setShowStartupLogs(!showStartupLogs)
|
||||
setStartupScriptOpen(!startupScriptOpen)
|
||||
}}
|
||||
>
|
||||
{showStartupLogs ? (
|
||||
<VisibilityOffOutlined />
|
||||
) : (
|
||||
<VisibilityOutlined />
|
||||
)}
|
||||
{showStartupLogs ? "Hide" : "Show"} Startup Logs
|
||||
<PlayCircleOutlined />
|
||||
View Startup Script
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{agent.startup_script && (
|
||||
<Link
|
||||
className={styles.startupLink}
|
||||
variant="body2"
|
||||
ref={startupScriptAnchorRef}
|
||||
onClick={() => {
|
||||
setStartupScriptOpen(!startupScriptOpen)
|
||||
<Popover
|
||||
classes={{
|
||||
paper: styles.startupScriptPopover,
|
||||
}}
|
||||
open={startupScriptOpen}
|
||||
onClose={() => setStartupScriptOpen(false)}
|
||||
anchorEl={startupScriptAnchorRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<SyntaxHighlighter
|
||||
style={darcula}
|
||||
language="shell"
|
||||
showLineNumbers
|
||||
// Use inline styles does not work correctly
|
||||
// https://github.com/react-syntax-highlighter/react-syntax-highlighter/issues/329
|
||||
codeTagProps={{ style: {} }}
|
||||
customStyle={{
|
||||
background: theme.palette.background.default,
|
||||
maxWidth: 600,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
<PlayCircleOutlined />
|
||||
View Startup Script
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Popover
|
||||
classes={{
|
||||
paper: styles.startupScriptPopover,
|
||||
}}
|
||||
open={startupScriptOpen}
|
||||
onClose={() => setStartupScriptOpen(false)}
|
||||
anchorEl={startupScriptAnchorRef.current}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<SyntaxHighlighter
|
||||
style={darcula}
|
||||
language="shell"
|
||||
showLineNumbers
|
||||
// Use inline styles does not work correctly
|
||||
// https://github.com/react-syntax-highlighter/react-syntax-highlighter/issues/329
|
||||
codeTagProps={{ style: {} }}
|
||||
customStyle={{
|
||||
background: theme.palette.background.default,
|
||||
maxWidth: 600,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{agent.startup_script || ""}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</Popover>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
wrap="wrap"
|
||||
maxWidth="750px"
|
||||
>
|
||||
{showApps && agent.status === "connected" && (
|
||||
<>
|
||||
{agent.apps.map((app) => (
|
||||
<AppLink
|
||||
key={app.slug}
|
||||
appsHost={applicationsHost}
|
||||
app={app}
|
||||
agent={agent}
|
||||
workspace={workspace}
|
||||
/>
|
||||
))}
|
||||
|
||||
<TerminalLink
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
userName={workspace.owner_name}
|
||||
/>
|
||||
{!hideSSHButton && (
|
||||
<SSHButton
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
sshPrefix={sshPrefix}
|
||||
/>
|
||||
)}
|
||||
{!hideVSCodeDesktopButton && (
|
||||
<VSCodeDesktopButton
|
||||
userName={workspace.owner_name}
|
||||
workspaceName={workspace.name}
|
||||
agentName={agent.name}
|
||||
folderPath={agent.expanded_directory}
|
||||
/>
|
||||
)}
|
||||
{applicationsHost !== undefined && applicationsHost !== "" && (
|
||||
<PortForwardButton
|
||||
host={applicationsHost}
|
||||
workspaceName={workspace.name}
|
||||
agentId={agent.id}
|
||||
agentName={agent.name}
|
||||
username={workspace.owner_name}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{showApps && agent.status === "connecting" && (
|
||||
<>
|
||||
<AppLinkSkeleton width={84} />
|
||||
<AppLinkSkeleton width={112} />
|
||||
</>
|
||||
{agent.startup_script || ""}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</Popover>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{showStartupLogs && (
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
import { action } from "@storybook/addon-actions"
|
||||
import { Story } from "@storybook/react"
|
||||
import { WatchAgentMetadataContext } from "components/Resources/AgentMetadata"
|
||||
import { ProvisionerJobLog } from "api/typesGenerated"
|
||||
import * as Mocks from "../../testHelpers/entities"
|
||||
import { Workspace, WorkspaceErrors, WorkspaceProps } from "./Workspace"
|
||||
import { withReactContext } from "storybook-react-context"
|
||||
import EventSource from "eventsourcemock"
|
||||
|
||||
export default {
|
||||
title: "components/Workspace",
|
||||
component: Workspace,
|
||||
argTypes: {},
|
||||
decorators: [
|
||||
withReactContext({
|
||||
Context: WatchAgentMetadataContext,
|
||||
initialState: (_: string): EventSource => {
|
||||
// Need Bruno's help here.
|
||||
return new EventSource()
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const Template: Story<WorkspaceProps> = (args) => <Workspace {...args} />
|
||||
|
|
118
site/yarn.lock
118
site/yarn.lock
|
@ -2219,6 +2219,23 @@
|
|||
global "^4.4.0"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/addons@^6.3.6":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.16.tgz#07e8f2205f86fa4c9dada719e3e096cb468e3cdd"
|
||||
integrity sha512-p3DqQi+8QRL5k7jXhXmJZLsE/GqHqyY6PcoA1oNTJr0try48uhTGUOYkgzmqtDaa/qPFO5LP+xCPzZXckGtquQ==
|
||||
dependencies:
|
||||
"@storybook/api" "6.5.16"
|
||||
"@storybook/channels" "6.5.16"
|
||||
"@storybook/client-logger" "6.5.16"
|
||||
"@storybook/core-events" "6.5.16"
|
||||
"@storybook/csf" "0.0.2--canary.4566f4d.1"
|
||||
"@storybook/router" "6.5.16"
|
||||
"@storybook/theming" "6.5.16"
|
||||
"@types/webpack-env" "^1.16.0"
|
||||
core-js "^3.8.2"
|
||||
global "^4.4.0"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/api@6.5.12":
|
||||
version "6.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.12.tgz#7cc82087fc9298be03f15bf4ab9c4aab294b3bac"
|
||||
|
@ -2242,6 +2259,29 @@
|
|||
ts-dedent "^2.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
"@storybook/api@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.16.tgz#897915b76de05587fd702951d5d836f708043662"
|
||||
integrity sha512-HOsuT8iomqeTMQJrRx5U8nsC7lJTwRr1DhdD0SzlqL4c80S/7uuCy4IZvOt4sYQjOzW5fOo/kamcoBXyLproTA==
|
||||
dependencies:
|
||||
"@storybook/channels" "6.5.16"
|
||||
"@storybook/client-logger" "6.5.16"
|
||||
"@storybook/core-events" "6.5.16"
|
||||
"@storybook/csf" "0.0.2--canary.4566f4d.1"
|
||||
"@storybook/router" "6.5.16"
|
||||
"@storybook/semver" "^7.3.2"
|
||||
"@storybook/theming" "6.5.16"
|
||||
core-js "^3.8.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
global "^4.4.0"
|
||||
lodash "^4.17.21"
|
||||
memoizerific "^1.11.3"
|
||||
regenerator-runtime "^0.13.7"
|
||||
store2 "^2.12.0"
|
||||
telejson "^6.0.8"
|
||||
ts-dedent "^2.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
"@storybook/api@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9"
|
||||
|
@ -2351,6 +2391,15 @@
|
|||
ts-dedent "^2.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
"@storybook/channels@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.16.tgz#3fb9a3b5666ecb951a2d0cf8b0699b084ef2d3c6"
|
||||
integrity sha512-VylzaWQZaMozEwZPJdyJoz+0jpDa8GRyaqu9TGG6QGv+KU5POoZaGLDkRE7TzWkyyP0KQLo80K99MssZCpgSeg==
|
||||
dependencies:
|
||||
core-js "^3.8.2"
|
||||
ts-dedent "^2.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
"@storybook/channels@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79"
|
||||
|
@ -2394,6 +2443,14 @@
|
|||
core-js "^3.8.2"
|
||||
global "^4.4.0"
|
||||
|
||||
"@storybook/client-logger@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.16.tgz#955cc46b389e7151c9eb1585a75e6a0605af61a1"
|
||||
integrity sha512-pxcNaCj3ItDdicPTXTtmYJE3YC1SjxFrBmHcyrN+nffeNyiMuViJdOOZzzzucTUG0wcOOX8jaSyak+nnHg5H1Q==
|
||||
dependencies:
|
||||
core-js "^3.8.2"
|
||||
global "^4.4.0"
|
||||
|
||||
"@storybook/client-logger@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014"
|
||||
|
@ -2521,6 +2578,13 @@
|
|||
dependencies:
|
||||
core-js "^3.8.2"
|
||||
|
||||
"@storybook/core-events@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.16.tgz#b1c265dac755007dae172d9d4b72656c9e5d7bb3"
|
||||
integrity sha512-qMZQwmvzpH5F2uwNUllTPg6eZXr2OaYZQRRN8VZJiuorZzDNdAFmiVWMWdkThwmyLEJuQKXxqCL8lMj/7PPM+g==
|
||||
dependencies:
|
||||
core-js "^3.8.2"
|
||||
|
||||
"@storybook/core-events@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637"
|
||||
|
@ -2790,6 +2854,17 @@
|
|||
qs "^6.10.0"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/router@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.16.tgz#28fb4d34e8219351a40bee1fc94dcacda6e1bd8b"
|
||||
integrity sha512-ZgeP8a5YV/iuKbv31V8DjPxlV4AzorRiR8OuSt/KqaiYXNXlOoQDz/qMmiNcrshrfLpmkzoq7fSo4T8lWo2UwQ==
|
||||
dependencies:
|
||||
"@storybook/client-logger" "6.5.16"
|
||||
core-js "^3.8.2"
|
||||
memoizerific "^1.11.3"
|
||||
qs "^6.10.0"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/router@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8"
|
||||
|
@ -2874,6 +2949,16 @@
|
|||
memoizerific "^1.11.3"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/theming@6.5.16":
|
||||
version "6.5.16"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.16.tgz#b999bdb98945b605b93b9dfdf7408535b701e2aa"
|
||||
integrity sha512-hNLctkjaYLRdk1+xYTkC1mg4dYz2wSv6SqbLpcKMbkPHTE0ElhddGPHQqB362md/w9emYXNkt1LSMD8Xk9JzVQ==
|
||||
dependencies:
|
||||
"@storybook/client-logger" "6.5.16"
|
||||
core-js "^3.8.2"
|
||||
memoizerific "^1.11.3"
|
||||
regenerator-runtime "^0.13.7"
|
||||
|
||||
"@storybook/theming@6.5.9":
|
||||
version "6.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa"
|
||||
|
@ -8853,7 +8938,7 @@ is-plain-obj@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
|
||||
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
|
||||
|
||||
is-plain-object@5.0.0:
|
||||
is-plain-object@5.0.0, is-plain-object@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||
|
@ -11043,6 +11128,11 @@ mock-socket@^9.1.0:
|
|||
resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282"
|
||||
integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==
|
||||
|
||||
mock-xmlhttprequest@^7.0.3:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mock-xmlhttprequest/-/mock-xmlhttprequest-7.0.4.tgz#5e188da009cf46900e522f690cbea8d26274a872"
|
||||
integrity sha512-hA0fIHy/74p5DE0rdmrpU0sV1U+gnWTcgShWequGRLy0L1eT+zY0ozFukawpLaxMwIA+orRcqFRElYwT+5p81A==
|
||||
|
||||
monaco-editor@0.34.1:
|
||||
version "0.34.1"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.34.1.tgz#1b75c4ad6bc4c1f9da656d740d98e0b850a22f87"
|
||||
|
@ -12590,6 +12680,14 @@ react@18.2.0:
|
|||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
reactcss@^1.2.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
|
||||
|
@ -13638,6 +13736,24 @@ store2@^2.12.0:
|
|||
resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068"
|
||||
integrity sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==
|
||||
|
||||
storybook-addon-mock@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/storybook-addon-mock/-/storybook-addon-mock-3.2.0.tgz#5832b1e49ff39ffab7a0ae8ec7de8bfdb8ddea45"
|
||||
integrity sha512-LaggsF/6Lt0AyHiotIEVQpwKfIiZ3KsNqtdXKVnIdOetjaD7GaOQeX0jIZiZUFX/i6QLmMuNoXFngqqkdVtfSg==
|
||||
dependencies:
|
||||
mock-xmlhttprequest "^7.0.3"
|
||||
path-to-regexp "^6.2.0"
|
||||
polished "^4.2.2"
|
||||
|
||||
storybook-react-context@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/storybook-react-context/-/storybook-react-context-0.6.0.tgz#06c7b48dc95f4619cf12e59429305fbd6f2b1373"
|
||||
integrity sha512-6IOUbSoC1WW68x8zQBEh8tZsVXjEvOBSJSOhkaD9o8IF9caIg/o1jnwuGibdyAd47ARN6g95O0N0vFBjXcB7pA==
|
||||
dependencies:
|
||||
"@storybook/addons" "^6.3.6"
|
||||
is-plain-object "^5.0.0"
|
||||
react "^17.0.2"
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
|
||||
|
|
Loading…
Reference in New Issue