Use apiextension v1

- upgrade from apiextension v1beta1 to v1
 - generate yaml manifest for crd intead of applying it at runtime
  - users will have to apply the manifest with kubectl
 - kg and kgctl log an error if the crd is not present
 - now validation should actually work

Signed-off-by: leonnicolas <leonloechner@gmx.de>
This commit is contained in:
leonnicolas
2021-06-14 09:08:46 +02:00
parent e272d725a5
commit 36643b77b4
584 changed files with 50911 additions and 55838 deletions

View File

@@ -0,0 +1,30 @@
/*
Copyright 2019 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 pretty contains utilities for formatting terminal help output,
// and a use of those to display marker help.
//
// Terminal Output
//
// The Span interface and Table struct allow you to construct tables with
// colored formatting, without causing ANSI formatting characters to mess up width
// calculations.
//
// Marker Help
//
// The MarkersSummary prints a summary table for marker help, while the MarkersDetails
// prints out more detailed information, with explainations of the individual marker fields.
package pretty

View File

@@ -0,0 +1,171 @@
package pretty
import (
"fmt"
"io"
"sigs.k8s.io/controller-tools/pkg/genall/help"
"github.com/fatih/color"
)
var (
headingStyle = Decoration(*color.New(color.Bold, color.Underline))
markerNameStyle = Decoration(*color.New(color.Bold))
fieldSummaryStyle = Decoration(*color.New(color.FgGreen, color.Italic))
markerTargetStyle = Decoration(*color.New(color.Faint))
fieldDetailStyle = Decoration(*color.New(color.Italic, color.FgGreen))
deprecatedStyle = Decoration(*color.New(color.CrossedOut))
)
// MarkersSummary returns a condensed summary of help for the given markers.
func MarkersSummary(groupName string, markers []help.MarkerDoc) Span {
out := new(SpanWriter)
out.Print(Text("\n"))
out.Print(headingStyle.Containing(Text(groupName)))
out.Print(Text("\n\n"))
table := &Table{Sizing: &TableCalculator{Padding: 2}}
for _, marker := range markers {
table.StartRow()
table.Column(MarkerSyntaxHelp(marker))
table.Column(markerTargetStyle.Containing(Text(marker.Target)))
summary := new(SpanWriter)
if marker.DeprecatedInFavorOf != nil && len(*marker.DeprecatedInFavorOf) > 0 {
summary.Print(markerNameStyle.Containing(Text("(use ")))
summary.Print(markerNameStyle.Containing(Text(*marker.DeprecatedInFavorOf)))
summary.Print(markerNameStyle.Containing(Text(") ")))
}
summary.Print(Text(marker.Summary))
table.Column(summary)
table.EndRow()
}
out.Print(table)
out.Print(Text("\n"))
return out
}
// MarkersDetails returns detailed help for the given markers, including detailed field help.
func MarkersDetails(fullDetail bool, groupName string, markers []help.MarkerDoc) Span {
out := new(SpanWriter)
out.Print(Line(headingStyle.Containing(Text(groupName))))
out.Print(Newlines(2))
for _, marker := range markers {
out.Print(Line(markerName(marker)))
out.Print(Text(" "))
out.Print(markerTargetStyle.Containing(Text(marker.Target)))
summary := new(SpanWriter)
if marker.DeprecatedInFavorOf != nil && len(*marker.DeprecatedInFavorOf) > 0 {
summary.Print(markerNameStyle.Containing(Text("(use ")))
summary.Print(markerNameStyle.Containing(Text(*marker.DeprecatedInFavorOf)))
summary.Print(markerNameStyle.Containing(Text(") ")))
}
summary.Print(Text(marker.Summary))
if !marker.AnonymousField() {
out.Print(Indented(1, Line(summary)))
if len(marker.Details) > 0 && fullDetail {
out.Print(Indented(1, Line(Text(marker.Details))))
}
}
if marker.AnonymousField() {
out.Print(Indented(1, Line(fieldDetailStyle.Containing(FieldSyntaxHelp(marker.Fields[0])))))
out.Print(Text(" "))
out.Print(summary)
if len(marker.Details) > 0 && fullDetail {
out.Print(Indented(2, Line(Text(marker.Details))))
}
out.Print(Newlines(1))
} else if !marker.Empty() {
out.Print(Newlines(1))
if fullDetail {
for _, arg := range marker.Fields {
out.Print(Indented(1, Line(fieldDetailStyle.Containing(FieldSyntaxHelp(arg)))))
out.Print(Indented(2, Line(Text(arg.Summary))))
if len(arg.Details) > 0 && fullDetail {
out.Print(Indented(2, Line(Text(arg.Details))))
out.Print(Newlines(1))
}
}
out.Print(Newlines(1))
} else {
table := &Table{Sizing: &TableCalculator{Padding: 2}}
for _, arg := range marker.Fields {
table.StartRow()
table.Column(fieldDetailStyle.Containing(FieldSyntaxHelp(arg)))
table.Column(Text(arg.Summary))
table.EndRow()
}
out.Print(Indented(1, table))
}
} else {
out.Print(Newlines(1))
}
}
return out
}
func FieldSyntaxHelp(arg help.FieldHelp) Span {
return fieldSyntaxHelp(arg, "")
}
// fieldSyntaxHelp prints the syntax help for a particular marker argument.
func fieldSyntaxHelp(arg help.FieldHelp, sep string) Span {
if arg.Optional {
return FromWriter(func(out io.Writer) error {
_, err := fmt.Fprintf(out, "[%s%s=<%s>]", sep, arg.Name, arg.TypeString())
return err
})
}
return FromWriter(func(out io.Writer) error {
_, err := fmt.Fprintf(out, "%s%s=<%s>", sep, arg.Name, arg.TypeString())
return err
})
}
// markerName returns a span containing just the appropriately-formatted marker name.
func markerName(def help.MarkerDoc) Span {
if def.DeprecatedInFavorOf != nil {
return deprecatedStyle.Containing(Text("+" + def.Name))
}
return markerNameStyle.Containing(Text("+" + def.Name))
}
// MarkerSyntaxHelp assembles syntax help for a given marker.
func MarkerSyntaxHelp(def help.MarkerDoc) Span {
out := new(SpanWriter)
out.Print(markerName(def))
if def.Empty() {
return out
}
sep := ":"
if def.AnonymousField() {
sep = ""
}
fieldStyle := fieldSummaryStyle
if def.DeprecatedInFavorOf != nil {
fieldStyle = deprecatedStyle
}
for _, arg := range def.Fields {
out.Print(fieldStyle.Containing(fieldSyntaxHelp(arg, sep)))
sep = ","
}
return out
}

View File

@@ -0,0 +1,304 @@
package pretty
import (
"bytes"
"fmt"
"io"
"github.com/fatih/color"
)
// NB(directxman12): this isn't particularly elegant, but it's also
// sufficiently simple as to be maintained here. Man (roff) would've
// probably worked, but it's not necessarily on Windows by default.
// Span is a chunk of content that is writable to an output, but knows how to
// calculate its apparent visual "width" on the terminal (not to be confused
// with the raw length, which may include zero-width coloring sequences).
type Span interface {
// VisualLength reports the "width" as perceived by the user on the terminal
// (i.e. widest line, ignoring ANSI escape characters).
VisualLength() int
// WriteTo writes the full span contents to the given writer.
WriteTo(io.Writer) error
}
// Table is a Span that writes its data in table form, with sizing controlled
// by the given table calculator. Rows are started with StartRow, followed by
// some calls to Column, followed by a call to EndRow. Once all rows are
// added, the table can be used as a Span.
type Table struct {
Sizing *TableCalculator
cellsByRow [][]Span
colSizes []int
}
// StartRow starts a new row.
// It must eventually be followed by EndRow.
func (t *Table) StartRow() {
t.cellsByRow = append(t.cellsByRow, []Span(nil))
}
// EndRow ends the currently started row.
func (t *Table) EndRow() {
lastRow := t.cellsByRow[len(t.cellsByRow)-1]
sizes := make([]int, len(lastRow))
for i, cell := range lastRow {
sizes[i] = cell.VisualLength()
}
t.Sizing.AddRowSizes(sizes...)
}
// Column adds the given span as a new column to the current row.
func (t *Table) Column(contents Span) {
currentRowInd := len(t.cellsByRow) - 1
t.cellsByRow[currentRowInd] = append(t.cellsByRow[currentRowInd], contents)
}
// SkipRow prints a span without having it contribute to the table calculation.
func (t *Table) SkipRow(contents Span) {
t.cellsByRow = append(t.cellsByRow, []Span{contents})
}
func (t *Table) WriteTo(out io.Writer) error {
if t.colSizes == nil {
t.colSizes = t.Sizing.ColumnWidths()
}
for _, cells := range t.cellsByRow {
currentPosition := 0
for colInd, cell := range cells {
colSize := t.colSizes[colInd]
diff := colSize - cell.VisualLength()
if err := cell.WriteTo(out); err != nil {
return err
}
if diff > 0 {
if err := writePadding(out, columnPadding, diff); err != nil {
return err
}
}
currentPosition += colSize
}
if _, err := fmt.Fprint(out, "\n"); err != nil {
return err
}
}
return nil
}
func (t *Table) VisualLength() int {
if t.colSizes == nil {
t.colSizes = t.Sizing.ColumnWidths()
}
res := 0
for _, colSize := range t.colSizes {
res += colSize
}
return res
}
// Text is a span that simply contains raw text. It's a good starting point.
type Text string
func (t Text) VisualLength() int { return len(t) }
func (t Text) WriteTo(w io.Writer) error {
_, err := w.Write([]byte(t))
return err
}
// indented is a span that indents all lines by the given number of tabs.
type indented struct {
Amount int
Content Span
}
func (i *indented) VisualLength() int { return i.Content.VisualLength() }
func (i *indented) WriteTo(w io.Writer) error {
var out bytes.Buffer
if err := i.Content.WriteTo(&out); err != nil {
return err
}
lines := bytes.Split(out.Bytes(), []byte("\n"))
for lineInd, line := range lines {
if lineInd != 0 {
if _, err := w.Write([]byte("\n")); err != nil {
return err
}
}
if len(line) == 0 {
continue
}
if err := writePadding(w, indentPadding, i.Amount); err != nil {
return err
}
if _, err := w.Write(line); err != nil {
return err
}
}
return nil
}
// Indented returns a span that indents all lines by the given number of tabs.
func Indented(amt int, content Span) Span {
return &indented{Amount: amt, Content: content}
}
// fromWriter is a span that takes content from a function expecting a Writer.
type fromWriter struct {
cache []byte
cacheError error
run func(io.Writer) error
}
func (f *fromWriter) VisualLength() int {
if f.cache == nil {
var buf bytes.Buffer
if err := f.run(&buf); err != nil {
f.cacheError = err
}
f.cache = buf.Bytes()
}
return len(f.cache)
}
func (f *fromWriter) WriteTo(w io.Writer) error {
if f.cache != nil {
if f.cacheError != nil {
return f.cacheError
}
_, err := w.Write(f.cache)
return err
}
return f.run(w)
}
// FromWriter returns a span that takes content from a function expecting a Writer.
func FromWriter(run func(io.Writer) error) Span {
return &fromWriter{run: run}
}
// Decoration represents a terminal decoration.
type Decoration color.Color
// Containing returns a Span that has the given decoration applied.
func (d Decoration) Containing(contents Span) Span {
return &decorated{
Contents: contents,
Attributes: color.Color(d),
}
}
// decorated is a span that has some terminal decoration applied.
type decorated struct {
Contents Span
Attributes color.Color
}
func (d *decorated) VisualLength() int { return d.Contents.VisualLength() }
func (d *decorated) WriteTo(w io.Writer) error {
oldOut := color.Output
color.Output = w
defer func() { color.Output = oldOut }()
d.Attributes.Set()
defer color.Unset()
return d.Contents.WriteTo(w)
}
// SpanWriter is a span that contains multiple sub-spans.
type SpanWriter struct {
contents []Span
}
func (m *SpanWriter) VisualLength() int {
res := 0
for _, span := range m.contents {
res += span.VisualLength()
}
return res
}
func (m *SpanWriter) WriteTo(w io.Writer) error {
for _, span := range m.contents {
if err := span.WriteTo(w); err != nil {
return err
}
}
return nil
}
// Print adds a new span to this SpanWriter.
func (m *SpanWriter) Print(s Span) {
m.contents = append(m.contents, s)
}
// lines is a span that adds some newlines, optionally followed by some content.
type lines struct {
content Span
amountBefore int
}
func (l *lines) VisualLength() int {
if l.content == nil {
return 0
}
return l.content.VisualLength()
}
func (l *lines) WriteTo(w io.Writer) error {
if err := writePadding(w, linesPadding, l.amountBefore); err != nil {
return err
}
if l.content != nil {
if err := l.content.WriteTo(w); err != nil {
return err
}
}
return nil
}
// Newlines returns a span just containing some newlines.
func Newlines(amt int) Span {
return &lines{amountBefore: amt}
}
// Line returns a span that emits a newline, followed by the given content.
func Line(content Span) Span {
return &lines{amountBefore: 1, content: content}
}
var (
columnPadding = []byte(" ")
indentPadding = []byte("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")
linesPadding = []byte("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
)
// writePadding writes out padding of the given type in the given amount to the writer.
// Each byte in the padding buffer contributes 1 to the amount -- the padding being
// a buffer is just for efficiency.
func writePadding(out io.Writer, typ []byte, amt int) error {
if amt <= len(typ) {
_, err := out.Write(typ[:amt])
return err
}
num := amt / len(typ)
rem := amt % len(typ)
for i := 0; i < num; i++ {
if _, err := out.Write(typ); err != nil {
return err
}
}
if _, err := out.Write(typ[:rem]); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,64 @@
/*
Copyright 2019 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 pretty
// TableCalculator calculates column widths (with optional padding)
// for a table based on the maximum required column width.
type TableCalculator struct {
cellSizesByCol [][]int
Padding int
MaxWidth int
}
// AddRowSizes registers a new row with cells of the given sizes.
func (c *TableCalculator) AddRowSizes(cellSizes ...int) {
if len(cellSizes) > len(c.cellSizesByCol) {
for range cellSizes[len(c.cellSizesByCol):] {
c.cellSizesByCol = append(c.cellSizesByCol, []int(nil))
}
}
for i, size := range cellSizes {
c.cellSizesByCol[i] = append(c.cellSizesByCol[i], size)
}
}
// ColumnWidths calculates the appropriate column sizes given the
// previously registered rows.
func (c *TableCalculator) ColumnWidths() []int {
maxColWidths := make([]int, len(c.cellSizesByCol))
for colInd, cellSizes := range c.cellSizesByCol {
max := 0
for _, cellSize := range cellSizes {
if max < cellSize {
max = cellSize
}
}
maxColWidths[colInd] = max
}
actualMaxWidth := c.MaxWidth - c.Padding
for i, width := range maxColWidths {
if actualMaxWidth > 0 && width > actualMaxWidth {
maxColWidths[i] = actualMaxWidth
}
maxColWidths[i] += c.Padding
}
return maxColWidths
}