Skip to content

Commit

Permalink
Merge pull request #842 from jlandowner/share-workspace2
Browse files Browse the repository at this point in the history
Support sharing workspaces
  • Loading branch information
oruharo authored Jun 19, 2024
2 parents aea574b + 0ae93b3 commit 0bf903d
Show file tree
Hide file tree
Showing 35 changed files with 984 additions and 373 deletions.
7 changes: 4 additions & 3 deletions api/v1alpha1/user_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ type UserSpec struct {
}

type UserStatus struct {
Phase corev1.NamespacePhase `json:"phase,omitempty"`
Namespace ObjectRef `json:"namespace,omitempty"`
Addons []ObjectRef `json:"addons,omitempty"`
Phase corev1.NamespacePhase `json:"phase,omitempty"`
Namespace ObjectRef `json:"namespace,omitempty"`
Addons []ObjectRef `json:"addons,omitempty"`
SharedWorkspaces []ObjectRef `json:"sharedWorkspaces,omitempty"`
}

type UserAddon struct {
Expand Down
13 changes: 7 additions & 6 deletions api/v1alpha1/workspace_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ const (

// NetworkRule is an abstract network configuration rule for workspace
type NetworkRule struct {
Protocol string `json:"protocol"`
PortNumber int32 `json:"portNumber"`
CustomHostPrefix string `json:"customHostPrefix,omitempty"`
HTTPPath string `json:"httpPath,omitempty"`
TargetPortNumber *int32 `json:"targetPortNumber,omitempty"`
Public bool `json:"public"`
Protocol string `json:"protocol"`
PortNumber int32 `json:"portNumber"`
CustomHostPrefix string `json:"customHostPrefix,omitempty"`
HTTPPath string `json:"httpPath,omitempty"`
TargetPortNumber *int32 `json:"targetPortNumber,omitempty"`
Public bool `json:"public"`
AllowedUsers []string `json:"allowedUsers,omitempty"`
}

func HTTPUniqueKey(host, httpPath string) string {
Expand Down
12 changes: 12 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions charts/cosmo/crds/cosmo-workspace.github.io_users.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,56 @@ spec:
x-kubernetes-map-type: atomic
phase:
type: string
sharedWorkspaces:
items:
description: ObjectRef is a reference of resource which is created
by the Instance
properties:
apiVersion:
description: API version of the referent.
type: string
creationTimestamp:
format: date-time
type: string
fieldPath:
description: |-
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
type: array
type: object
type: object
served: true
Expand Down
4 changes: 4 additions & 0 deletions charts/cosmo/crds/cosmo-workspace.github.io_workspaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ spec:
description: NetworkRule is an abstract network configuration rule
for workspace
properties:
allowedUsers:
items:
type: string
type: array
customHostPrefix:
type: string
httpPath:
Expand Down
1 change: 1 addition & 0 deletions charts/cosmo/templates/cosmo-username-headers-addon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ spec:
headers:
customRequestHeaders:
X-Cosmo-UserName: '{{ print "{{USER_NAME}}" }}'
X-Cosmo-UserName-{{ print "{{USER_NAME}}" }}: '1'
customResponseHeaders:
X-Cosmo-UserName: '{{ print "{{USER_NAME}}" }}'
{{- end }}
50 changes: 50 additions & 0 deletions config/crd/bases/cosmo-workspace.github.io_users.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,56 @@ spec:
x-kubernetes-map-type: atomic
phase:
type: string
sharedWorkspaces:
items:
description: ObjectRef is a reference of resource which is created
by the Instance
properties:
apiVersion:
description: API version of the referent.
type: string
creationTimestamp:
format: date-time
type: string
fieldPath:
description: |-
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
type: array
type: object
type: object
served: true
Expand Down
4 changes: 4 additions & 0 deletions config/crd/bases/cosmo-workspace.github.io_workspaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ spec:
description: NetworkRule is an abstract network configuration rule
for workspace
properties:
allowedUsers:
items:
type: string
type: array
customHostPrefix:
type: string
httpPath:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ spec:
headers:
customRequestHeaders:
X-Cosmo-UserName: '{{USER_NAME}}'
X-Cosmo-UserName-{{USER_NAME}}: '1'
customResponseHeaders:
X-Cosmo-UserName: '{{USER_NAME}}'
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ spec:
headers:
customRequestHeaders:
X-Cosmo-UserName: "{{USER_NAME}}"
X-Cosmo-UserName-{{USER_NAME}}: "1"
customResponseHeaders:
X-Cosmo-UserName: "{{USER_NAME}}"
2 changes: 1 addition & 1 deletion internal/cmd/workspace/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (o *CreateOption) RunE(cmd *cobra.Command, args []string) error {
}

fmt.Fprintln(cmd.OutOrStdout(), color.GreenString("Successfully created workspace %s", o.WorkspaceName))
OutputTable(cmd.OutOrStdout(), []*dashv1alpha1.Workspace{ws})
OutputTable(cmd.OutOrStdout(), o.UserName, []*dashv1alpha1.Workspace{ws})

return nil
}
Expand Down
47 changes: 32 additions & 15 deletions internal/cmd/workspace/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/cosmo-workspace/cosmo/pkg/apiconv"
"github.com/cosmo-workspace/cosmo/pkg/cli"
"github.com/cosmo-workspace/cosmo/pkg/clog"
"github.com/cosmo-workspace/cosmo/pkg/kosmo"
dashv1alpha1 "github.com/cosmo-workspace/cosmo/proto/gen/dashboard/v1alpha1"
)

Expand Down Expand Up @@ -105,7 +106,7 @@ func (o *GetOption) RunE(cmd *cobra.Command, args []string) error {
users = u
}
for _, user := range users {
wss, err := o.ListWorkspaces(ctx, user.Name)
wss, err := o.ListWorkspaces(ctx, user.Name, !o.AllUsers)
if err != nil {
return err
}
Expand All @@ -116,14 +117,18 @@ func (o *GetOption) RunE(cmd *cobra.Command, args []string) error {

workspaces = o.ApplyFilters(workspaces)

username := o.UserName
if o.AllUsers {
username = ""
}
if o.OutputFormat == "yaml" {
o.OutputYAML(cmd.OutOrStdout(), workspaces)
return nil
} else if o.OutputFormat == "wide" {
OutputWideTable(cmd.OutOrStdout(), workspaces)
OutputWideTable(cmd.OutOrStdout(), username, workspaces)
return nil
} else {
OutputTable(cmd.OutOrStdout(), workspaces)
OutputTable(cmd.OutOrStdout(), username, workspaces)
return nil
}
}
Expand All @@ -136,11 +141,11 @@ func (o *GetOption) ListUsers(ctx context.Context) ([]*dashv1alpha1.User, error)
}
}

