Skip to content

Commit

Permalink
Merge pull request #779 from sttts/sttts-kcp-logicalcluster-kubectl-k…
Browse files Browse the repository at this point in the history
…cp-create-context

6. kubectl-kcp: add create-context command
  • Loading branch information
sttts authored Apr 1, 2022
2 parents d204d62 + 061a716 commit bec9897
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 19 deletions.
44 changes: 38 additions & 6 deletions pkg/cliplugins/workspace/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ var (
# create a workspace and immediately enter it
%[1]s workspace create my-workspace --use
# create a context with the current workspace, e.g. root:default:my-workspace
%[1]s workspace create-context
# create a context with the current workspace, named context-name
%[1]s workspace create-context context-name
`
)

Expand Down Expand Up @@ -78,7 +84,7 @@ func NewCmdWorkspace(streams genericclioptions.IOStreams) (*cobra.Command, error

cmd := &cobra.Command{
Aliases: []string{"ws", "workspaces"},
Use: "workspace [list|create|<workspace>|..|-|<root:absolute:workspace>]",
Use: "workspace [list|create|create-context|<workspace>|..|-|<root:absolute:workspace>]",
Short: "Manages KCP workspaces",
Example: fmt.Sprintf(workspaceExample, "kubectl kcp"),
SilenceUsage: true,
Expand Down Expand Up @@ -143,15 +149,11 @@ func NewCmdWorkspace(streams genericclioptions.IOStreams) (*cobra.Command, error
if err := opts.Validate(); err != nil {
return err
}

kubeconfig, err := plugin.NewKubeConfig(opts)
if err != nil {
return err
}
if err := kubeconfig.CreateWorkspace(cmd.Context(), args[0], workspaceType, ignoreExisting, enterAfterCreation, time.Minute); err != nil {
return err
}
return nil
return kubeconfig.CreateWorkspace(cmd.Context(), args[0], workspaceType, ignoreExisting, enterAfterCreation, time.Minute)
},
}
createCmd.Flags().StringVar(&workspaceType, "type", "", "A workspace type (default: Universal)")
Expand All @@ -162,6 +164,35 @@ func NewCmdWorkspace(streams genericclioptions.IOStreams) (*cobra.Command, error
return nil, err
}

var overwriteContext bool
createContextCmd := &cobra.Command{
Use: "create-context [<context-name>] [--overwrite]",
Short: "Create a kubeconfig context for the current workspace",
Example: "kcp workspace create-context",
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
if err := opts.Validate(); err != nil {
return err
}
kubeconfig, err := plugin.NewKubeConfig(opts)
if err != nil {
return err
}

if len(args) > 1 {
return cmd.Help()
}

arg := ""
if len(args) == 1 {
arg = args[0]
}

return kubeconfig.CreateContext(cmd.Context(), arg, overwriteContext)
},
}
createContextCmd.Flags().BoolVar(&overwriteContext, "overwrite", overwriteContext, "Overwrite the context if it already exists")

deleteCmd := &cobra.Command{
Use: "delete",
Short: "Replaced with \"kubectl delete workspace <workspace-name>\"",
Expand All @@ -177,6 +208,7 @@ func NewCmdWorkspace(streams genericclioptions.IOStreams) (*cobra.Command, error
cmd.AddCommand(currentCmd)
cmd.AddCommand(listCmd)
cmd.AddCommand(createCmd)
cmd.AddCommand(createContextCmd)
cmd.AddCommand(deleteCmd)
return cmd, nil
}
58 changes: 55 additions & 3 deletions pkg/cliplugins/workspace/plugin/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (kc *KubeConfig) UseWorkspace(ctx context.Context, name string) error {
// Store the currentContext content for later to set as previous context
currentContext, found := kc.startingConfig.Contexts[kc.currentContext]
if !found {
return fmt.Errorf("current %q context not found", currentContext)
return fmt.Errorf("current %q context not found", kc.currentContext)
}

var newServerHost, workspaceType string
Expand Down Expand Up @@ -253,7 +253,7 @@ func (kc *KubeConfig) CurrentWorkspace(ctx context.Context) error {
func (kc *KubeConfig) currentWorkspace(ctx context.Context, host, workspaceType string) error {
_, clusterName, err := parseClusterURL(host)
if err != nil {
_, err = fmt.Fprintf(kc.Out, "Current workspace is the URL %q\n", host)
_, err = fmt.Fprintf(kc.Out, "Current workspace is the URL %q.\n", host)
return err
}

Expand All @@ -274,7 +274,7 @@ func (kc *KubeConfig) currentWorkspace(ctx context.Context, host, workspaceType
if workspaceName != workspacePrettyName {
message += fmt.Sprintf(" aliased as %q", workspacePrettyName)
}
_, err = fmt.Fprintln(kc.Out, message)
_, err = fmt.Fprintln(kc.Out, message+".")
return err
}

Expand Down Expand Up @@ -406,3 +406,55 @@ func (kc *KubeConfig) ListWorkspaces(ctx context.Context, opts *Options) error {

return printer.PrintObj(table, opts.Out)
}

func (kc *KubeConfig) CreateContext(ctx context.Context, name string, overwrite bool) error {
config, err := clientcmd.NewDefaultClientConfig(*kc.startingConfig, nil).RawConfig()
if err != nil {
return err
}
currentContext, ok := config.Contexts[config.CurrentContext]
if !ok {
return fmt.Errorf("current context %q is not found in kubeconfig", config.CurrentContext)
}
currentCluster, ok := config.Clusters[currentContext.Cluster]
if !ok {
return fmt.Errorf("current cluster %q is not found in kubeconfig", currentContext.Cluster)
}
_, currentClusterName, err := parseClusterURL(currentCluster.Server)
if err != nil {
return fmt.Errorf("current URL %q does not point to cluster workspace", currentCluster.Server)
}

if name == "" {
name = currentClusterName.String()
}

_, existedBefore := kc.startingConfig.Contexts[name]
if existedBefore && !overwrite {
return fmt.Errorf("context %q already exists in kubeconfig, use --overwrite to update it", name)
}

newKubeConfig := kc.startingConfig.DeepCopy()
newCluster := *currentCluster
newKubeConfig.Clusters[name] = &newCluster
newContext := *currentContext
newContext.Cluster = name
newKubeConfig.Contexts[name] = &newContext
newKubeConfig.CurrentContext = name

if err := kc.modifyConfig(newKubeConfig); err != nil {
return err
}

if existedBefore {
if kc.startingConfig.CurrentContext == name {
fmt.Fprintf(kc.Out, "Updated context %q.\n", name) // nolint: errcheck
} else {
fmt.Fprintf(kc.Out, "Updated context %q and switched to it.\n", name) // nolint: errcheck
}
} else {
fmt.Fprintf(kc.Out, "Created context %q and switched to it.\n", name) // nolint: errcheck
}

return nil
}
Loading

0 comments on commit bec9897

Please sign in to comment.