mirror of
https://github.com/kubernetes-sigs/kind.git
synced 2025-12-01 07:26:05 +07:00
@@ -77,7 +77,7 @@ kind create cluster --image kindest/node:latest
|
||||
Multi-node clusters and other advanced features may be configured with a config
|
||||
file, for more usage see [the docs][user guide] or run `kind [command] --help`
|
||||
|
||||
## Community, discussion, contribution, and support
|
||||
## Community
|
||||
|
||||
Please reach out for bugs, feature requests, and other issues!
|
||||
The maintainers of this project are reachable via:
|
||||
@@ -90,7 +90,10 @@ Current maintainers are [@BenTheElder] and [@munnerz] - feel free to
|
||||
reach out if you have any questions!
|
||||
|
||||
Pull Requests are very welcome!
|
||||
See the [issue tracker] if you're unsure where to start, or feel free to reach out to discuss.
|
||||
If you're planning a new feature, please file an issue to discuss first.
|
||||
|
||||
Check the [issue tracker] for `help wanted` issues if you're unsure where to
|
||||
start, or feel free to reach out to discuss. 🙂
|
||||
|
||||
See also: our own [contributor guide] and the Kubernetes [community page].
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.13
|
||||
require (
|
||||
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible
|
||||
github.com/mattn/go-colorable v0.1.4
|
||||
github.com/mattn/go-isatty v0.0.10
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/spf13/cobra v0.0.5
|
||||
|
||||
4
go.sum
4
go.sum
@@ -52,6 +52,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@@ -98,6 +101,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
|
||||
|
||||
@@ -107,3 +107,20 @@ func CreateWithStopBeforeSettingUpKubernetes(stopBeforeSettingUpKubernetes bool)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CreateWithDisplayUsage enables displaying usage if displayUsage is true
|
||||
func CreateWithDisplayUsage(displayUsage bool) CreateOption {
|
||||
return createOptionAdapter(func(o *internalcreate.ClusterOptions) error {
|
||||
o.DisplayUsage = displayUsage
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CreateWithDisplaySalutation enables display a salutation t the end of create
|
||||
// cluster if displaySalutation is true
|
||||
func CreateWithDisplaySalutation(displaySalutation bool) CreateOption {
|
||||
return createOptionAdapter(func(o *internalcreate.ClusterOptions) error {
|
||||
o.DisplaySalutation = displaySalutation
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,6 +90,8 @@ func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole) error {
|
||||
cluster.CreateWithRetain(flags.Retain),
|
||||
cluster.CreateWithWaitForReady(flags.Wait),
|
||||
cluster.CreateWithKubeconfigPath(flags.Kubeconfig),
|
||||
cluster.CreateWithDisplayUsage(true),
|
||||
cluster.CreateWithDisplaySalutation(true),
|
||||
); err != nil {
|
||||
if errs := errors.Errors(err); errs != nil {
|
||||
for _, problem := range errs {
|
||||
|
||||
@@ -18,6 +18,7 @@ package create
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
@@ -64,6 +65,9 @@ type ClusterOptions struct {
|
||||
KubeconfigPath string
|
||||
// see https://github.com/kubernetes-sigs/kind/issues/324
|
||||
StopBeforeSettingUpKubernetes bool // if false kind should setup kubernetes after creating nodes
|
||||
// Options to control output
|
||||
DisplayUsage bool
|
||||
DisplaySalutation bool
|
||||
}
|
||||
|
||||
// Cluster creates a cluster
|
||||
@@ -137,31 +141,49 @@ func Cluster(logger log.Logger, ctx *context.Context, opts *ClusterOptions) erro
|
||||
}
|
||||
}
|
||||
|
||||
// skip the rest if we're not setting up kubernetes
|
||||
if opts.StopBeforeSettingUpKubernetes {
|
||||
return nil
|
||||
}
|
||||
|
||||
return exportKubeconfig(logger, ctx, opts.KubeconfigPath)
|
||||
}
|
||||
|
||||
// exportKubeconfig exports the cluster's kubeconfig and prints usage
|
||||
func exportKubeconfig(logger log.Logger, ctx *context.Context, kubeconfigPath string) error {
|
||||
// actually export KUBECONFIG
|
||||
if err := kubeconfig.Export(ctx, kubeconfigPath); err != nil {
|
||||
if err := kubeconfig.Export(ctx, opts.KubeconfigPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// optionally display usage
|
||||
if opts.DisplayUsage {
|
||||
logUsage(logger, ctx, opts.KubeconfigPath)
|
||||
}
|
||||
// optionally give the user a friendly salutation
|
||||
if opts.DisplaySalutation {
|
||||
logger.V(0).Info("")
|
||||
logSalutation(logger)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func logUsage(logger log.Logger, ctx *context.Context, explicitKubeconfigPath string) {
|
||||
// construct a sample command for interacting with the cluster
|
||||
kctx := kubeconfig.ContextForCluster(ctx.Name())
|
||||
sampleCommand := fmt.Sprintf("kubectl cluster-info --context %s", kctx)
|
||||
if kubeconfigPath != "" {
|
||||
if explicitKubeconfigPath != "" {
|
||||
// explicit path, include this
|
||||
sampleCommand += " --kubeconfig " + shellescape.Quote(kubeconfigPath)
|
||||
sampleCommand += " --kubeconfig " + shellescape.Quote(explicitKubeconfigPath)
|
||||
}
|
||||
|
||||
logger.V(0).Infof(`Set kubectl context to "%s"`, kctx)
|
||||
logger.V(0).Infof("You can now use your cluster with:\n\n" + sampleCommand)
|
||||
return nil
|
||||
}
|
||||
|
||||
func logSalutation(logger log.Logger) {
|
||||
salutations := []string{
|
||||
"Have a nice day! 👋",
|
||||
"Thanks for using kind! 😊",
|
||||
"Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/",
|
||||
"Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂",
|
||||
}
|
||||
r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
|
||||
s := salutations[r.Intn(len(salutations))]
|
||||
logger.V(0).Info(s)
|
||||
}
|
||||
|
||||
func fixupOptions(opts *ClusterOptions) error {
|
||||
|
||||
@@ -19,8 +19,11 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// custom CLI loading spinner for kind
|
||||
@@ -60,7 +63,11 @@ type Spinner struct {
|
||||
var _ io.Writer = &Spinner{}
|
||||
|
||||
// NewSpinner initializes and returns a new Spinner that will write to w
|
||||
// NOTE: w should be os.Stderr or similar, and it should be a Terminal
|
||||
func NewSpinner(w io.Writer) *Spinner {
|
||||
if v, ok := w.(*os.File); ok {
|
||||
w = colorable.NewColorable(v)
|
||||
}
|
||||
return &Spinner{
|
||||
stop: make(chan struct{}, 1),
|
||||
stopped: make(chan struct{}),
|
||||
@@ -116,7 +123,7 @@ func (s *Spinner) Start() {
|
||||
func() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
fmt.Fprintf(s.writer, "\r%s%s%s", s.prefix, frame, s.suffix)
|
||||
fmt.Fprintf(s.writer, "\x1b[?7l\x1b[2K\r%s%s%s\x1b[?7h", s.prefix, frame, s.suffix)
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -149,7 +156,7 @@ func (s *Spinner) Write(p []byte) (n int, err error) {
|
||||
return s.writer.Write(p)
|
||||
}
|
||||
// otherwise: we will rewrite the line first
|
||||
if _, err := s.writer.Write([]byte("\r")); err != nil {
|
||||
if _, err := s.writer.Write([]byte("\x1b[2K\r")); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return s.writer.Write(p)
|
||||
|
||||
@@ -28,6 +28,9 @@ type Status struct {
|
||||
spinner *Spinner
|
||||
status string
|
||||
logger log.Logger
|
||||
// for controlling coloring etc
|
||||
successFormat string
|
||||
failureFormat string
|
||||
}
|
||||
|
||||
// StatusForLogger returns a new status object for the logger l,
|
||||
@@ -35,13 +38,18 @@ type Status struct {
|
||||
// will be used for the status
|
||||
func StatusForLogger(l log.Logger) *Status {
|
||||
s := &Status{
|
||||
logger: l,
|
||||
logger: l,
|
||||
successFormat: " ✓ %s\n",
|
||||
failureFormat: " ✗ %s\n",
|
||||
}
|
||||
// if we're using the CLI logger, check for if it has a spinner setup
|
||||
// and wire the status to that
|
||||
if v, ok := l.(*Logger); ok {
|
||||
if v2, ok := v.writer.(*Spinner); ok {
|
||||
s.spinner = v2
|
||||
// use colored success / failure messages
|
||||
s.successFormat = " \x1b[32m✓\x1b[0m %s\n"
|
||||
s.failureFormat = " \x1b[31m✗\x1b[0m %s\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
@@ -72,11 +80,10 @@ func (s *Status) End(success bool) {
|
||||
s.spinner.Stop()
|
||||
fmt.Fprint(s.spinner.writer, "\r")
|
||||
}
|
||||
|
||||
if success {
|
||||
s.logger.V(0).Infof(" ✓ %s\n", s.status)
|
||||
s.logger.V(0).Infof(s.successFormat, s.status)
|
||||
} else {
|
||||
s.logger.V(0).Infof(" ✗ %s\n", s.status)
|
||||
s.logger.V(0).Infof(s.failureFormat, s.status)
|
||||
}
|
||||
|
||||
s.status = ""
|
||||
|
||||
@@ -54,7 +54,7 @@ kind create cluster --image kindest/node:latest
|
||||
Multi-node clusters and other advanced features may be configured with a config
|
||||
file, for more usage see [the user guide][user guide] or run `kind [command] --help`
|
||||
|
||||
## Community, discussion, contribution, and support
|
||||
## Community
|
||||
|
||||
Please reach out for bugs, feature requests, and other issues!
|
||||
The maintainers of this project are reachable via:
|
||||
@@ -67,7 +67,10 @@ Current maintainers are [@BenTheElder] and [@munnerz] - feel free to
|
||||
reach out if you have any questions!
|
||||
|
||||
Pull Requests are very welcome!
|
||||
See the [issue tracker] if you're unsure where to start, or feel free to reach out to discuss.
|
||||
If you're planning a new feature, please file an issue to discuss first.
|
||||
|
||||
Check the [issue tracker] for `help wanted` issues if you're unsure where to
|
||||
start, or feel free to reach out to discuss. 🙂
|
||||
|
||||
See also: our own [contributor guide] and the Kubernetes [community page].
|
||||
|
||||
|
||||
Reference in New Issue
Block a user