mirror of https://github.com/coder/coder.git
chore: move proto to sdk conversion to agentsdk (#11831)
`agentsdk` depends on `agent/proto` because it needs to get the version to dial. Therefore, the conversion routines need to live in `agentsdk` so that we can convert to and from the Manifest. I briefly considered refactoring the agent to only reference `proto.Manifest`, but decided against it because we might have multiple protocol versions in the future, its useful to have a protocol-independent data structure.
This commit is contained in:
parent
1e8a9c09fe
commit
0eff646c31
|
@ -677,7 +677,7 @@ func (a *agent) fetchServiceBannerLoop(ctx context.Context, aAPI proto.DRPCAgent
|
||||||
a.logger.Error(ctx, "failed to update service banner", slog.Error(err))
|
a.logger.Error(ctx, "failed to update service banner", slog.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
serviceBanner := proto.SDKServiceBannerFromProto(sbp)
|
serviceBanner := agentsdk.ServiceBannerFromProto(sbp)
|
||||||
a.serviceBanner.Store(&serviceBanner)
|
a.serviceBanner.Store(&serviceBanner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,7 +710,7 @@ func (a *agent) run(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("fetch service banner: %w", err)
|
return xerrors.Errorf("fetch service banner: %w", err)
|
||||||
}
|
}
|
||||||
serviceBanner := proto.SDKServiceBannerFromProto(sbp)
|
serviceBanner := agentsdk.ServiceBannerFromProto(sbp)
|
||||||
a.serviceBanner.Store(&serviceBanner)
|
a.serviceBanner.Store(&serviceBanner)
|
||||||
|
|
||||||
manifest, err := a.client.Manifest(ctx)
|
manifest, err := a.client.Manifest(ctx)
|
||||||
|
|
|
@ -277,7 +277,7 @@ func (f *FakeAgentAPI) GetServiceBanner(context.Context, *agentproto.GetServiceB
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return agentproto.ServiceBannerFromSDK(sb), nil
|
return agentsdk.ProtoFromServiceBanner(sb), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*FakeAgentAPI) UpdateStats(context.Context, *agentproto.UpdateStatsRequest) (*agentproto.UpdateStatsResponse, error) {
|
func (*FakeAgentAPI) UpdateStats(context.Context, *agentproto.UpdateStatsRequest) (*agentproto.UpdateStatsResponse, error) {
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"golang.org/x/xerrors"
|
|
||||||
|
|
||||||
"github.com/coder/coder/v2/codersdk"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SDKAgentMetadataDescriptionsFromProto(descriptions []*WorkspaceAgentMetadata_Description) []codersdk.WorkspaceAgentMetadataDescription {
|
|
||||||
ret := make([]codersdk.WorkspaceAgentMetadataDescription, len(descriptions))
|
|
||||||
for i, description := range descriptions {
|
|
||||||
ret[i] = SDKAgentMetadataDescriptionFromProto(description)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKAgentMetadataDescriptionFromProto(description *WorkspaceAgentMetadata_Description) codersdk.WorkspaceAgentMetadataDescription {
|
|
||||||
return codersdk.WorkspaceAgentMetadataDescription{
|
|
||||||
DisplayName: description.DisplayName,
|
|
||||||
Key: description.Key,
|
|
||||||
Script: description.Script,
|
|
||||||
Interval: int64(description.Interval.AsDuration()),
|
|
||||||
Timeout: int64(description.Timeout.AsDuration()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKAgentScriptsFromProto(protoScripts []*WorkspaceAgentScript) ([]codersdk.WorkspaceAgentScript, error) {
|
|
||||||
ret := make([]codersdk.WorkspaceAgentScript, len(protoScripts))
|
|
||||||
for i, protoScript := range protoScripts {
|
|
||||||
app, err := SDKAgentScriptFromProto(protoScript)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("parse script %v: %w", i, err)
|
|
||||||
}
|
|
||||||
ret[i] = app
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKAgentScriptFromProto(protoScript *WorkspaceAgentScript) (codersdk.WorkspaceAgentScript, error) {
|
|
||||||
id, err := uuid.FromBytes(protoScript.LogSourceId)
|
|
||||||
if err != nil {
|
|
||||||
return codersdk.WorkspaceAgentScript{}, xerrors.Errorf("parse id: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return codersdk.WorkspaceAgentScript{
|
|
||||||
LogSourceID: id,
|
|
||||||
LogPath: protoScript.LogPath,
|
|
||||||
Script: protoScript.Script,
|
|
||||||
Cron: protoScript.Cron,
|
|
||||||
RunOnStart: protoScript.RunOnStart,
|
|
||||||
RunOnStop: protoScript.RunOnStop,
|
|
||||||
StartBlocksLogin: protoScript.StartBlocksLogin,
|
|
||||||
Timeout: protoScript.Timeout.AsDuration(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKAppsFromProto(protoApps []*WorkspaceApp) ([]codersdk.WorkspaceApp, error) {
|
|
||||||
ret := make([]codersdk.WorkspaceApp, len(protoApps))
|
|
||||||
for i, protoApp := range protoApps {
|
|
||||||
app, err := SDKAppFromProto(protoApp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("parse app %v (%q): %w", i, protoApp.Slug, err)
|
|
||||||
}
|
|
||||||
ret[i] = app
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKAppFromProto(protoApp *WorkspaceApp) (codersdk.WorkspaceApp, error) {
|
|
||||||
id, err := uuid.FromBytes(protoApp.Id)
|
|
||||||
if err != nil {
|
|
||||||
return codersdk.WorkspaceApp{}, xerrors.Errorf("parse id: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sharingLevel codersdk.WorkspaceAppSharingLevel = codersdk.WorkspaceAppSharingLevel(strings.ToLower(protoApp.SharingLevel.String()))
|
|
||||||
if _, ok := codersdk.MapWorkspaceAppSharingLevels[sharingLevel]; !ok {
|
|
||||||
return codersdk.WorkspaceApp{}, xerrors.Errorf("unknown app sharing level: %v (%q)", protoApp.SharingLevel, protoApp.SharingLevel.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var health codersdk.WorkspaceAppHealth = codersdk.WorkspaceAppHealth(strings.ToLower(protoApp.Health.String()))
|
|
||||||
if _, ok := codersdk.MapWorkspaceAppHealths[health]; !ok {
|
|
||||||
return codersdk.WorkspaceApp{}, xerrors.Errorf("unknown app health: %v (%q)", protoApp.Health, protoApp.Health.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return codersdk.WorkspaceApp{
|
|
||||||
ID: id,
|
|
||||||
URL: protoApp.Url,
|
|
||||||
External: protoApp.External,
|
|
||||||
Slug: protoApp.Slug,
|
|
||||||
DisplayName: protoApp.DisplayName,
|
|
||||||
Command: protoApp.Command,
|
|
||||||
Icon: protoApp.Icon,
|
|
||||||
Subdomain: protoApp.Subdomain,
|
|
||||||
SubdomainName: protoApp.SubdomainName,
|
|
||||||
SharingLevel: sharingLevel,
|
|
||||||
Healthcheck: codersdk.Healthcheck{
|
|
||||||
URL: protoApp.Healthcheck.Url,
|
|
||||||
Interval: int32(protoApp.Healthcheck.Interval.AsDuration().Seconds()),
|
|
||||||
Threshold: protoApp.Healthcheck.Threshold,
|
|
||||||
},
|
|
||||||
Health: health,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SDKServiceBannerFromProto(sbp *ServiceBanner) codersdk.ServiceBannerConfig {
|
|
||||||
return codersdk.ServiceBannerConfig{
|
|
||||||
Enabled: sbp.GetEnabled(),
|
|
||||||
Message: sbp.GetMessage(),
|
|
||||||
BackgroundColor: sbp.GetBackgroundColor(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServiceBannerFromSDK(sb codersdk.ServiceBannerConfig) *ServiceBanner {
|
|
||||||
return &ServiceBanner{
|
|
||||||
Enabled: sb.Enabled,
|
|
||||||
Message: sb.Message,
|
|
||||||
BackgroundColor: sb.BackgroundColor,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/coder/coder/v2/agent/proto"
|
"github.com/coder/coder/v2/agent/proto"
|
||||||
"github.com/coder/coder/v2/coderd/appearance"
|
"github.com/coder/coder/v2/coderd/appearance"
|
||||||
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceBannerAPI struct {
|
type ServiceBannerAPI struct {
|
||||||
|
@ -19,5 +20,5 @@ func (a *ServiceBannerAPI) GetServiceBanner(ctx context.Context, _ *proto.GetSer
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("fetch appearance: %w", err)
|
return nil, xerrors.Errorf("fetch appearance: %w", err)
|
||||||
}
|
}
|
||||||
return proto.ServiceBannerFromSDK(cfg.ServiceBanner), nil
|
return agentsdk.ProtoFromServiceBanner(cfg.ServiceBanner), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,60 +179,16 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
sdkManifest, err := agentsdk.ManifestFromProto(manifest)
|
||||||
apps, err := agentproto.SDKAppsFromProto(manifest.Apps)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||||
Message: "Internal error converting workspace agent apps.",
|
Message: "Internal error converting manifest.",
|
||||||
Detail: err.Error(),
|
Detail: err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
scripts, err := agentproto.SDKAgentScriptsFromProto(manifest.Scripts)
|
httpapi.Write(ctx, rw, http.StatusOK, sdkManifest)
|
||||||
if err != nil {
|
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
||||||
Message: "Internal error converting workspace agent scripts.",
|
|
||||||
Detail: err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, err := uuid.FromBytes(manifest.AgentId)
|
|
||||||
if err != nil {
|
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
||||||
Message: "Internal error converting workspace agent ID.",
|
|
||||||
Detail: err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
workspaceID, err := uuid.FromBytes(manifest.WorkspaceId)
|
|
||||||
if err != nil {
|
|
||||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
|
||||||
Message: "Internal error converting workspace ID.",
|
|
||||||
Detail: err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpapi.Write(ctx, rw, http.StatusOK, agentsdk.Manifest{
|
|
||||||
AgentID: agentID,
|
|
||||||
AgentName: manifest.AgentName,
|
|
||||||
OwnerName: manifest.OwnerUsername,
|
|
||||||
WorkspaceID: workspaceID,
|
|
||||||
WorkspaceName: manifest.WorkspaceName,
|
|
||||||
Apps: apps,
|
|
||||||
Scripts: scripts,
|
|
||||||
DERPMap: tailnet.DERPMapFromProto(manifest.DerpMap),
|
|
||||||
DERPForceWebSockets: manifest.DerpForceWebsockets,
|
|
||||||
GitAuthConfigs: int(manifest.GitAuthConfigs),
|
|
||||||
EnvironmentVariables: manifest.EnvironmentVariables,
|
|
||||||
Directory: manifest.Directory,
|
|
||||||
VSCodePortProxyURI: manifest.VsCodePortProxyUri,
|
|
||||||
MOTDFile: manifest.MotdPath,
|
|
||||||
DisableDirectConnections: manifest.DisableDirectConnections,
|
|
||||||
Metadata: agentproto.SDKAgentMetadataDescriptionsFromProto(manifest.Metadata),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AgentAPIVersionREST = "1.0"
|
const AgentAPIVersionREST = "1.0"
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
package agentsdk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/agent/proto"
|
||||||
|
"github.com/coder/coder/v2/codersdk"
|
||||||
|
"github.com/coder/coder/v2/tailnet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ManifestFromProto(manifest *proto.Manifest) (Manifest, error) {
|
||||||
|
apps, err := AppsFromProto(manifest.Apps)
|
||||||
|
if err != nil {
|
||||||
|
return Manifest{}, xerrors.Errorf("error converting workspace agent apps: %w", err)
|
||||||
|
}
|
||||||
|
scripts, err := AgentScriptsFromProto(manifest.Scripts)
|
||||||
|
if err != nil {
|
||||||
|
return Manifest{}, xerrors.Errorf("error converting workspace agent scripts: %w", err)
|
||||||
|
}
|
||||||
|
agentID, err := uuid.FromBytes(manifest.AgentId)
|
||||||
|
if err != nil {
|
||||||
|
return Manifest{}, xerrors.Errorf("error converting workspace agent ID: %w", err)
|
||||||
|
}
|
||||||
|
workspaceID, err := uuid.FromBytes(manifest.WorkspaceId)
|
||||||
|
if err != nil {
|
||||||
|
return Manifest{}, xerrors.Errorf("error converting workspace ID: %w", err)
|
||||||
|
}
|
||||||
|
return Manifest{
|
||||||
|
AgentID: agentID,
|
||||||
|
AgentName: manifest.AgentName,
|
||||||
|
OwnerName: manifest.OwnerUsername,
|
||||||
|
WorkspaceID: workspaceID,
|
||||||
|
WorkspaceName: manifest.WorkspaceName,
|
||||||
|
Apps: apps,
|
||||||
|
Scripts: scripts,
|
||||||
|
DERPMap: tailnet.DERPMapFromProto(manifest.DerpMap),
|
||||||
|
DERPForceWebSockets: manifest.DerpForceWebsockets,
|
||||||
|
GitAuthConfigs: int(manifest.GitAuthConfigs),
|
||||||
|
EnvironmentVariables: manifest.EnvironmentVariables,
|
||||||
|
Directory: manifest.Directory,
|
||||||
|
VSCodePortProxyURI: manifest.VsCodePortProxyUri,
|
||||||
|
MOTDFile: manifest.MotdPath,
|
||||||
|
DisableDirectConnections: manifest.DisableDirectConnections,
|
||||||
|
Metadata: MetadataDescriptionsFromProto(manifest.Metadata),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromManifest(manifest Manifest) (*proto.Manifest, error) {
|
||||||
|
apps, err := ProtoFromApps(manifest.Apps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("convert workspace apps: %w", err)
|
||||||
|
}
|
||||||
|
return &proto.Manifest{
|
||||||
|
AgentId: manifest.AgentID[:],
|
||||||
|
AgentName: manifest.AgentName,
|
||||||
|
OwnerUsername: manifest.OwnerName,
|
||||||
|
WorkspaceId: manifest.WorkspaceID[:],
|
||||||
|
WorkspaceName: manifest.WorkspaceName,
|
||||||
|
GitAuthConfigs: uint32(manifest.GitAuthConfigs),
|
||||||
|
EnvironmentVariables: manifest.EnvironmentVariables,
|
||||||
|
Directory: manifest.Directory,
|
||||||
|
VsCodePortProxyUri: manifest.VSCodePortProxyURI,
|
||||||
|
MotdPath: manifest.MOTDFile,
|
||||||
|
DisableDirectConnections: manifest.DisableDirectConnections,
|
||||||
|
DerpForceWebsockets: manifest.DERPForceWebSockets,
|
||||||
|
DerpMap: tailnet.DERPMapToProto(manifest.DERPMap),
|
||||||
|
Scripts: ProtoFromScripts(manifest.Scripts),
|
||||||
|
Apps: apps,
|
||||||
|
Metadata: ProtoFromMetadataDescriptions(manifest.Metadata),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MetadataDescriptionsFromProto(descriptions []*proto.WorkspaceAgentMetadata_Description) []codersdk.WorkspaceAgentMetadataDescription {
|
||||||
|
ret := make([]codersdk.WorkspaceAgentMetadataDescription, len(descriptions))
|
||||||
|
for i, description := range descriptions {
|
||||||
|
ret[i] = MetadataDescriptionFromProto(description)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromMetadataDescriptions(descriptions []codersdk.WorkspaceAgentMetadataDescription) []*proto.WorkspaceAgentMetadata_Description {
|
||||||
|
ret := make([]*proto.WorkspaceAgentMetadata_Description, len(descriptions))
|
||||||
|
for i, d := range descriptions {
|
||||||
|
ret[i] = ProtoFromMetadataDescription(d)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func MetadataDescriptionFromProto(description *proto.WorkspaceAgentMetadata_Description) codersdk.WorkspaceAgentMetadataDescription {
|
||||||
|
return codersdk.WorkspaceAgentMetadataDescription{
|
||||||
|
DisplayName: description.DisplayName,
|
||||||
|
Key: description.Key,
|
||||||
|
Script: description.Script,
|
||||||
|
Interval: int64(description.Interval.AsDuration()),
|
||||||
|
Timeout: int64(description.Timeout.AsDuration()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromMetadataDescription(d codersdk.WorkspaceAgentMetadataDescription) *proto.WorkspaceAgentMetadata_Description {
|
||||||
|
return &proto.WorkspaceAgentMetadata_Description{
|
||||||
|
DisplayName: d.DisplayName,
|
||||||
|
Key: d.Key,
|
||||||
|
Script: d.Script,
|
||||||
|
Interval: durationpb.New(time.Duration(d.Interval)),
|
||||||
|
Timeout: durationpb.New(time.Duration(d.Timeout)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AgentScriptsFromProto(protoScripts []*proto.WorkspaceAgentScript) ([]codersdk.WorkspaceAgentScript, error) {
|
||||||
|
ret := make([]codersdk.WorkspaceAgentScript, len(protoScripts))
|
||||||
|
for i, protoScript := range protoScripts {
|
||||||
|
app, err := AgentScriptFromProto(protoScript)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parse script %v: %w", i, err)
|
||||||
|
}
|
||||||
|
ret[i] = app
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromScripts(scripts []codersdk.WorkspaceAgentScript) []*proto.WorkspaceAgentScript {
|
||||||
|
ret := make([]*proto.WorkspaceAgentScript, len(scripts))
|
||||||
|
for i, script := range scripts {
|
||||||
|
ret[i] = ProtoFromScript(script)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func AgentScriptFromProto(protoScript *proto.WorkspaceAgentScript) (codersdk.WorkspaceAgentScript, error) {
|
||||||
|
id, err := uuid.FromBytes(protoScript.LogSourceId)
|
||||||
|
if err != nil {
|
||||||
|
return codersdk.WorkspaceAgentScript{}, xerrors.Errorf("parse id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return codersdk.WorkspaceAgentScript{
|
||||||
|
LogSourceID: id,
|
||||||
|
LogPath: protoScript.LogPath,
|
||||||
|
Script: protoScript.Script,
|
||||||
|
Cron: protoScript.Cron,
|
||||||
|
RunOnStart: protoScript.RunOnStart,
|
||||||
|
RunOnStop: protoScript.RunOnStop,
|
||||||
|
StartBlocksLogin: protoScript.StartBlocksLogin,
|
||||||
|
Timeout: protoScript.Timeout.AsDuration(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromScript(s codersdk.WorkspaceAgentScript) *proto.WorkspaceAgentScript {
|
||||||
|
return &proto.WorkspaceAgentScript{
|
||||||
|
LogSourceId: s.LogSourceID[:],
|
||||||
|
LogPath: s.LogPath,
|
||||||
|
Script: s.Script,
|
||||||
|
Cron: s.Cron,
|
||||||
|
RunOnStart: s.RunOnStart,
|
||||||
|
RunOnStop: s.RunOnStop,
|
||||||
|
StartBlocksLogin: s.StartBlocksLogin,
|
||||||
|
Timeout: durationpb.New(s.Timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppsFromProto(protoApps []*proto.WorkspaceApp) ([]codersdk.WorkspaceApp, error) {
|
||||||
|
ret := make([]codersdk.WorkspaceApp, len(protoApps))
|
||||||
|
for i, protoApp := range protoApps {
|
||||||
|
app, err := AppFromProto(protoApp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parse app %v (%q): %w", i, protoApp.Slug, err)
|
||||||
|
}
|
||||||
|
ret[i] = app
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromApps(apps []codersdk.WorkspaceApp) ([]*proto.WorkspaceApp, error) {
|
||||||
|
ret := make([]*proto.WorkspaceApp, len(apps))
|
||||||
|
var err error
|
||||||
|
for i, a := range apps {
|
||||||
|
ret[i], err = ProtoFromApp(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppFromProto(protoApp *proto.WorkspaceApp) (codersdk.WorkspaceApp, error) {
|
||||||
|
id, err := uuid.FromBytes(protoApp.Id)
|
||||||
|
if err != nil {
|
||||||
|
return codersdk.WorkspaceApp{}, xerrors.Errorf("parse id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharingLevel := codersdk.WorkspaceAppSharingLevel(strings.ToLower(protoApp.SharingLevel.String()))
|
||||||
|
if _, ok := codersdk.MapWorkspaceAppSharingLevels[sharingLevel]; !ok {
|
||||||
|
return codersdk.WorkspaceApp{}, xerrors.Errorf("unknown app sharing level: %v (%q)", protoApp.SharingLevel, protoApp.SharingLevel.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
health := codersdk.WorkspaceAppHealth(strings.ToLower(protoApp.Health.String()))
|
||||||
|
if _, ok := codersdk.MapWorkspaceAppHealths[health]; !ok {
|
||||||
|
return codersdk.WorkspaceApp{}, xerrors.Errorf("unknown app health: %v (%q)", protoApp.Health, protoApp.Health.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return codersdk.WorkspaceApp{
|
||||||
|
ID: id,
|
||||||
|
URL: protoApp.Url,
|
||||||
|
External: protoApp.External,
|
||||||
|
Slug: protoApp.Slug,
|
||||||
|
DisplayName: protoApp.DisplayName,
|
||||||
|
Command: protoApp.Command,
|
||||||
|
Icon: protoApp.Icon,
|
||||||
|
Subdomain: protoApp.Subdomain,
|
||||||
|
SubdomainName: protoApp.SubdomainName,
|
||||||
|
SharingLevel: sharingLevel,
|
||||||
|
Healthcheck: codersdk.Healthcheck{
|
||||||
|
URL: protoApp.Healthcheck.Url,
|
||||||
|
Interval: int32(protoApp.Healthcheck.Interval.AsDuration().Seconds()),
|
||||||
|
Threshold: protoApp.Healthcheck.Threshold,
|
||||||
|
},
|
||||||
|
Health: health,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromApp(a codersdk.WorkspaceApp) (*proto.WorkspaceApp, error) {
|
||||||
|
sharingLevel, ok := proto.WorkspaceApp_SharingLevel_value[strings.ToUpper(string(a.SharingLevel))]
|
||||||
|
if !ok {
|
||||||
|
return nil, xerrors.Errorf("unknown sharing level %s", a.SharingLevel)
|
||||||
|
}
|
||||||
|
health, ok := proto.WorkspaceApp_Health_value[strings.ToUpper(string(a.Health))]
|
||||||
|
if !ok {
|
||||||
|
return nil, xerrors.Errorf("unknown health %s", a.Health)
|
||||||
|
}
|
||||||
|
return &proto.WorkspaceApp{
|
||||||
|
Id: a.ID[:],
|
||||||
|
Url: a.URL,
|
||||||
|
External: a.External,
|
||||||
|
Slug: a.Slug,
|
||||||
|
DisplayName: a.DisplayName,
|
||||||
|
Command: a.Command,
|
||||||
|
Icon: a.Icon,
|
||||||
|
Subdomain: a.Subdomain,
|
||||||
|
SubdomainName: a.SubdomainName,
|
||||||
|
SharingLevel: proto.WorkspaceApp_SharingLevel(sharingLevel),
|
||||||
|
Healthcheck: &proto.WorkspaceApp_Healthcheck{
|
||||||
|
Url: a.Healthcheck.URL,
|
||||||
|
Interval: durationpb.New(time.Duration(a.Healthcheck.Interval) * time.Second),
|
||||||
|
Threshold: a.Healthcheck.Threshold,
|
||||||
|
},
|
||||||
|
Health: proto.WorkspaceApp_Health(health),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServiceBannerFromProto(sbp *proto.ServiceBanner) codersdk.ServiceBannerConfig {
|
||||||
|
return codersdk.ServiceBannerConfig{
|
||||||
|
Enabled: sbp.GetEnabled(),
|
||||||
|
Message: sbp.GetMessage(),
|
||||||
|
BackgroundColor: sbp.GetBackgroundColor(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtoFromServiceBanner(sb codersdk.ServiceBannerConfig) *proto.ServiceBanner {
|
||||||
|
return &proto.ServiceBanner{
|
||||||
|
Enabled: sb.Enabled,
|
||||||
|
Message: sb.Message,
|
||||||
|
BackgroundColor: sb.BackgroundColor,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
package agentsdk_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/codersdk"
|
||||||
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
|
"github.com/coder/coder/v2/tailnet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestManifest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
manifest := agentsdk.Manifest{
|
||||||
|
AgentID: uuid.New(),
|
||||||
|
AgentName: "test-agent",
|
||||||
|
OwnerName: "test-owner",
|
||||||
|
WorkspaceID: uuid.New(),
|
||||||
|
WorkspaceName: "test-workspace",
|
||||||
|
GitAuthConfigs: 3,
|
||||||
|
VSCodePortProxyURI: "http://proxy.example.com/stuff",
|
||||||
|
Apps: []codersdk.WorkspaceApp{
|
||||||
|
{
|
||||||
|
ID: uuid.New(),
|
||||||
|
URL: "http://app1.example.com",
|
||||||
|
External: true,
|
||||||
|
Slug: "app1",
|
||||||
|
DisplayName: "App 1",
|
||||||
|
Command: "app1 -d",
|
||||||
|
Icon: "app1.png",
|
||||||
|
Subdomain: true,
|
||||||
|
SubdomainName: "app1.example.com",
|
||||||
|
SharingLevel: codersdk.WorkspaceAppSharingLevelAuthenticated,
|
||||||
|
Healthcheck: codersdk.Healthcheck{
|
||||||
|
URL: "http://localhost:3030/healthz",
|
||||||
|
Interval: 55555666,
|
||||||
|
Threshold: 55555666,
|
||||||
|
},
|
||||||
|
Health: codersdk.WorkspaceAppHealthHealthy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: uuid.New(),
|
||||||
|
URL: "http://app2.example.com",
|
||||||
|
External: false,
|
||||||
|
Slug: "app2",
|
||||||
|
DisplayName: "App 2",
|
||||||
|
Command: "app2 -d",
|
||||||
|
Icon: "app2.png",
|
||||||
|
Subdomain: false,
|
||||||
|
SubdomainName: "app2.example.com",
|
||||||
|
SharingLevel: codersdk.WorkspaceAppSharingLevelPublic,
|
||||||
|
Healthcheck: codersdk.Healthcheck{
|
||||||
|
URL: "http://localhost:3032/healthz",
|
||||||
|
Interval: 22555666,
|
||||||
|
Threshold: 22555666,
|
||||||
|
},
|
||||||
|
Health: codersdk.WorkspaceAppHealthInitializing,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DERPMap: &tailcfg.DERPMap{
|
||||||
|
HomeParams: &tailcfg.DERPHomeParams{RegionScore: map[int]float64{999: 0.025}},
|
||||||
|
Regions: map[int]*tailcfg.DERPRegion{
|
||||||
|
999: {
|
||||||
|
EmbeddedRelay: true,
|
||||||
|
RegionID: 999,
|
||||||
|
RegionCode: "default",
|
||||||
|
RegionName: "HOME",
|
||||||
|
Avoid: false,
|
||||||
|
Nodes: []*tailcfg.DERPNode{
|
||||||
|
{
|
||||||
|
Name: "Home1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DERPForceWebSockets: true,
|
||||||
|
EnvironmentVariables: map[string]string{"FOO": "bar"},
|
||||||
|
Directory: "/home/coder",
|
||||||
|
MOTDFile: "/etc/motd",
|
||||||
|
DisableDirectConnections: true,
|
||||||
|
Metadata: []codersdk.WorkspaceAgentMetadataDescription{
|
||||||
|
{
|
||||||
|
DisplayName: "CPU",
|
||||||
|
Key: "cpu",
|
||||||
|
Script: "getcpu",
|
||||||
|
Interval: 44444422,
|
||||||
|
Timeout: 44444411,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DisplayName: "MEM",
|
||||||
|
Key: "mem",
|
||||||
|
Script: "getmem",
|
||||||
|
Interval: 54444422,
|
||||||
|
Timeout: 54444411,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scripts: []codersdk.WorkspaceAgentScript{
|
||||||
|
{
|
||||||
|
LogSourceID: uuid.New(),
|
||||||
|
LogPath: "/var/log/script.log",
|
||||||
|
Script: "script",
|
||||||
|
Cron: "somecron",
|
||||||
|
RunOnStart: true,
|
||||||
|
RunOnStop: true,
|
||||||
|
StartBlocksLogin: true,
|
||||||
|
Timeout: time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LogSourceID: uuid.New(),
|
||||||
|
LogPath: "/var/log/script2.log",
|
||||||
|
Script: "script2",
|
||||||
|
Cron: "somecron2",
|
||||||
|
RunOnStart: false,
|
||||||
|
RunOnStop: true,
|
||||||
|
StartBlocksLogin: true,
|
||||||
|
Timeout: time.Second * 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p, err := agentsdk.ProtoFromManifest(manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
back, err := agentsdk.ManifestFromProto(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, manifest.AgentID, back.AgentID)
|
||||||
|
require.Equal(t, manifest.AgentName, back.AgentName)
|
||||||
|
require.Equal(t, manifest.OwnerName, back.OwnerName)
|
||||||
|
require.Equal(t, manifest.WorkspaceID, back.WorkspaceID)
|
||||||
|
require.Equal(t, manifest.WorkspaceName, back.WorkspaceName)
|
||||||
|
require.Equal(t, manifest.GitAuthConfigs, back.GitAuthConfigs)
|
||||||
|
require.Equal(t, manifest.VSCodePortProxyURI, back.VSCodePortProxyURI)
|
||||||
|
require.Equal(t, manifest.Apps, back.Apps)
|
||||||
|
require.NotNil(t, back.DERPMap)
|
||||||
|
require.True(t, tailnet.CompareDERPMaps(manifest.DERPMap, back.DERPMap))
|
||||||
|
require.Equal(t, manifest.DERPForceWebSockets, back.DERPForceWebSockets)
|
||||||
|
require.Equal(t, manifest.EnvironmentVariables, back.EnvironmentVariables)
|
||||||
|
require.Equal(t, manifest.Directory, back.Directory)
|
||||||
|
require.Equal(t, manifest.MOTDFile, back.MOTDFile)
|
||||||
|
require.Equal(t, manifest.DisableDirectConnections, back.DisableDirectConnections)
|
||||||
|
require.Equal(t, manifest.Metadata, back.Metadata)
|
||||||
|
require.Equal(t, manifest.Scripts, back.Scripts)
|
||||||
|
}
|
|
@ -184,7 +184,7 @@ func requireGetServiceBanner(ctx context.Context, t *testing.T, client *agentsdk
|
||||||
aAPI := proto.NewDRPCAgentClient(cc)
|
aAPI := proto.NewDRPCAgentClient(cc)
|
||||||
sbp, err := aAPI.GetServiceBanner(ctx, &proto.GetServiceBannerRequest{})
|
sbp, err := aAPI.GetServiceBanner(ctx, &proto.GetServiceBannerRequest{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return proto.SDKServiceBannerFromProto(sbp)
|
return agentsdk.ServiceBannerFromProto(sbp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomSupportLinks(t *testing.T) {
|
func TestCustomSupportLinks(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue