Added version tags, separated command implementaitons, added wait and timeout

Signed-off-by: Rishabh Gupta <r.g.gupta@outlook.com>
pull/4/head
Rishabh Gupta 5 years ago
parent 476e3de625
commit 29fced4ef7
  1. 12
      Makefile
  2. 161
      cli/commands.go
  3. 2
      cli/config.go
  4. 18
      cli/run.go
  5. 215
      main.go
  6. 12
      version/version.go

@ -4,21 +4,27 @@ SHELL := /bin/bash
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 windows/amd64
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256
# get git tag
GIT_TAG := $(shell git describe --tags)
ifeq ($(GIT_TAG),)
GIT_TAG := $(shell git describe --always)
endif
# Go options
GO ?= go
PKG := $(shell go mod vendor)
TAGS :=
TESTS := .
TESTFLAGS :=
LDFLAGS := -w -s
LDFLAGS := -w -s -X github.com/iwilltry42/k3d-go/version.Version=${GIT_TAG}
GOFLAGS :=
BINDIR := $(CURDIR)/bin
BINARIES := k3d
BINARIES := k3d
export GO111MODULE=on
# go source files, ignore vendor directory
SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
SRC = $(shell find . -type f -name '*.go' -not -path "./*/*")
.PHONY: all build build-cross clean fmt simplify check

@ -0,0 +1,161 @@
package run
import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"path"
"strings"
"time"
"github.com/urfave/cli"
)
// CheckTools checks if the installed tools work correctly
func CheckTools(c *cli.Context) error {
log.Print("Checking docker...")
cmd := "docker"
args := []string{"version"}
if err := runCommand(true, cmd, args...); err != nil {
log.Fatalf("Checking docker: FAILED")
return err
}
log.Println("Checking docker: SUCCESS")
return nil
}
// CreateCluster creates a new single-node cluster container and initializes the cluster directory
func CreateCluster(c *cli.Context) error {
if c.IsSet("timeout") && !c.IsSet("wait") {
return errors.New("--wait flag is not specified")
}
port := fmt.Sprintf("%s:%s", c.String("port"), c.String("port"))
image := fmt.Sprintf("rancher/k3s:%s", c.String("version"))
cmd := "docker"
args := []string{
"run",
"--name", c.String("name"),
"-e", "K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml",
"--publish", port,
"--privileged",
}
extraArgs := []string{}
if c.IsSet("volume") {
extraArgs = append(extraArgs, "--volume", c.String("volume"))
}
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
args = append(args,
"-d",
image,
"server", // cmd
"--https-listen-port", c.String("port"), //args
)
log.Printf("Creating cluster [%s]", c.String("name"))
if err := runCommand(true, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't create cluster [%s] -> %+v", c.String("name"), err)
return err
}
start := time.Now()
timeout := time.Duration(c.Int("timeout")) * time.Second
for c.IsSet("wait") {
if timeout != 0 && !time.Now().After(start.Add(timeout)) {
err := DeleteCluster(c)
if err != nil {
return err
}
return errors.New("Cluster timeout expired")
}
cmd := "docker"
args = []string{
"logs",
c.String("name"),
}
prog := exec.Command(cmd, args...)
output, err := prog.CombinedOutput()
if err != nil {
return err
}
if strings.Contains(string(output), "Running kubelet") {
break
}
time.Sleep(1 * time.Second)
}
createClusterDir(c.String("name"))
log.Printf("SUCCESS: created cluster [%s]", c.String("name"))
log.Printf(`You can now use the cluster with:
export KUBECONFIG="$(%s get-kubeconfig --name='%s')"
kubectl cluster-info`, os.Args[0], c.String("name"))
return nil
}
// DeleteCluster removes the cluster container and its cluster directory
func DeleteCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"rm", c.String("name")}
log.Printf("Deleting cluster [%s]", c.String("name"))
if err := runCommand(true, cmd, args...); err != nil {
log.Printf("WARNING: couldn't delete cluster [%s], trying a force remove now.", c.String("name"))
args = append(args, "-f")
if err := runCommand(true, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't delete cluster [%s] -> %+v", c.String("name"), err)
return err
}
}
deleteClusterDir(c.String("name"))
log.Printf("SUCCESS: deleted cluster [%s]", c.String("name"))
return nil
}
// StopCluster stops a running cluster container (restartable)
func StopCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"stop", c.String("name")}
log.Printf("Stopping cluster [%s]", c.String("name"))
if err := runCommand(true, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't stop cluster [%s] -> %+v", c.String("name"), err)
return err
}
log.Printf("SUCCESS: stopped cluster [%s]", c.String("name"))
return nil
}
// StartCluster starts a stopped cluster container
func StartCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"start", c.String("name")}
log.Printf("Starting cluster [%s]", c.String("name"))
if err := runCommand(true, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't start cluster [%s] -> %+v", c.String("name"), err)
return err
}
log.Printf("SUCCESS: started cluster [%s]", c.String("name"))
return nil
}
// ListClusters prints a list of created clusters
func ListClusters(c *cli.Context) error {
printClusters(c.Bool("all"))
return nil
}
// GetKubeConfig grabs the kubeconfig from the running cluster and prints the path to stdout
func GetKubeConfig(c *cli.Context) error {
sourcePath := fmt.Sprintf("%s:/output/kubeconfig.yaml", c.String("name"))
destPath, _ := getClusterDir(c.String("name"))
cmd := "docker"
args := []string{"cp", sourcePath, destPath}
if err := runCommand(false, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't get kubeconfig for cluster [%s] -> %+v", c.String("name"), err)
return err
}
fmt.Printf("%s\n", path.Join(destPath, "kubeconfig.yaml"))
return nil
}