func (o *GetOption) ListWorkspaces(ctx context.Context, userName string) ([]*dashv1alpha1.Workspace, error) {
func (o *GetOption) ListWorkspaces(ctx context.Context, userName string, includeShared bool) ([]*dashv1alpha1.Workspace, error) {
if o.UseKubeAPI {
return o.listWorkspacesByKubeClient(ctx, userName)
return o.listWorkspacesByKubeClient(ctx, userName, includeShared)
} else {
return o.listWorkspacesWithDashClient(ctx, userName)
return o.listWorkspacesWithDashClient(ctx, userName, includeShared)
}
}

Expand All @@ -156,12 +161,14 @@ func (o *GetOption) listUsersWithDashClient(ctx context.Context) ([]*dashv1alpha
return res.Msg.Items, nil
}

func (o *GetOption) listWorkspacesWithDashClient(ctx context.Context, userName string) ([]*dashv1alpha1.Workspace, error) {
func (o *GetOption) listWorkspacesWithDashClient(ctx context.Context, userName string, includeShared bool) ([]*dashv1alpha1.Workspace, error) {
req := &dashv1alpha1.GetWorkspacesRequest{
UserName: userName,
WithRaw: ptr.To(o.OutputFormat == "yaml"),
UserName: userName,
WithRaw: ptr.To(o.OutputFormat == "yaml"),
IncludeShared: ptr.To(includeShared),
}
c := o.CosmoDashClient
o.Logr.DebugAll().Info("WorkspaceServiceClient.GetWorkspaces", "req", req)
res, err := c.WorkspaceServiceClient.GetWorkspaces(ctx, cli.NewRequestWithToken(req, o.CliConfig))
if err != nil {
return nil, fmt.Errorf("failed to connect dashboard server: %w", err)
Expand Down Expand Up @@ -210,37 +217,47 @@ func (o *GetOption) OutputYAML(w io.Writer, objs []*dashv1alpha1.Workspace) {
fmt.Fprintln(w, strings.Join(docs, "---\n"))
}

func OutputTable(out io.Writer, workspaces []*dashv1alpha1.Workspace) {
func OutputTable(out io.Writer, username string, workspaces []*dashv1alpha1.Workspace) {
data := [][]string{}

for _, v := range workspaces {
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, v.Status.Phase, v.Status.MainUrl})
mainURL := v.Status.MainUrl
if username != "" && v.OwnerName != username {
mainURL = "[shared workspace. see shared URLs by `cosmoctl ws get-network`]"
}
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, v.Status.Phase, mainURL})
}

cli.OutputTable(out,
[]string{"USER", "NAME", "TEMPLATE", "PHASE", "MAINURL"},
data)
}

func OutputWideTable(out io.Writer, workspaces []*dashv1alpha1.Workspace) {
func OutputWideTable(out io.Writer, username string, workspaces []*dashv1alpha1.Workspace) {
data := [][]string{}

for _, v := range workspaces {
mainURL := v.Status.MainUrl
if v.OwnerName != username {
mainURL = `[shared workspace. see shared URLs by "cosmoctl ws get-network"]`
}
vars := make([]string, 0, len(v.Spec.Vars))
for k, vv := range v.Spec.Vars {
vars = append(vars, fmt.Sprintf("%s=%s", k, vv))
}
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, strings.Join(vars, ","), v.Status.Phase, v.Status.MainUrl})
data = append(data, []string{v.OwnerName, v.Name, v.Spec.Template, strings.Join(vars, ","), v.Status.Phase, mainURL})
}

cli.OutputTable(out,
[]string{"USER", "NAME", "TEMPLATE", "VARS", "PHASE", "MAINURL"},
data)
}

