From 746e7f988e227839df29ab5d0018a4b798b63d2f Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Thu, 16 Nov 2017 10:18:00 +0100 Subject: [PATCH 1/2] Clone documentation utility from //pkg/kubectl/cmd/templates --- hack/.golint_failures | 1 + pkg/util/BUILD | 1 + pkg/util/normalizer/BUILD | 29 ++++++ pkg/util/normalizer/markdown.go | 151 ++++++++++++++++++++++++++++++ pkg/util/normalizer/normalizer.go | 80 ++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 pkg/util/normalizer/BUILD create mode 100644 pkg/util/normalizer/markdown.go create mode 100644 pkg/util/normalizer/normalizer.go diff --git a/hack/.golint_failures b/hack/.golint_failures index 8014e1ea08d..0cdb190b982 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -354,6 +354,7 @@ pkg/util/labels pkg/util/mount pkg/util/netsh/testing pkg/util/node +pkg/util/normalizer pkg/util/oom pkg/util/parsers pkg/util/procfs diff --git a/pkg/util/BUILD b/pkg/util/BUILD index a206466463d..d6187341efb 100644 --- a/pkg/util/BUILD +++ b/pkg/util/BUILD @@ -38,6 +38,7 @@ filegroup( "//pkg/util/net:all-srcs", "//pkg/util/netsh:all-srcs", "//pkg/util/node:all-srcs", + "//pkg/util/normalizer:all-srcs", "//pkg/util/nsenter:all-srcs", "//pkg/util/oom:all-srcs", "//pkg/util/parsers:all-srcs", diff --git a/pkg/util/normalizer/BUILD b/pkg/util/normalizer/BUILD new file mode 100644 index 00000000000..41637f99a9c --- /dev/null +++ b/pkg/util/normalizer/BUILD @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "markdown.go", + "normalizer.go", + ], + importpath = "k8s.io/kubernetes/pkg/util/normalizer", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/MakeNowJust/heredoc:go_default_library", + "//vendor/github.com/russross/blackfriday:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/util/normalizer/markdown.go b/pkg/util/normalizer/markdown.go new file mode 100644 index 00000000000..3f064d07354 --- /dev/null +++ b/pkg/util/normalizer/markdown.go @@ -0,0 +1,151 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is copied from /pkg/kubectl/cmd/templates/markdown.go +In a future PR we should remove the original copy and use +/pkg/util/normalizer everywhere. +*/ + +package normalizer + +import ( + "bytes" + "fmt" + "strings" + + "github.com/russross/blackfriday" +) + +const linebreak = "\n" + +// ASCIIRenderer implements blackfriday.Renderer +var _ blackfriday.Renderer = &ASCIIRenderer{} + +// ASCIIRenderer is a blackfriday.Renderer intended for rendering markdown +// documents as plain text, well suited for human reading on terminals. +type ASCIIRenderer struct { + Indentation string + + listItemCount uint + listLevel uint +} + +// NormalText gets a text chunk *after* the markdown syntax was already +// processed and does a final cleanup on things we don't expect here, like +// removing linebreaks on things that are not a paragraph break (auto unwrap). +func (r *ASCIIRenderer) NormalText(out *bytes.Buffer, text []byte) { + raw := string(text) + lines := strings.Split(raw, linebreak) + for _, line := range lines { + trimmed := strings.Trim(line, " \n\t") + out.WriteString(trimmed) + out.WriteString(" ") + } +} + +// List renders the start and end of a list. +func (r *ASCIIRenderer) List(out *bytes.Buffer, text func() bool, flags int) { + r.listLevel++ + out.WriteString(linebreak) + text() + r.listLevel-- +} + +// ListItem renders list items and supports both ordered and unordered lists. +func (r *ASCIIRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) { + if flags&blackfriday.LIST_ITEM_BEGINNING_OF_LIST != 0 { + r.listItemCount = 1 + } else { + r.listItemCount++ + } + indent := strings.Repeat(r.Indentation, int(r.listLevel)) + var bullet string + if flags&blackfriday.LIST_TYPE_ORDERED != 0 { + bullet += fmt.Sprintf("%d.", r.listItemCount) + } else { + bullet += "*" + } + out.WriteString(indent + bullet + " ") + r.fw(out, text) + out.WriteString(linebreak) +} + +// Paragraph renders the start and end of a paragraph. +func (r *ASCIIRenderer) Paragraph(out *bytes.Buffer, text func() bool) { + out.WriteString(linebreak) + text() + out.WriteString(linebreak) +} + +// BlockCode renders a chunk of text that represents source code. +func (r *ASCIIRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) { + out.WriteString(linebreak) + lines := []string{} + for _, line := range strings.Split(string(text), linebreak) { + indented := r.Indentation + line + lines = append(lines, indented) + } + out.WriteString(strings.Join(lines, linebreak)) +} + +func (r *ASCIIRenderer) GetFlags() int { return 0 } +func (r *ASCIIRenderer) HRule(out *bytes.Buffer) { + out.WriteString(linebreak + "----------" + linebreak) +} +func (r *ASCIIRenderer) LineBreak(out *bytes.Buffer) { out.WriteString(linebreak) } +func (r *ASCIIRenderer) TitleBlock(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) { text() } +func (r *ASCIIRenderer) BlockHtml(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) BlockQuote(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) TableRow(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) } +func (r *ASCIIRenderer) TableCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) } +func (r *ASCIIRenderer) Footnotes(out *bytes.Buffer, text func() bool) { text() } +func (r *ASCIIRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) { r.fw(out, text) } +func (r *ASCIIRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) { r.fw(out, link) } +func (r *ASCIIRenderer) CodeSpan(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) Emphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) RawHtmlTag(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) StrikeThrough(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) { r.fw(out, ref) } +func (r *ASCIIRenderer) Entity(out *bytes.Buffer, entity []byte) { r.fw(out, entity) } +func (r *ASCIIRenderer) Smartypants(out *bytes.Buffer, text []byte) { r.fw(out, text) } +func (r *ASCIIRenderer) DocumentHeader(out *bytes.Buffer) {} +func (r *ASCIIRenderer) DocumentFooter(out *bytes.Buffer) {} +func (r *ASCIIRenderer) TocHeaderWithAnchor(text []byte, level int, anchor string) {} +func (r *ASCIIRenderer) TocHeader(text []byte, level int) {} +func (r *ASCIIRenderer) TocFinalize() {} + +func (r *ASCIIRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) { + r.fw(out, header, body) +} + +func (r *ASCIIRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { + r.fw(out, link) +} + +func (r *ASCIIRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { + r.fw(out, link) +} + +func (r *ASCIIRenderer) fw(out *bytes.Buffer, text ...[]byte) { + for _, t := range text { + out.Write(t) + } +} diff --git a/pkg/util/normalizer/normalizer.go b/pkg/util/normalizer/normalizer.go new file mode 100644 index 00000000000..28179f70b17 --- /dev/null +++ b/pkg/util/normalizer/normalizer.go @@ -0,0 +1,80 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is copied from /pkg/kubectl/cmd/templates/normalizer.go +In a future PR we should remove the original copy and use +/pkg/util/normalizer everywhere. +*/ + +package normalizer + +import ( + "strings" + + "github.com/MakeNowJust/heredoc" + "github.com/russross/blackfriday" +) + +const indentation = ` ` + +// LongDesc normalizes a command's long description to follow the conventions. +func LongDesc(s string) string { + if len(s) == 0 { + return s + } + return normalizer{s}.Heredoc().Markdown().Trim().string +} + +// Examples normalizes a command's examples to follow the conventions. +func Examples(s string) string { + if len(s) == 0 { + return s + } + return normalizer{s}.Trim().Indent().string +} + +type normalizer struct { + string +} + +func (s normalizer) Markdown() normalizer { + bytes := []byte(s.string) + formatted := blackfriday.Markdown(bytes, &ASCIIRenderer{Indentation: indentation}, 0) + s.string = string(formatted) + return s +} + +func (s normalizer) Heredoc() normalizer { + s.string = heredoc.Doc(s.string) + return s +} + +func (s normalizer) Trim() normalizer { + s.string = strings.TrimSpace(s.string) + return s +} + +func (s normalizer) Indent() normalizer { + indentedLines := []string{} + for _, line := range strings.Split(s.string, "\n") { + trimmed := strings.TrimSpace(line) + indented := indentation + trimmed + indentedLines = append(indentedLines, indented) + } + s.string = strings.Join(indentedLines, "\n") + return s +} From 01b928cd6cdddff7c80bc079b1e4c67fc59072c2 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Thu, 16 Nov 2017 10:20:22 +0100 Subject: [PATCH 2/2] kubeadm-doc-preflight --- cmd/kubeadm/app/cmd/cmd.go | 2 +- cmd/kubeadm/app/cmd/init.go | 2 +- cmd/kubeadm/app/cmd/join.go | 2 +- cmd/kubeadm/app/cmd/phases/BUILD | 1 + cmd/kubeadm/app/cmd/phases/preflight.go | 46 ++++++++++++++++++----- cmd/kubeadm/app/cmd/util/BUILD | 10 ++++- cmd/kubeadm/app/cmd/util/cmdutil.go | 2 +- cmd/kubeadm/app/cmd/util/cmdutil_test.go | 2 +- cmd/kubeadm/app/cmd/util/documentation.go | 33 ++++++++++++++++ cmd/kubeadm/app/kubeadm.go | 1 + 10 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 cmd/kubeadm/app/cmd/util/documentation.go diff --git a/cmd/kubeadm/app/cmd/cmd.go b/cmd/kubeadm/app/cmd/cmd.go index 202aff5be75..a3124b31be2 100644 --- a/cmd/kubeadm/app/cmd/cmd.go +++ b/cmd/kubeadm/app/cmd/cmd.go @@ -40,7 +40,7 @@ func NewKubeadmCommand(_ io.Reader, out, err io.Writer) *cobra.Command { kubeadm: easily bootstrap a secure Kubernetes cluster. ┌──────────────────────────────────────────────────────────┐ - │ KUBEADM IS BETA, DO NOT USE IT FOR PRODUCTION CLUSTERS! │ + │ KUBEADM IS CURRENTLY IN BETA │ │ │ │ But please, try it out and give us feedback at: │ │ https://github.com/kubernetes/kubeadm/issues │ diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index afac54e35cd..7b58ece280a 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -218,7 +218,7 @@ func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, sk // NewInit validates given arguments and instantiates Init struct with provided information. func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight, skipTokenPrint, dryRun bool, criSocket string) (*Init, error) { - fmt.Println("[kubeadm] WARNING: kubeadm is in beta. Please do not use it for production clusters!") + fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta") if cfgPath != "" { b, err := ioutil.ReadFile(cfgPath) diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index d1dc73c6a5f..0d84aea2382 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -176,7 +176,7 @@ type Join struct { // NewJoin instantiates Join struct with given arguments func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, skipPreFlight bool, criSocket string) (*Join, error) { - fmt.Println("[kubeadm] WARNING: kubeadm is in beta. Please do not use it for production clusters!") + fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta") if cfg.NodeName == "" { cfg.NodeName = nodeutil.GetHostname("") diff --git a/cmd/kubeadm/app/cmd/phases/BUILD b/cmd/kubeadm/app/cmd/phases/BUILD index b68d2611daa..4d2d158ede2 100644 --- a/cmd/kubeadm/app/cmd/phases/BUILD +++ b/cmd/kubeadm/app/cmd/phases/BUILD @@ -47,6 +47,7 @@ go_library( "//cmd/kubeadm/app/util/config:go_default_library", "//cmd/kubeadm/app/util/kubeconfig:go_default_library", "//pkg/api/legacyscheme:go_default_library", + "//pkg/util/normalizer:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", diff --git a/cmd/kubeadm/app/cmd/phases/preflight.go b/cmd/kubeadm/app/cmd/phases/preflight.go index d9d39041e8c..9e91da3ae0a 100644 --- a/cmd/kubeadm/app/cmd/phases/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/preflight.go @@ -22,15 +22,37 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + "k8s.io/kubernetes/pkg/util/normalizer" utilsexec "k8s.io/utils/exec" ) +var ( + masterPreflightLongDesc = normalizer.LongDesc(` + Run master pre-flight checks, functionally equivalent to what implemented by kubeadm init. + ` + cmdutil.AlphaDisclaimer) + + masterPreflightExample = normalizer.Examples(` + # Run master pre-flight checks. + kubeadm alpha phase preflight master + `) + + nodePreflightLongDesc = normalizer.LongDesc(` + Run node pre-flight checks, functionally equivalent to what implemented by kubeadm join. + ` + cmdutil.AlphaDisclaimer) + + nodePreflightExample = normalizer.Examples(` + # Run node pre-flight checks. + kubeadm alpha phase preflight node + `) +) + // NewCmdPreFlight calls cobra.Command for preflight checks func NewCmdPreFlight() *cobra.Command { cmd := &cobra.Command{ Use: "preflight", Short: "Run pre-flight checks", - RunE: cmdutil.SubCmdRunE("preflight"), + Long: cmdutil.MacroCommandLongDescription, } cmd.AddCommand(NewCmdPreFlightMaster()) @@ -41,12 +63,15 @@ func NewCmdPreFlight() *cobra.Command { // NewCmdPreFlightMaster calls cobra.Command for master preflight checks func NewCmdPreFlightMaster() *cobra.Command { cmd := &cobra.Command{ - Use: "master", - Short: "Run master pre-flight checks.", - RunE: func(cmd *cobra.Command, args []string) error { + Use: "master", + Short: "Run master pre-flight checks", + Long: masterPreflightLongDesc, + Example: masterPreflightExample, + Run: func(cmd *cobra.Command, args []string) { cfg := &kubeadmapi.MasterConfiguration{} criSocket := "" - return preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket) + err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket) + kubeadmutil.CheckErr(err) }, } @@ -56,12 +81,15 @@ func NewCmdPreFlightMaster() *cobra.Command { // NewCmdPreFlightNode calls cobra.Command for node preflight checks func NewCmdPreFlightNode() *cobra.Command { cmd := &cobra.Command{ - Use: "node", - Short: "Run node pre-flight checks.", - RunE: func(cmd *cobra.Command, args []string) error { + Use: "node", + Short: "Run node pre-flight checks", + Long: nodePreflightLongDesc, + Example: nodePreflightExample, + Run: func(cmd *cobra.Command, args []string) { cfg := &kubeadmapi.NodeConfiguration{} criSocket := "" - return preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket) + err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket) + kubeadmutil.CheckErr(err) }, } diff --git a/cmd/kubeadm/app/cmd/util/BUILD b/cmd/kubeadm/app/cmd/util/BUILD index 8478951086f..f7a9b69cf5c 100644 --- a/cmd/kubeadm/app/cmd/util/BUILD +++ b/cmd/kubeadm/app/cmd/util/BUILD @@ -2,10 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["cmdutil.go"], + srcs = [ + "cmdutil.go", + "documentation.go", + ], importpath = "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util", visibility = ["//visibility:public"], - deps = ["//vendor/github.com/spf13/cobra:go_default_library"], + deps = [ + "//pkg/util/normalizer:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", + ], ) go_test( diff --git a/cmd/kubeadm/app/cmd/util/cmdutil.go b/cmd/kubeadm/app/cmd/util/cmdutil.go index 21386775103..87dcdff67f0 100644 --- a/cmd/kubeadm/app/cmd/util/cmdutil.go +++ b/cmd/kubeadm/app/cmd/util/cmdutil.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package phases +package util import ( "fmt" diff --git a/cmd/kubeadm/app/cmd/util/cmdutil_test.go b/cmd/kubeadm/app/cmd/util/cmdutil_test.go index ef4d81ce009..40fcf87dfbd 100644 --- a/cmd/kubeadm/app/cmd/util/cmdutil_test.go +++ b/cmd/kubeadm/app/cmd/util/cmdutil_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package phases +package util import ( "testing" diff --git a/cmd/kubeadm/app/cmd/util/documentation.go b/cmd/kubeadm/app/cmd/util/documentation.go new file mode 100644 index 00000000000..8befcc75576 --- /dev/null +++ b/cmd/kubeadm/app/cmd/util/documentation.go @@ -0,0 +1,33 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "k8s.io/kubernetes/pkg/util/normalizer" +) + +var ( + // AlphaDisclaimer to be places at the end of description of commands in alpha release + AlphaDisclaimer = ` + Alpha Disclaimer: this command is currently alpha but, please try it out and give us feedback! + ` + + // MacroCommandLongDescription provide a standard description for "macro" commands + MacroCommandLongDescription = normalizer.LongDesc(` + This command is not meant to be run on its own. See list of available subcommands. + `) +) diff --git a/cmd/kubeadm/app/kubeadm.go b/cmd/kubeadm/app/kubeadm.go index 68ea1973b36..ccf69ee8d92 100644 --- a/cmd/kubeadm/app/kubeadm.go +++ b/cmd/kubeadm/app/kubeadm.go @@ -28,6 +28,7 @@ import ( // Run creates and executes new kubeadm command func Run() error { // We do not want these flags to show up in --help + pflag.CommandLine.MarkHidden("version") pflag.CommandLine.MarkHidden("google-json-key") pflag.CommandLine.MarkHidden("log-flush-frequency")