@ -1,4 +1,4 @@
package main
package run
import (
"context"

@ -0,0 +1,18 @@
package run
import (
"log"
"os"
"os/exec"
)
// runCommand accepts the name and args and runs the specified command
func runCommand(verbose bool, name string, args ...string) error {
if verbose {
log.Printf("Running command: %+v", append([]string{name}, args...))
}
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

@ -1,178 +1,14 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path"
"github.com/iwilltry42/k3d-go/cli"
"github.com/iwilltry42/k3d-go/version"
"github.com/urfave/cli"
)
// createCluster creates a new single-node cluster container and initializes the cluster directory
func createCluster(c *cli.Context) error {
createClusterDir(c.String("name"))
port := fmt.Sprintf("%s:%s", c.String("port"), c.String("port"))
image := fmt.Sprintf("rancher/k3s:%s", c.String("version"))
cmd := "docker"
args := []string{
"run",
"--name", c.String("name"),
"-e", "K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml",
"--publish", port,
"--privileged",
}
extraArgs := []string{}
if c.IsSet("volume") {
extraArgs = append(extraArgs, "--volume", c.String("volume"))
}
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
args = append(args,
"-d",
image,
"server", // cmd
"--https-listen-port", c.String("port"), //args
)
log.Printf("Creating cluster [%s]", c.String("name"))
if err := run(true, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't create cluster [%s] -> %+v", c.String("name"), err)
return err
}
log.Printf("SUCCESS: created cluster [%s]", c.String("name"))
log.Printf(`You can now use the cluster with:
export KUBECONFIG="$(%s get-kubeconfig --name='%s')"
kubectl cluster-info`, os.Args[0], c.String("name"))
return nil
}
// deleteCluster removes the cluster container and its cluster directory
func deleteCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"rm"}
clusters := []string{}
// operate on one or all clusters
if !c.Bool("all") {
clusters = append(clusters, c.String("name"))
} else {
clusterList, err := getClusterNames()
if err != nil {
log.Fatalf("ERROR: `--all` specified, but no clusters were found.")
}
clusters = append(clusters, clusterList...)
}
// remove clusters one by one instead of appending all names to the docker command
// this allows for more granular error handling and logging
for _, cluster := range clusters {
log.Printf("Removing cluster [%s]", cluster)
args = append(args, cluster)
if err := run(true, cmd, args...); err != nil {
log.Printf("WARNING: couldn't delete cluster [%s], trying a force remove now.", cluster)
args = args[:len(args)-1] // pop last element from list (name of cluster)
args = append(args, "-f", cluster)
if err := run(true, cmd, args...); err != nil {
log.Printf("FAILURE: couldn't delete cluster [%s] -> %+v", cluster, err)
}
args = args[:len(args)-1] // pop last element from list (-f flag)
}
deleteClusterDir(cluster)
log.Printf("SUCCESS: removed cluster [%s]", cluster)
args = args[:len(args)-1] // pop last element from list (name of last cluster)
}
return nil
}
// stopCluster stops a running cluster container (restartable)
func stopCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"stop"}
clusters := []string{}
// operate on one or all clusters
if !c.Bool("all") {
clusters = append(clusters, c.String("name"))
} else {
clusterList, err := getClusterNames()
if err != nil {
log.Fatalf("ERROR: `--all` specified, but no clusters were found.")
}
clusters = append(clusters, clusterList...)
}
// stop clusters one by one instead of appending all names to the docker command
// this allows for more granular error handling and logging
for _, cluster := range clusters {
log.Printf("Starting cluster [%s]", cluster)
args = append(args, cluster)
if err := run(true, cmd, args...); err != nil {
log.Printf("FAILURE: couldn't stop cluster [%s] -> %+v", cluster, err)
}
log.Printf("SUCCESS: stopped cluster [%s]", cluster)
args = args[:len(args)-1] // pop last element from list (name of last cluster)
}
return nil
}
// startCluster starts a stopped cluster container
func startCluster(c *cli.Context) error {
cmd := "docker"
args := []string{"start"}
clusters := []string{}
// operate on one or all clusters
if !c.Bool("all") {
clusters = append(clusters, c.String("name"))
} else {
clusterList, err := getClusterNames()
if err != nil {
log.Fatalf("ERROR: `--all` specified, but no clusters were found.")
}
clusters = append(clusters, clusterList...)
}
// start clusters one by one instead of appending all names to the docker command
// this allows for more granular error handling and logging
for _, cluster := range clusters {
log.Printf("Starting cluster [%s]", cluster)
args = append(args, cluster)
if err := run(true, cmd, args...); err != nil {
log.Printf("FAILURE: couldn't start cluster [%s] -> %+v", cluster, err)
}
log.Printf("SUCCESS: started cluster [%s]", cluster)
args = args[:len(args)-1] // pop last element from list (name of last cluster)
}
return nil
}
// listClusters prints a list of created clusters
func listClusters(c *cli.Context) error {
printClusters(c.Bool("all"))
return nil
}
// getKubeConfig grabs the kubeconfig from the running cluster and prints the path to stdout
func getKubeConfig(c *cli.Context) error {
sourcePath := fmt.Sprintf("%s:/output/kubeconfig.yaml", c.String("name"))
destPath, _ := getClusterDir(c.String("name"))
cmd := "docker"
args := []string{"cp", sourcePath, destPath}
if err := run(false, cmd, args...); err != nil {
log.Fatalf("FAILURE: couldn't get kubeconfig for cluster [%s] -> %+v", c.String("name"), err)
return err
}
fmt.Printf("%s\n", path.Join(destPath, "kubeconfig.yaml"))
return nil
}
// main represents the CLI application
func main() {
@ -180,7 +16,7 @@ func main() {
app := cli.NewApp()
app.Name = "k3d"
app.Usage = "Run k3s in Docker!"
app.Version = "v0.1.1"
app.Version = version.GetVersion()
app.Authors = []cli.Author{
cli.Author{
Name: "iwilltry42",
@ -195,17 +31,7 @@ func main() {
Name: "check-tools",
Aliases: []string{"ct"},
Usage: "Check if docker is running",
Action: func(c *cli.Context) error {
log.Print("Checking docker...")
cmd := "docker"
args := []string{"version"}
if err := run(true, cmd, args...); err != nil {
log.Fatalf("Checking docker: FAILED")
return err
}
log.Println("Checking docker: SUCCESS")
return nil
},
Action: run.CheckTools,
},
{
// create creates a new k3s cluster in a container
@ -220,7 +46,7 @@ func main() {
},
cli.StringFlag{
Name: "volume, v",
Usage: "Mount a volume into the cluster node (Docker notation: `source:destination`",
Usage: "Mount a volume into the cluster node (Docker notation: `source:destination`)",
},
cli.StringFlag{
Name: "version",
@ -232,8 +58,17 @@ func main() {
Value: 6443,
Usage: "Set a port on which the ApiServer will listen",
},
cli.IntFlag{
Name: "timeout, t",
Value: 0,
Usage: "Set the timeout value when --wait flag is set",
},
cli.BoolFlag{
Name: "wait, w",
Usage: "Wait for the cluster to come up",
},
},
Action: createCluster,
Action: run.CreateCluster,
},
{
// delete deletes an existing k3s cluster (remove container and cluster directory)
@ -251,7 +86,7 @@ func main() {
Usage: "delete all existing clusters (this ignores the --name/-n flag)",
},
},
Action: deleteCluster,
Action: run.DeleteCluster,
},
{
// stop stopy a running cluster (its container) so it's restartable
@ -268,7 +103,7 @@ func main() {
Usage: "stop all running clusters (this ignores the --name/-n flag)",
},
},
Action: stopCluster,
Action: run.StopCluster,
},
{
// start restarts a stopped cluster container
@ -285,7 +120,7 @@ func main() {
Usage: "start all stopped clusters (this ignores the --name/-n flag)",
},
},
Action: startCluster,
Action: run.StartCluster,
},
{
// list prints a list of created clusters
@ -298,7 +133,7 @@ func main() {
Usage: "also show non-running clusters",
},
},
Action: listClusters,
Action: run.ListClusters,
},
{
// get-kubeconfig grabs the kubeconfig from the cluster and prints the path to it
@ -315,7 +150,7 @@ func main() {
Usage: "get kubeconfig for all clusters (this ignores the --name/-n flag)",
},
},
Action: getKubeConfig,
Action: run.GetKubeConfig,
},
}
@ -325,13 +160,3 @@ func main() {
log.Fatal(err)
}
}
func run(verbose bool, name string, args ...string) error {
if verbose {
log.Printf("Running command: %+v", append([]string{name}, args...))
}
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

@ -0,0 +1,12 @@
package version
// Version is the string that contains version
var Version string
// GetVersion returns the version for cli, it gets it from "git describe --tags" or returns "dev" when doing simple go build
func GetVersion() string {
if len(Version) == 0 {
return "dev"
}
return Version
}
Loading…
Cancel
Save