Merge pull request #66 from andyz-dev/bash

[Feature] Add Bash shell support
pull/72/head
Thorsten Klein 5 years ago committed by GitHub
commit df5cc1da84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 90
      cli/cluster.go
  2. 63
      cli/commands.go
  3. 51
      cli/shell.go
  4. 22
      main.go

@ -1,8 +1,10 @@
package run
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path"
@ -85,6 +87,94 @@ func getClusterDir(name string) (string, error) {
return path.Join(homeDir, ".config", "k3d", name), nil
}
func getClusterKubeConfigPath(cluster string) (string, error) {
clusterDir, err := getClusterDir(cluster)
return path.Join(clusterDir, "kubeconfig.yaml"), err
}
func createKubeConfigFile(cluster string) error {
ctx := context.Background()
docker, err := client.NewEnvClient()
if err != nil {
return err
}
filters := filters.NewArgs()
filters.Add("label", "app=k3d")
filters.Add("label", fmt.Sprintf("cluster=%s", cluster))
filters.Add("label", "component=server")
server, err := docker.ContainerList(ctx, types.ContainerListOptions{
Filters: filters,
})
if err != nil {
return fmt.Errorf("Failed to get server container for cluster %s\n%+v", cluster, err)
}
if len(server) == 0 {
return fmt.Errorf("No server container for cluster %s", cluster)
}
// get kubeconfig file from container and read contents
reader, _, err := docker.CopyFromContainer(ctx, server[0].ID, "/output/kubeconfig.yaml")
if err != nil {
return fmt.Errorf("ERROR: couldn't copy kubeconfig.yaml from server container %s\n%+v", server[0].ID, err)
}
defer reader.Close()
readBytes, err := ioutil.ReadAll(reader)
if err != nil {
return fmt.Errorf("ERROR: couldn't read kubeconfig from container\n%+v", err)
}
// create destination kubeconfig file
destPath, err := getClusterKubeConfigPath(cluster)
if err != nil {
return err
}
kubeconfigfile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("ERROR: couldn't create kubeconfig file %s\n%+v", destPath, err)
}
defer kubeconfigfile.Close()
// write to file, skipping the first 512 bytes which contain file metadata and trimming any NULL characters
_, err = kubeconfigfile.Write(bytes.Trim(readBytes[512:], "\x00"))
if err != nil {
return fmt.Errorf("ERROR: couldn't write to kubeconfig.yaml\n%+v", err)
}
return nil
}
func getKubeConfig(cluster string) (string, error) {
kubeConfigPath, err := getClusterKubeConfigPath(cluster)
if err != nil {
return "", err
}
if clusters, err := getClusters(false, cluster); err != nil || len(clusters) != 1 {
if err != nil {
return "", err
}
return "", fmt.Errorf("Cluster %s does not exist", cluster)
}
// If kubeconfi.yaml has not been created, generate it now
if _, err := os.Stat(kubeConfigPath); err != nil {
if os.IsNotExist(err) {
if err = createKubeConfigFile(cluster); err != nil {
return "", err
}
} else {
return "", err
}
}
return kubeConfigPath, nil
}
// printClusters prints the names of existing clusters
func printClusters() {
clusters, err := getClusters(true, "")

@ -9,15 +9,12 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/urfave/cli"
@ -346,61 +343,21 @@ func ListClusters(c *cli.Context) error {
// GetKubeConfig grabs the kubeconfig from the running cluster and prints the path to stdout
func GetKubeConfig(c *cli.Context) error {
ctx := context.Background()
docker, err := client.NewEnvClient()
if err != nil {
return err
}
filters := filters.NewArgs()
filters.Add("label", "app=k3d")
filters.Add("label", fmt.Sprintf("cluster=%s", c.String("name")))
filters.Add("label", "component=server")
server, err := docker.ContainerList(ctx, types.ContainerListOptions{
Filters: filters,
})
if err != nil {
return fmt.Errorf("Failed to get server container for cluster %s\n%+v", c.String("name"), err)
}
if len(server) == 0 {
return fmt.Errorf("No server container for cluster %s", c.String("name"))
}
// get kubeconfig file from container and read contents
reader, _, err := docker.CopyFromContainer(ctx, server[0].ID, "/output/kubeconfig.yaml")
if err != nil {
return fmt.Errorf("ERROR: couldn't copy kubeconfig.yaml from server container %s\n%+v", server[0].ID, err)
}
defer reader.Close()
readBytes, err := ioutil.ReadAll(reader)
if err != nil {
return fmt.Errorf("ERROR: couldn't read kubeconfig from container\n%+v", err)
}
// create destination kubeconfig file
clusterDir, err := getClusterDir(c.String("name"))
destPath := fmt.Sprintf("%s/kubeconfig.yaml", clusterDir)
cluster := c.String("name")
kubeConfigPath, err := getKubeConfig(cluster)
if err != nil {
return err
}
kubeconfigfile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("ERROR: couldn't create kubeconfig.yaml in %s\n%+v", clusterDir, err)
}
defer kubeconfigfile.Close()
// output kubeconfig file path to stdout
fmt.Println(kubeConfigPath)
return nil
}
// write to file, skipping the first 512 bytes which contain file metadata and trimming any NULL characters
_, err = kubeconfigfile.Write(bytes.Trim(readBytes[512:], "\x00"))
if err != nil {
return fmt.Errorf("ERROR: couldn't write to kubeconfig.yaml\n%+v", err)
func Shell(c *cli.Context) error {
if c.String("shell") != "bash" {
return fmt.Errorf("%s is not supported. Only bash is supported", c.String("shell"))
}
// output kubeconfig file path to stdout
fmt.Println(destPath)
return nil
return bashShell(c.String("name"), c.String("command"))
}

@ -0,0 +1,51 @@
package run
import (
"fmt"
"os"
"os/exec"
)
func bashShell(cluster string, command string) error {
kubeConfigPath, err := getKubeConfig(cluster)
if err != nil {
return err
}
subShell := os.ExpandEnv("$__K3D_CLUSTER__")
if len(subShell) > 0 {
return fmt.Errorf("Error: Already in subshell of cluster %s", subShell)
}
bashPath, err := exec.LookPath("bash")
if err != nil {
return err
}
cmd := exec.Command(bashPath, "--noprofile", "--norc")
if len(command) > 0 {
cmd.Args = append(cmd.Args, "-c", command)
}
// Set up stdio
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
// Set up Promot
setPS1 := fmt.Sprintf("PS1=[%s}%s", cluster, os.Getenv("PS1"))
// Set up KUBECONFIG
setKube := fmt.Sprintf("KUBECONFIG=%s", kubeConfigPath)
// Declare subshell
subShell = fmt.Sprintf("__K3D_CLUSTER__=%s", cluster)
newEnv := append(os.Environ(), setPS1, setKube, subShell)
cmd.Env = newEnv
return cmd.Run()
}

@ -45,6 +45,28 @@ func main() {
Usage: "Check if docker is running",
Action: run.CheckTools,
},
{
// shell starts a shell in the context of a running cluster
Name: "shell",
Usage: "Start a subshell for a cluster",
Flags: []cli.Flag{
cli.StringFlag{
Name: "name, n",
Value: defaultK3sClusterName,
Usage: "Set a name for the cluster",
},
cli.StringFlag{
Name: "command, c",
Usage: "Run a shell command in the context of the cluster",
},
cli.StringFlag{
Name: "shell, s",
Value: "bash",
Usage: "Sub shell type. Only bash is supported. (default bash)",
},
},
Action: run.Shell,
},
{
// create creates a new k3s cluster in docker containers
Name: "create",

Loading…
Cancel
Save