mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
* Update proto * proto gen * protocol update * convert ide config * proto def +1 * proto gen +1 * papi impl +1 * impl + 2 * Update components/public-api-server/pkg/apiv1/workspace.go Co-authored-by: iQQBot <tianshi8650@gmail.com> --------- Co-authored-by: iQQBot <tianshi8650@gmail.com>
577 lines
18 KiB
Go
577 lines
18 KiB
Go
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package apiv1
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
connect "github.com/bufbuild/connect-go"
|
|
"github.com/gitpod-io/gitpod/common-go/experiments"
|
|
"github.com/gitpod-io/gitpod/common-go/log"
|
|
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
|
|
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
|
|
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
|
|
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
func NewWorkspaceService(serverConnPool proxy.ServerConnectionPool, expClient experiments.Client) *WorkspaceService {
|
|
return &WorkspaceService{
|
|
connectionPool: serverConnPool,
|
|
expClient: expClient,
|
|
}
|
|
}
|
|
|
|
type WorkspaceService struct {
|
|
connectionPool proxy.ServerConnectionPool
|
|
expClient experiments.Client
|
|
|
|
v1connect.UnimplementedWorkspacesServiceHandler
|
|
}
|
|
|
|
func (s *WorkspaceService) CreateAndStartWorkspace(ctx context.Context, req *connect.Request[v1.CreateAndStartWorkspaceRequest]) (*connect.Response[v1.CreateAndStartWorkspaceResponse], error) {
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ws, err := conn.CreateWorkspace(ctx, &protocol.CreateWorkspaceOptions{
|
|
ContextURL: req.Msg.GetContextUrl(),
|
|
OrganizationId: req.Msg.GetOrganizationId(),
|
|
IgnoreRunningWorkspaceOnSameCommit: req.Msg.GetIgnoreRunningWorkspaceOnSameCommit(),
|
|
IgnoreRunningPrebuild: req.Msg.GetIgnoreRunningPrebuild(),
|
|
AllowUsingPreviousPrebuilds: req.Msg.GetAllowUsingPreviousPrebuilds(),
|
|
ForceDefaultConfig: req.Msg.GetForceDefaultConfig(),
|
|
StartWorkspaceOptions: protocol.StartWorkspaceOptions{
|
|
WorkspaceClass: req.Msg.GetStartSpec().GetWorkspaceClass(),
|
|
Region: req.Msg.GetStartSpec().GetRegion(),
|
|
IdeSettings: &protocol.IDESettings{
|
|
DefaultIde: req.Msg.StartSpec.IdeSettings.GetDefaultIde(),
|
|
UseLatestVersion: req.Msg.StartSpec.IdeSettings.GetUseLatestVersion(),
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to create workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.CreateAndStartWorkspaceResponse{
|
|
WorkspaceId: ws.CreatedWorkspaceID,
|
|
}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) GetWorkspace(ctx context.Context, req *connect.Request[v1.GetWorkspaceRequest]) (*connect.Response[v1.GetWorkspaceResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ws, err := conn.GetWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
workspace, err := s.convertWorkspaceInfo(ctx, ws)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to convert workspace.")
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.GetWorkspaceResponse{
|
|
Result: workspace,
|
|
}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) StreamWorkspaceStatus(ctx context.Context, req *connect.Request[v1.StreamWorkspaceStatusRequest], stream *connect.ServerStream[v1.StreamWorkspaceStatusResponse]) error {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
workspace, err := conn.GetWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace.")
|
|
return proxy.ConvertError(err)
|
|
}
|
|
|
|
if workspace.LatestInstance == nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get latest instance.")
|
|
return connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("instance not found"))
|
|
}
|
|
|
|
ch, err := conn.WorkspaceUpdates(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace instance updates.")
|
|
return proxy.ConvertError(err)
|
|
}
|
|
|
|
for update := range ch {
|
|
instance, err := convertWorkspaceInstance(update, workspace.Workspace.Context, workspace.Workspace.Config, workspace.Workspace.Shareable)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to convert workspace instance.")
|
|
return proxy.ConvertError(err)
|
|
}
|
|
err = stream.Send(&v1.StreamWorkspaceStatusResponse{
|
|
Result: &v1.WorkspaceStatus{
|
|
Instance: instance,
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to stream workspace status.")
|
|
return proxy.ConvertError(err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *WorkspaceService) GetOwnerToken(ctx context.Context, req *connect.Request[v1.GetOwnerTokenRequest]) (*connect.Response[v1.GetOwnerTokenResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ownerToken, err := conn.GetOwnerToken(ctx, workspaceID)
|
|
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get owner token.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.GetOwnerTokenResponse{Token: ownerToken}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) ListWorkspaces(ctx context.Context, req *connect.Request[v1.ListWorkspacesRequest]) (*connect.Response[v1.ListWorkspacesResponse], error) {
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
limit, err := getLimitFromPagination(req.Msg.GetPagination())
|
|
if err != nil {
|
|
// getLimitFromPagination returns gRPC errors
|
|
return nil, err
|
|
}
|
|
serverResp, err := conn.GetWorkspaces(ctx, &protocol.GetWorkspacesOptions{
|
|
Limit: float64(limit),
|
|
OrganizationId: req.Msg.GetOrganizationId(),
|
|
})
|
|
if err != nil {
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
res := make([]*v1.Workspace, 0, len(serverResp))
|
|
for _, ws := range serverResp {
|
|
workspace, err := s.convertWorkspaceInfo(ctx, ws)
|
|
if err != nil {
|
|
// convertWorkspaceInfo returns gRPC errors
|
|
return nil, err
|
|
}
|
|
res = append(res, workspace)
|
|
}
|
|
|
|
return connect.NewResponse(
|
|
&v1.ListWorkspacesResponse{
|
|
Result: res,
|
|
},
|
|
), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) UpdatePort(ctx context.Context, req *connect.Request[v1.UpdatePortRequest]) (*connect.Response[v1.UpdatePortResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var portVisibility string
|
|
var portProtocol string
|
|
|
|
switch req.Msg.GetPort().GetPolicy() {
|
|
case v1.PortPolicy_PORT_POLICY_PRIVATE:
|
|
portVisibility = protocol.PortVisibilityPrivate
|
|
case v1.PortPolicy_PORT_POLICY_PUBLIC:
|
|
portVisibility = protocol.PortVisibilityPublic
|
|
default:
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Unknown port policy specified."))
|
|
}
|
|
switch req.Msg.GetPort().GetProtocol() {
|
|
case v1.PortProtocol_PORT_PROTOCOL_HTTP, v1.PortProtocol_PORT_PROTOCOL_UNSPECIFIED:
|
|
portProtocol = protocol.PortProtocolHTTP
|
|
case v1.PortProtocol_PORT_PROTOCOL_HTTPS:
|
|
portProtocol = protocol.PortProtocolHTTPS
|
|
default:
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Unknown port protocol specified."))
|
|
}
|
|
_, err = conn.OpenPort(ctx, workspaceID, &protocol.WorkspaceInstancePort{
|
|
Port: float64(req.Msg.Port.Port),
|
|
Visibility: portVisibility,
|
|
Protocol: portProtocol,
|
|
})
|
|
if err != nil {
|
|
log.Extract(ctx).Error("Failed to update port")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(
|
|
&v1.UpdatePortResponse{},
|
|
), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) StartWorkspace(ctx context.Context, req *connect.Request[v1.StartWorkspaceRequest]) (*connect.Response[v1.StartWorkspaceResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = conn.StartWorkspace(ctx, workspaceID, &protocol.StartWorkspaceOptions{})
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to start workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
ws, err := conn.GetWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
workspace, err := s.convertWorkspaceInfo(ctx, ws)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to convert workspace.")
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.StartWorkspaceResponse{Result: workspace}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) StopWorkspace(ctx context.Context, req *connect.Request[v1.StopWorkspaceRequest]) (*connect.Response[v1.StopWorkspaceResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conn.StopWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to stop workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
ws, err := conn.GetWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
workspace, err := s.convertWorkspaceInfo(ctx, ws)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to convert workspace.")
|
|
return nil, err
|
|
}
|
|
|
|
return connect.NewResponse(&v1.StopWorkspaceResponse{Result: workspace}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) DeleteWorkspace(ctx context.Context, req *connect.Request[v1.DeleteWorkspaceRequest]) (*connect.Response[v1.DeleteWorkspaceResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conn.DeleteWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to delete workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
return connect.NewResponse(&v1.DeleteWorkspaceResponse{}), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) ListWorkspaceClasses(ctx context.Context, req *connect.Request[v1.ListWorkspaceClassesRequest]) (*connect.Response[v1.ListWorkspaceClassesResponse], error) {
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
classes, err := conn.GetSupportedWorkspaceClasses(ctx)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace classes.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
res := make([]*v1.WorkspaceClass, 0, len(classes))
|
|
for _, c := range classes {
|
|
res = append(res, &v1.WorkspaceClass{
|
|
Id: c.ID,
|
|
DisplayName: c.DisplayName,
|
|
Description: c.Description,
|
|
IsDefault: c.IsDefault,
|
|
})
|
|
}
|
|
|
|
return connect.NewResponse(
|
|
&v1.ListWorkspaceClassesResponse{
|
|
Result: res,
|
|
},
|
|
), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) GetDefaultWorkspaceImage(ctx context.Context, req *connect.Request[v1.GetDefaultWorkspaceImageRequest]) (*connect.Response[v1.GetDefaultWorkspaceImageResponse], error) {
|
|
conn, err := getConnection(ctx, s.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wsImage, err := conn.GetDefaultWorkspaceImage(ctx, &protocol.GetDefaultWorkspaceImageParams{
|
|
WorkspaceID: req.Msg.GetWorkspaceId(),
|
|
})
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get default workspace image.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
source := v1.GetDefaultWorkspaceImageResponse_IMAGE_SOURCE_UNSPECIFIED
|
|
if wsImage.Source == protocol.WorkspaceImageSourceInstallation {
|
|
source = v1.GetDefaultWorkspaceImageResponse_IMAGE_SOURCE_INSTALLATION
|
|
} else if wsImage.Source == protocol.WorkspaceImageSourceOrganization {
|
|
source = v1.GetDefaultWorkspaceImageResponse_IMAGE_SOURCE_ORGANIZATION
|
|
}
|
|
|
|
return connect.NewResponse(&v1.GetDefaultWorkspaceImageResponse{
|
|
Image: wsImage.Image,
|
|
Source: source,
|
|
}), nil
|
|
}
|
|
|
|
func getLimitFromPagination(pagination *v1.Pagination) (int, error) {
|
|
const (
|
|
defaultLimit = 20
|
|
maxLimit = 100
|
|
)
|
|
|
|
if pagination == nil {
|
|
return defaultLimit, nil
|
|
}
|
|
if pagination.PageSize == 0 {
|
|
return defaultLimit, nil
|
|
}
|
|
if pagination.PageSize < 0 || maxLimit < pagination.PageSize {
|
|
return 0, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid pagination page size (must be 0 < x < %d)", maxLimit))
|
|
}
|
|
|
|
return int(pagination.PageSize), nil
|
|
}
|
|
|
|
func (s *WorkspaceService) convertWorkspaceInfo(ctx context.Context, input *protocol.WorkspaceInfo) (*v1.Workspace, error) {
|
|
return convertWorkspaceInfo(input)
|
|
}
|
|
|
|
// convertWorkspaceInfo converts a "protocol workspace" to a "public API workspace". Returns gRPC errors if things go wrong.
|
|
func convertWorkspaceInfo(input *protocol.WorkspaceInfo) (*v1.Workspace, error) {
|
|
instance, err := convertWorkspaceInstance(input.LatestInstance, input.Workspace.Context, input.Workspace.Config, input.Workspace.Shareable)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v1.Workspace{
|
|
WorkspaceId: input.Workspace.ID,
|
|
OwnerId: input.Workspace.OwnerID,
|
|
ProjectId: "",
|
|
Context: &v1.WorkspaceContext{
|
|
ContextUrl: input.Workspace.ContextURL,
|
|
Details: &v1.WorkspaceContext_Git_{Git: &v1.WorkspaceContext_Git{
|
|
NormalizedContextUrl: input.Workspace.Context.NormalizedContextURL,
|
|
Repository: &v1.WorkspaceContext_Repository{
|
|
Name: input.Workspace.Context.Repository.Name,
|
|
Owner: input.Workspace.Context.Repository.Owner,
|
|
},
|
|
}},
|
|
},
|
|
Description: input.Workspace.Description,
|
|
Status: &v1.WorkspaceStatus{
|
|
Instance: instance,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func convertIdeConfig(ideConfig *protocol.WorkspaceInstanceIDEConfig) *v1.WorkspaceInstanceStatus_EditorReference {
|
|
if ideConfig == nil {
|
|
return nil
|
|
}
|
|
ideVersion := "stable"
|
|
if ideConfig.UseLatest {
|
|
ideVersion = "latest"
|
|
}
|
|
return &v1.WorkspaceInstanceStatus_EditorReference{
|
|
Name: ideConfig.IDE,
|
|
Version: ideVersion,
|
|
PreferToolbox: ideConfig.PreferToolbox,
|
|
}
|
|
}
|
|
|
|
func convertWorkspaceInstance(wsi *protocol.WorkspaceInstance, wsCtx *protocol.WorkspaceContext, config *protocol.WorkspaceConfig, shareable bool) (*v1.WorkspaceInstance, error) {
|
|
if wsi == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
creationTime, err := parseGitpodTimestamp(wsi.CreationTime)
|
|
if err != nil {
|
|
// TODO(cw): should this really return an error and possibly fail the entire operation?
|
|
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("cannot parse creation time: %v", err))
|
|
}
|
|
|
|
var phase v1.WorkspaceInstanceStatus_Phase
|
|
switch wsi.Status.Phase {
|
|
case "unknown":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_UNSPECIFIED
|
|
case "preparing":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_PREPARING
|
|
case "building":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_IMAGEBUILD
|
|
case "pending":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_PENDING
|
|
case "creating":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_CREATING
|
|
case "initializing":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_INITIALIZING
|
|
case "running":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_RUNNING
|
|
case "interrupted":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_INTERRUPTED
|
|
case "stopping":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_STOPPING
|
|
case "stopped":
|
|
phase = v1.WorkspaceInstanceStatus_PHASE_STOPPED
|
|
default:
|
|
// TODO(cw): should this really return an error and possibly fail the entire operation?
|
|
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("cannot convert instance phase: %s", wsi.Status.Phase))
|
|
}
|
|
|
|
var admissionLevel v1.AdmissionLevel
|
|
if shareable {
|
|
admissionLevel = v1.AdmissionLevel_ADMISSION_LEVEL_EVERYONE
|
|
} else {
|
|
admissionLevel = v1.AdmissionLevel_ADMISSION_LEVEL_OWNER_ONLY
|
|
}
|
|
|
|
var firstUserActivity *timestamppb.Timestamp
|
|
if fua := wsi.Status.Conditions.FirstUserActivity; fua != "" {
|
|
firstUserActivity, _ = parseGitpodTimestamp(fua)
|
|
}
|
|
|
|
var ports []*v1.Port
|
|
for _, p := range wsi.Status.ExposedPorts {
|
|
port := &v1.Port{
|
|
Port: uint64(p.Port),
|
|
Url: p.URL,
|
|
}
|
|
if p.Visibility == protocol.PortVisibilityPublic {
|
|
port.Policy = v1.PortPolicy_PORT_POLICY_PUBLIC
|
|
} else {
|
|
port.Policy = v1.PortPolicy_PORT_POLICY_PRIVATE
|
|
}
|
|
if p.Protocol == protocol.PortProtocolHTTPS {
|
|
port.Protocol = v1.PortProtocol_PORT_PROTOCOL_HTTPS
|
|
} else {
|
|
port.Protocol = v1.PortProtocol_PORT_PROTOCOL_HTTP
|
|
}
|
|
|
|
ports = append(ports, port)
|
|
}
|
|
|
|
// Calculate initial workspace folder location
|
|
var recentFolders []string
|
|
location := ""
|
|
if config != nil {
|
|
location = config.WorkspaceLocation
|
|
if location == "" {
|
|
location = config.CheckoutLocation
|
|
}
|
|
}
|
|
if location == "" && wsCtx != nil && wsCtx.Repository != nil {
|
|
location = wsCtx.Repository.Name
|
|
|
|
}
|
|
recentFolders = append(recentFolders, filepath.Join("/workspace", location))
|
|
|
|
gitStatus := convertGitStatus(wsi.GitStatus)
|
|
|
|
return &v1.WorkspaceInstance{
|
|
InstanceId: wsi.ID,
|
|
WorkspaceId: wsi.WorkspaceID,
|
|
CreatedAt: creationTime,
|
|
Status: &v1.WorkspaceInstanceStatus{
|
|
StatusVersion: uint64(wsi.Status.Version),
|
|
Phase: phase,
|
|
Message: wsi.Status.Message,
|
|
Url: wsi.IdeURL,
|
|
Admission: admissionLevel,
|
|
Conditions: &v1.WorkspaceInstanceStatus_Conditions{
|
|
Failed: wsi.Status.Conditions.Failed,
|
|
Timeout: wsi.Status.Conditions.Timeout,
|
|
FirstUserActivity: firstUserActivity,
|
|
},
|
|
Ports: ports,
|
|
RecentFolders: recentFolders,
|
|
GitStatus: gitStatus,
|
|
Editor: convertIdeConfig(wsi.Configuration.IDEConfig),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func convertGitStatus(repo *protocol.WorkspaceInstanceRepoStatus) *v1.GitStatus {
|
|
if repo == nil {
|
|
return nil
|
|
}
|
|
return &v1.GitStatus{
|
|
Branch: repo.Branch,
|
|
LatestCommit: repo.LatestCommit,
|
|
TotalUncommitedFiles: int32(repo.TotalUncommitedFiles),
|
|
TotalUntrackedFiles: int32(repo.TotalUntrackedFiles),
|
|
TotalUnpushedCommits: int32(repo.TotalUnpushedCommits),
|
|
UncommitedFiles: repo.UncommitedFiles,
|
|
UntrackedFiles: repo.UntrackedFiles,
|
|
UnpushedCommits: repo.UnpushedCommits,
|
|
}
|
|
}
|