func (o *GetOption) listWorkspacesByKubeClient(ctx context.Context, userName string) ([]*dashv1alpha1.Workspace, error) {
func (o *GetOption) listWorkspacesByKubeClient(ctx context.Context, userName string, includeShared bool) ([]*dashv1alpha1.Workspace, error) {
c := o.KosmoClient
workspaces, err := c.ListWorkspacesByUserName(ctx, userName)
workspaces, err := c.ListWorkspacesByUserName(ctx, userName, func(o *kosmo.ListWorkspacesOptions) {
o.IncludeShared = includeShared
})
if err != nil {
return nil, err
}
Expand Down
5 changes: 3 additions & 2 deletions internal/cmd/workspace/get_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -109,11 +110,11 @@ func (o *GetNetworkOption) OutputTable(w io.Writer, workspace *dashv1alpha1.Work
data := [][]string{}

for _, v := range workspace.Spec.Network {
data = append(data, []string{fmt.Sprintf("%d", v.PortNumber), v.CustomHostPrefix, v.HttpPath, strconv.FormatBool(v.Public), v.Url})
data = append(data, []string{fmt.Sprintf("%d", v.PortNumber), v.CustomHostPrefix, v.HttpPath, strconv.FormatBool(v.Public), strings.Join(v.AllowedUsers, ","), v.Url})
}

cli.OutputTable(w,
[]string{"PORT", "CUSTOM_HOST_PREFIX", "HTTP_PATH", "PUBLIC", "URL"},
[]string{"PORT", "CUSTOM_HOST_PREFIX", "HTTP_PATH", "PUBLIC", "ALLOWED_USERS", "URL"},
data)
}

Expand Down
3 changes: 2 additions & 1 deletion internal/cmd/workspace/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (o *UpdateOption) RunE(cmd *cobra.Command, args []string) error {
}

fmt.Fprintln(cmd.OutOrStdout(), color.GreenString("Successfully updated workspace %s", o.WorkspaceName))
OutputTable(cmd.OutOrStdout(), []*dashv1alpha1.Workspace{ws})
OutputTable(cmd.OutOrStdout(), o.UserName, []*dashv1alpha1.Workspace{ws})

return nil
}
Expand Down Expand Up @@ -171,6 +171,7 @@ func (o *UpdateOption) GetWorkspaceWithDashClient(ctx context.Context) (*dashv1a
UserName: o.UserName,
}
c := o.CosmoDashClient
o.Logr.DebugAll().Info("WorkspaceServiceClient.GetWorkspace", "req", req)
res, err := c.WorkspaceServiceClient.GetWorkspace(ctx, cli.NewRequestWithToken(req, o.CliConfig))
if err != nil {
return nil, fmt.Errorf("failed to connect dashboard server: %w", err)
Expand Down
Loading

0 comments on commit 0bf903d

Please sign in to comment.