Upgrade k8s package verison (#5358)

* upgrade k8s package version

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

* Script upgrade and code formatting.

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
hongzhouzi
2022-11-15 14:56:38 +08:00
committed by GitHub
parent 5f91c1663a
commit 44167aa47a
3106 changed files with 321340 additions and 172080 deletions

View File

@@ -61,7 +61,7 @@ type Factory interface {
UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a schema that can validate objects stored on disk.
Validator(validate bool) (validation.Schema, error)
Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error)
// OpenAPISchema returns the parsed openapi schema definition
OpenAPISchema() (openapi.Resources, error)
// OpenAPIGetter returns a getter for the openapi schema document

View File

@@ -24,6 +24,7 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
@@ -141,8 +142,14 @@ func (f *factoryImpl) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r
return restclient.RESTClientFor(cfg)
}
func (f *factoryImpl) Validator(validate bool) (validation.Schema, error) {
if !validate {
func (f *factoryImpl) Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error) {
// client-side schema validation is only performed
// when the validationDirective is strict.
// If the directive is warn, we rely on the ParamVerifyingSchema
// to ignore the client-side validation and provide a warning
// to the user that attempting warn validation when SS validation
// is unsupported is inert.
if validationDirective == metav1.FieldValidationIgnore {
return validation.NullSchema{}, nil
}
@@ -151,10 +158,11 @@ func (f *factoryImpl) Validator(validate bool) (validation.Schema, error) {
return nil, err
}
return validation.ConjunctiveSchema{
schema := validation.ConjunctiveSchema{
openapivalidation.NewSchemaValidation(resources),
validation.NoDoubleKeySchema{},
}, nil
}
return validation.NewParamVerifyingSchema(schema, verifier, string(validationDirective)), nil
}
// OpenAPISchema returns metadata and structural information about

View File

@@ -30,12 +30,14 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
@@ -50,6 +52,7 @@ import (
const (
ApplyAnnotationsFlag = "save-config"
DefaultErrorExitCode = 1
DefaultChunkSize = 500
)
type debugError interface {
@@ -86,10 +89,13 @@ func DefaultBehaviorOnFatal() {
fatalErrHandler = fatal
}
// fatal prints the message (if provided) and then exits. If V(6) or greater,
// klog.Fatal is invoked for extended information.
// fatal prints the message (if provided) and then exits. If V(99) or greater,
// klog.Fatal is invoked for extended information. This is intended for maintainer
// debugging and out of a reasonable range for users.
func fatal(msg string, code int) {
if klog.V(6).Enabled() {
// nolint:logcheck // Not using the result of klog.V(99) inside the if
// branch is okay, we just use it to determine how to terminate.
if klog.V(99).Enabled() {
klog.FatalDepth(2, msg)
}
if len(msg) > 0 {
@@ -127,6 +133,20 @@ func CheckDiffErr(err error) {
})
}
// isInvalidReasonStatusError returns true if this is an API Status error with reason=Invalid.
// This is distinct from generic 422 errors we want to fall back to generic error handling.
func isInvalidReasonStatusError(err error) bool {
if !apierrors.IsInvalid(err) {
return false
}
statusError, isStatusError := err.(*apierrors.StatusError)
if !isStatusError {
return false
}
status := statusError.Status()
return status.Reason == metav1.StatusReasonInvalid
}
// checkErr formats a given error as a string and calls the passed handleErr
// func with that string and an kubectl exit code.
func checkErr(err error, handleErr func(string, int)) {
@@ -142,16 +162,26 @@ func checkErr(err error, handleErr func(string, int)) {
switch {
case err == ErrExit:
handleErr("", DefaultErrorExitCode)
case apierrors.IsInvalid(err):
details := err.(*apierrors.StatusError).Status().Details
case isInvalidReasonStatusError(err):
status := err.(*apierrors.StatusError).Status()
details := status.Details
s := "The request is invalid"
if details == nil {
// if we have no other details, include the message from the server if present
if len(status.Message) > 0 {
s += ": " + status.Message
}
handleErr(s, DefaultErrorExitCode)
return
}
if len(details.Kind) != 0 || len(details.Name) != 0 {
s = fmt.Sprintf("The %s %q is invalid", details.Kind, details.Name)
} else if len(status.Message) > 0 && len(details.Causes) == 0 {
// only append the message if we have no kind/name details and no causes,
// since default invalid error constructors duplicate that information in the message
s += ": " + status.Message
}
if len(details.Causes) > 0 {
errs := statusCausesToAggrError(details.Causes)
handleErr(MultilineError(s+": ", errs), DefaultErrorExitCode)
@@ -393,11 +423,16 @@ func GetPodRunningTimeoutFlag(cmd *cobra.Command) (time.Duration, error) {
}
func AddValidateFlags(cmd *cobra.Command) {
cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it")
}
cmd.Flags().String(
"validate",
"strict",
`Must be one of: strict (or true), warn, ignore (or false).
"true" or "strict" will use a schema to validate the input and fail the request if invalid. It will perform server side validation if ServerSideFieldValidation is enabled on the api-server, but will fall back to less reliable client-side validation if not.
"warn" will warn about unknown or duplicate fields without blocking the request if server-side field validation is enabled on the API server, and behave as "ignore" otherwise.
"false" or "ignore" will not perform any schema validation, silently dropping any unknown or duplicate fields.`,
)
func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) {
cmd.Flags().BoolVar(&options.EnableValidation, "validate", options.EnableValidation, "If true, use a schema to validate the input before sending it")
cmd.Flags().Lookup("validate").NoOptDefVal = "strict"
}
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
@@ -455,19 +490,28 @@ func AddApplyAnnotationVarFlags(cmd *cobra.Command, applyAnnotation *bool) {
cmd.Flags().BoolVar(applyAnnotation, ApplyAnnotationsFlag, *applyAnnotation, "If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.")
}
// AddGeneratorFlags adds flags common to resource generation commands
// TODO: need to take a pass at other generator commands to use this set of flags
func AddGeneratorFlags(cmd *cobra.Command, defaultGenerator string) {
cmd.Flags().String("generator", defaultGenerator, "The name of the API generator to use.")
cmd.Flags().MarkDeprecated("generator", "has no effect and will be removed in the future.")
AddDryRunFlag(cmd)
func AddChunkSizeFlag(cmd *cobra.Command, value *int64) {
cmd.Flags().Int64Var(value, "chunk-size", *value,
"Return large lists in chunks rather than all at once. Pass 0 to disable. This flag is beta and may change in the future.")
}
func AddLabelSelectorFlagVar(cmd *cobra.Command, p *string) {
cmd.Flags().StringVarP(p, "selector", "l", *p, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.")
}
func AddSubresourceFlags(cmd *cobra.Command, subresource *string, usage string, allowedSubresources ...string) {
cmd.Flags().StringVar(subresource, "subresource", "", fmt.Sprintf("%s Must be one of %v. This flag is alpha and may change in the future.", usage, allowedSubresources))
CheckErr(cmd.RegisterFlagCompletionFunc("subresource", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return allowedSubresources, cobra.ShellCompDirectiveNoFileComp
}))
}
type ValidateOptions struct {
EnableValidation bool
ValidationDirective string
}
// Merge requires JSON serialization
// Merge converts the passed in object to JSON, merges the fragment into it using an RFC7396 JSON Merge Patch,
// and returns the resulting object
// TODO: merge assumes JSON serialization, and does not properly abstract API retrieval
func Merge(codec runtime.Codec, dst runtime.Object, fragment string) (runtime.Object, error) {
// encode dst into versioned json and apply fragment directly too it
@@ -486,6 +530,46 @@ func Merge(codec runtime.Codec, dst runtime.Object, fragment string) (runtime.Ob
return out, nil
}
// StrategicMerge converts the passed in object to JSON, merges the fragment into it using a Strategic Merge Patch,
// and returns the resulting object
func StrategicMerge(codec runtime.Codec, dst runtime.Object, fragment string, dataStruct runtime.Object) (runtime.Object, error) {
target, err := runtime.Encode(codec, dst)
if err != nil {
return nil, err
}
patched, err := strategicpatch.StrategicMergePatch(target, []byte(fragment), dataStruct)
if err != nil {
return nil, err
}
out, err := runtime.Decode(codec, patched)
if err != nil {
return nil, err
}
return out, nil
}
// JSONPatch converts the passed in object to JSON, performs an RFC6902 JSON Patch using operations specified in the
// fragment, and returns the resulting object
func JSONPatch(codec runtime.Codec, dst runtime.Object, fragment string) (runtime.Object, error) {
target, err := runtime.Encode(codec, dst)
if err != nil {
return nil, err
}
patch, err := jsonpatch.DecodePatch([]byte(fragment))
if err != nil {
return nil, err
}
patched, err := patch.Apply(target)
if err != nil {
return nil, err
}
out, err := runtime.Decode(codec, patched)
if err != nil {
return nil, err
}
return out, nil
}
// DumpReaderToFile writes all data from the given io.Reader to the specified file
// (usually for temporary use).
func DumpReaderToFile(reader io.Reader, filename string) error {
@@ -524,6 +608,28 @@ func GetFieldManagerFlag(cmd *cobra.Command) string {
return GetFlagString(cmd, "field-manager")
}
func GetValidationDirective(cmd *cobra.Command) (string, error) {
var validateFlag = GetFlagString(cmd, "validate")
b, err := strconv.ParseBool(validateFlag)
if err != nil {
switch validateFlag {
case "strict":
return metav1.FieldValidationStrict, nil
case "warn":
return metav1.FieldValidationWarn, nil
case "ignore":
return metav1.FieldValidationIgnore, nil
default:
return metav1.FieldValidationStrict, fmt.Errorf(`invalid - validate option %q; must be one of: strict (or true), warn, ignore (or false)`, validateFlag)
}
}
// The flag was a boolean
if b {
return metav1.FieldValidationStrict, nil
}
return metav1.FieldValidationIgnore, nil
}
type DryRunStrategy int
const (
@@ -666,7 +772,8 @@ func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
// arguments (sub-commands) are provided, or a usage error otherwise.
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
return func(c *cobra.Command, args []string) {
c.SetOutput(out)
c.SetOut(out)
c.SetErr(out)
RequireNoArguments(c, args)
c.Help()
CheckErr(ErrExit)
@@ -697,7 +804,9 @@ func ManualStrip(file []byte) []byte {
stripped := []byte{}
lines := bytes.Split(file, []byte("\n"))
for i, line := range lines {
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("#")) {
trimline := bytes.TrimSpace(line)
if bytes.HasPrefix(trimline, []byte("#")) && !bytes.HasPrefix(trimline, []byte("#!")) {
continue
}
stripped = append(stripped, line...)
@@ -748,3 +857,18 @@ func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
oldGeneratorName,
)
}
// Difference removes any elements of subArray from fullArray and returns the result
func Difference(fullArray []string, subArray []string) []string {
exclude := make(map[string]bool, len(subArray))
for _, elem := range subArray {
exclude[elem] = true
}
var result []string
for _, elem := range fullArray {
if _, found := exclude[elem]; !found {
result = append(result, elem)
}
}
return result
}

90
vendor/k8s.io/kubectl/pkg/cmd/util/override_options.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/*
Copyright 2021 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 (
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/i18n"
)
type OverrideType string
const (
// OverrideTypeJSON will use an RFC6902 JSON Patch to alter the generated output
OverrideTypeJSON OverrideType = "json"
// OverrideTypeMerge will use an RFC7396 JSON Merge Patch to alter the generated output
OverrideTypeMerge OverrideType = "merge"
// OverrideTypeStrategic will use a Strategic Merge Patch to alter the generated output
OverrideTypeStrategic OverrideType = "strategic"
)
const DefaultOverrideType = OverrideTypeMerge
type OverrideOptions struct {
Overrides string
OverrideType OverrideType
}
func (o *OverrideOptions) AddOverrideFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.Overrides, "overrides", "", i18n.T("An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field."))
cmd.Flags().StringVar((*string)(&o.OverrideType), "override-type", string(DefaultOverrideType), fmt.Sprintf("The method used to override the generated object: %s, %s, or %s.", OverrideTypeJSON, OverrideTypeMerge, OverrideTypeStrategic))
}
func (o *OverrideOptions) NewOverrider(dataStruct runtime.Object) *Overrider {
return &Overrider{
Options: o,
DataStruct: dataStruct,
}
}
type Overrider struct {
Options *OverrideOptions
DataStruct runtime.Object
}
func (o *Overrider) Apply(obj runtime.Object) (runtime.Object, error) {
if len(o.Options.Overrides) == 0 {
return obj, nil
}
codec := runtime.NewCodec(scheme.DefaultJSONEncoder(), scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...))
var overrideType OverrideType
if len(o.Options.OverrideType) == 0 {
overrideType = DefaultOverrideType
} else {
overrideType = o.Options.OverrideType
}
switch overrideType {
case OverrideTypeJSON:
return JSONPatch(codec, obj, o.Options.Overrides)
case OverrideTypeMerge:
return Merge(codec, obj, o.Options.Overrides)
case OverrideTypeStrategic:
return StrategicMerge(codec, obj, o.Options.Overrides, o.DataStruct)
default:
return nil, fmt.Errorf("invalid override type: %v", overrideType)
}
}

150
vendor/k8s.io/kubectl/pkg/util/i18n/i18n.go generated vendored Normal file
View File

@@ -0,0 +1,150 @@
/*
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.
*/
package i18n
import (
"archive/zip"
"bytes"
"embed"
"errors"
"fmt"
"os"
"strings"
gettext "github.com/chai2010/gettext-go"
"k8s.io/klog/v2"
)
//go:embed translations
var translations embed.FS
var knownTranslations = map[string][]string{
"kubectl": {
"default",
"en_US",
"fr_FR",
"zh_CN",
"ja_JP",
"zh_TW",
"it_IT",
"de_DE",
"ko_KR",
"pt_BR",
},
// only used for unit tests.
"test": {
"default",
"en_US",
},
}
func loadSystemLanguage() string {
// Implements the following locale priority order: LC_ALL, LC_MESSAGES, LANG
// Similarly to: https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
langStr := os.Getenv("LC_ALL")
if langStr == "" {
langStr = os.Getenv("LC_MESSAGES")
}
if langStr == "" {
langStr = os.Getenv("LANG")
}
if langStr == "" {
klog.V(3).Infof("Couldn't find the LC_ALL, LC_MESSAGES or LANG environment variables, defaulting to en_US")
return "default"
}
pieces := strings.Split(langStr, ".")
if len(pieces) != 2 {
klog.V(3).Infof("Unexpected system language (%s), defaulting to en_US", langStr)
return "default"
}
return pieces[0]
}
func findLanguage(root string, getLanguageFn func() string) string {
langStr := getLanguageFn()
translations := knownTranslations[root]
for ix := range translations {
if translations[ix] == langStr {
return langStr
}
}
klog.V(3).Infof("Couldn't find translations for %s, using default", langStr)
return "default"
}
// LoadTranslations loads translation files. getLanguageFn should return a language
// string (e.g. 'en-US'). If getLanguageFn is nil, then the loadSystemLanguage function
// is used, which uses the 'LANG' environment variable.
func LoadTranslations(root string, getLanguageFn func() string) error {
if getLanguageFn == nil {
getLanguageFn = loadSystemLanguage
}
langStr := findLanguage(root, getLanguageFn)
translationFiles := []string{
fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.po", root, langStr),
fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.mo", root, langStr),
}
klog.V(3).Infof("Setting language to %s", langStr)
// TODO: list the directory and load all files.
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
// Make sure to check the error on Close.
for _, file := range translationFiles {
filename := "translations/" + file
f, err := w.Create(file)
if err != nil {
return err
}
data, err := translations.ReadFile(filename)
if err != nil {
return err
}
if _, err := f.Write(data); err != nil {
return nil
}
}
if err := w.Close(); err != nil {
return err
}
gettext.BindLocale(gettext.New("k8s", root+".zip", buf.Bytes()))
gettext.SetDomain("k8s")
gettext.SetLanguage(langStr)
return nil
}
// T translates a string, possibly substituting arguments into it along
// the way. If len(args) is > 0, args1 is assumed to be the plural value
// and plural translation is used.
func T(defaultValue string, args ...int) string {
if len(args) == 0 {
return gettext.PGettext("", defaultValue)
}
return fmt.Sprintf(gettext.PNGettext("", defaultValue, defaultValue+".plural", args[0]),
args[0])
}
// Errorf produces an error with a translated error string.
// Substitution is performed via the `T` function above, following
// the same rules.
func Errorf(defaultValue string, args ...int) error {
return errors.New(T(defaultValue, args...))
}

View File

@@ -0,0 +1,7 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers: []
approvers:
- sig-cli-maintainers
emeritus_approvers:
- brendandburns

View File

@@ -0,0 +1,82 @@
# Translations README
This is a basic sketch of the workflow needed to add translations:
# Adding/Updating Translations
## New languages
Create `staging/src/k8s.io/kubectl/pkg/util/i18n/translations/kubectl/<language>/LC_MESSAGES/k8s.po`. There's
no need to update `translations/test/...` which is only used for unit tests.
There is an example [PR here](https://github.com/kubernetes/kubernetes/pull/40645) which adds support for French.
Once you've added a new language, you'll need to register it in
`staging/src/k8s.io/kubectl/pkg/util/i18n/i18n.go` by adding it to the `knownTranslations` map.
## Wrapping strings
There is a simple script in `staging/src/k8s.io/kubectl/pkg/util/i18n/translations/extract.py` that performs
simple regular expression based wrapping of strings. It can always
use improvements to understand additional strings.
## Extracting strings
Once the strings are wrapped, you can extract strings from go files using
the `go-xgettext` command which can be installed with:
```console
go get github.com/gosexy/gettext/go-xgettext
```
Once that's installed you can run `./hack/update-translations.sh`, which
will extract and sort any new strings.
## Adding new translations
Edit the appropriate `k8s.po` file, `poedit` is a popular open source tool
for translations. You can load the `staging/src/k8s.io/kubectl/pkg/util/i18n/translations/kubectl/template.pot` file
to find messages that might be missing.
Once you are done with your `k8s.po` file, generate the corresponding `k8s.mo`
file. `poedit` does this automatically on save, but you can also run
`./hack/update-translations.sh` to perform the `po` to `mo` translation.
We use the English translation as the `msgid`.
## Regenerating the bindata file
> Note: Regeneration of bindata is no more necessary for Kubernetes 1.22+ as
> the translations are now embedded into the binary at compile time.
> See: https://github.com/kubernetes/kubernetes/pull/99829
With the `mo` files up to date, you can now convert the generated files
into code using `go-bindata` command which can be installed with:
```console
go get github.com/go-bindata/go-bindata/...
```
Run `./hack/generate-bindata.sh`, this will turn the translation files
into generated code which will in turn be packaged into the Kubernetes
binaries.
## Extracting strings
There is a script in `staging/src/k8s.io/kubectl/pkg/util/i18n/translations/extract.py` that knows how to do some
simple extraction. It needs a lot of work.
# Using translations
To use translations, you simply need to add:
```go
import pkg/i18n
...
// Get a translated string
translated := i18n.T("Your message in english here")
// Get a translated plural string
translated := i18n.T("You had % items", items)
// Translated error
return i18n.Error("Something bad happened")
// Translated plural error
return i18n.Error("%d bad things happened")
```

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python
# 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.
"""Extract strings from command files and externalize into translation files.
Expects to be run from the root directory of the repository.
Usage:
extract.py pkg/kubectl/cmd/apply.go
"""
import fileinput
import sys
import re
class MatchHandler(object):
""" Simple holder for a regular expression and a function
to run if that regular expression matches a line.
The function should expect (re.match, file, linenumber) as parameters
"""
def __init__(self, regex, replace_fn):
self.regex = re.compile(regex)
self.replace_fn = replace_fn
def short_replace(match, file, line_number):
"""Replace a Short: ... cobra command description with an internationalization
"""
sys.stdout.write('{}i18n.T({}),\n'.format(match.group(1), match.group(2)))
SHORT_MATCH = MatchHandler(r'(\s+Short:\s+)("[^"]+"),', short_replace)
def import_replace(match, file, line_number):
"""Add an extra import for the i18n library.
Doesn't try to be smart and detect if it's already present, assumes a
gofmt round wil fix things.
"""
sys.stdout.write('{}\n"k8s.io/kubectl/pkg/util/i18n"\n'.format(match.group(1)))
IMPORT_MATCH = MatchHandler('(.*"k8s.io/kubectl/pkg/cmd/util")', import_replace)
def string_flag_replace(match, file, line_number):
"""Replace a cmd.Flags().String("...", "", "...") with an internationalization
"""
sys.stdout.write('{}i18n.T("{})"))\n'.format(match.group(1), match.group(2)))
STRING_FLAG_MATCH = MatchHandler('(\s+cmd\.Flags\(\).String\("[^"]*", "[^"]*", )"([^"]*)"\)', string_flag_replace)
def long_string_replace(match, file, line_number):
return '{}i18n.T({}){}'.format(match.group(1), match.group(2), match.group(3))
LONG_DESC_MATCH = MatchHandler('(LongDesc\()(`[^`]+`)([^\n]\n)', long_string_replace)
EXAMPLE_MATCH = MatchHandler('(Examples\()(`[^`]+`)([^\n]\n)', long_string_replace)
def replace(filename, matchers, multiline_matchers):
"""Given a file and a set of matchers, run those matchers
across the file and replace it with the results.
"""
# Run all the matchers
line_number = 0
for line in fileinput.input(filename, inplace=True):
line_number += 1
matched = False
for matcher in matchers:
match = matcher.regex.match(line)
if match:
matcher.replace_fn(match, filename, line_number)
matched = True
break
if not matched:
sys.stdout.write(line)
sys.stdout.flush()
with open(filename, 'r') as datafile:
content = datafile.read()
for matcher in multiline_matchers:
match = matcher.regex.search(content)
while match:
rep = matcher.replace_fn(match, filename, 0)
# Escape back references in the replacement string
# (And escape for Python)
# (And escape for regex)
rep = re.sub('\\\\(\\d)', '\\\\\\\\\\1', rep)
content = matcher.regex.sub(rep, content, 1)
match = matcher.regex.search(content)
sys.stdout.write(content)
# gofmt the file again
from subprocess import call
call(["goimports", "-w", filename])
replace(sys.argv[1], [SHORT_MATCH, IMPORT_MATCH, STRING_FLAG_MATCH], [LONG_DESC_MATCH, EXAMPLE_MATCH])

View File

@@ -0,0 +1,6 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- sig-cli-maintainers
reviewers:
- sig-cli-reviewers

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,103 @@
# Test translations for unit tests.
# Copyright (C) 2016
# This file is distributed under the same license as the Kubernetes package.
# FIRST AUTHOR brendan.d.burns@gmail.com, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: gettext-go-examples-hello\n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2021-07-07 20:15+0200\n"
"PO-Revision-Date: 2017-01-29 22:54-0800\n"
"Last-Translator: Brendan Burns <brendan.d.burns@gmail.com>\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/delete_cluster.go#L38
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go:42
msgid "Delete the specified cluster from the kubeconfig"
msgstr "Supprimer le cluster spécifié du kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/delete_context.go#L38
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go:42
msgid "Delete the specified context from the kubeconfig"
msgstr "Supprimer le contexte spécifié du kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/get_contexts.go#L62
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_contexts.go:72
msgid "Describe one or many contexts"
msgstr "Décrire un ou plusieurs contextes"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/get_clusters.go#L40
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_clusters.go:41
msgid "Display clusters defined in the kubeconfig"
msgstr "Afficher les cluster définis dans kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/view.go#L64
#: staging/src/k8s.io/kubectl/pkg/cmd/config/view.go:81
msgid "Display merged kubeconfig settings or a specified kubeconfig file"
msgstr ""
"Afficher les paramètres fusionnés de kubeconfig ou d'un fichier kubeconfig "
"spécifié"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/config.go#L39
#: staging/src/k8s.io/kubectl/pkg/cmd/config/config.go:42
msgid "Modify kubeconfig files"
msgstr "Modifier des fichiers kubeconfig"
#: staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go:135
msgid "Update the annotations on a resource"
msgstr "Mettre à jour les annotations d'une ressource"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/apply.go#L98
#~ msgid "Apply a configuration to a resource by filename or stdin"
#~ msgstr ""
#~ "Appliquer une configuration à une ressource par nom de fichier ou depuis "
#~ "stdin"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/current_context.go#L48
#~ msgid "Displays the current-context"
#~ msgstr "Affiche le contexte actuel"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_cluster.go#L67
#~ msgid "Sets a cluster entry in kubeconfig"
#~ msgstr "Définit un cluster dans kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_context.go#L57
#~ msgid "Sets a context entry in kubeconfig"
#~ msgstr "Définit un contexte dans kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_authinfo.go#L103
#~ msgid "Sets a user entry in kubeconfig"
#~ msgstr "Définit un utilisateur dans kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/set.go#L59
#~ msgid "Sets an individual value in a kubeconfig file"
#~ msgstr "Définit une valeur individuelle dans un fichier kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/use_context.go#L48
#~ msgid "Sets the current-context in a kubeconfig file"
#~ msgstr "Définit le contexte courant dans un fichier kubeconfig"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/unset.go#L47
#~ msgid "Unsets an individual value in a kubeconfig file"
#~ msgstr "Supprime une valeur individuelle dans un fichier kubeconfig"
#~ msgid ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgid_plural ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgstr[0] ""
#~ "watch n'est compatible qu'avec les ressources individuelles et les "
#~ "collections de ressources. - %d ressource a été trouvée. "
#~ msgstr[1] ""
#~ "watch n'est compatible qu'avec les ressources individuelles et les "
#~ "collections de ressources. - %d ressources ont été trouvées. "

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,96 @@
# Test translations for unit tests.
# Copyright (C) 2017
# This file is distributed under the same license as the Kubernetes package.
# FIRST AUTHOR ianyrchoi@gmail.com, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: gettext-go-examples-hello\n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2021-07-07 20:15+0200\n"
"PO-Revision-Date: 2018-04-03 06:05+0900\n"
"Last-Translator: Ian Y. Choi <ianyrchoi@gmail.com>\n"
"Language-Team: \n"
"Language: ko_KR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.6\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Plural-Forms: nplurals=1; plural=0;\n"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/delete_cluster.go#L38
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go:42
msgid "Delete the specified cluster from the kubeconfig"
msgstr "kubeconfig에서 지정된 클러스터를 삭제합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/delete_context.go#L38
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go:42
msgid "Delete the specified context from the kubeconfig"
msgstr "kubeconfig에서 지정된 컨텍스트를 삭제합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/get_contexts.go#L62
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_contexts.go:72
msgid "Describe one or many contexts"
msgstr "하나 또는 여러 컨텍스트를 설명합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/get_clusters.go#L40
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_clusters.go:41
msgid "Display clusters defined in the kubeconfig"
msgstr "kubeconfig에 정의된 클러스터를 표시합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/view.go#L64
#: staging/src/k8s.io/kubectl/pkg/cmd/config/view.go:81
msgid "Display merged kubeconfig settings or a specified kubeconfig file"
msgstr "병합된 kubeconfig 설정 또는 지정된 kubeconfig 파일을 표시합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/config.go#L39
#: staging/src/k8s.io/kubectl/pkg/cmd/config/config.go:42
msgid "Modify kubeconfig files"
msgstr "kubeconfig 파일을 수정합니다"
#: staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go:135
msgid "Update the annotations on a resource"
msgstr "자원에 대한 주석을 업데이트합니다"
# https://github.com/kubernetes/kubernetes/blob/masterpkg/kubectl/cmd/apply.go#L98
#~ msgid "Apply a configuration to a resource by filename or stdin"
#~ msgstr "구성을 파일 이름 또는 stdin에 의한 자원에 적용합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/current_context.go#L48
#~ msgid "Displays the current-context"
#~ msgstr "현재-컨텍스트를 표시합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_cluster.go#L67
#~ msgid "Sets a cluster entry in kubeconfig"
#~ msgstr "kubeconfig에서 클러스터 항목을 설정합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_context.go#L57
#~ msgid "Sets a context entry in kubeconfig"
#~ msgstr "kubeconfig에서 컨텍스트 항목을 설정합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/create_authinfo.go#L103
#~ msgid "Sets a user entry in kubeconfig"
#~ msgstr "kubeconfig에서 사용자 항목을 설정합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/set.go#L59
#~ msgid "Sets an individual value in a kubeconfig file"
#~ msgstr "kubeconfig 파일에서 단일값을 설정합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/use_context.go#L48
#~ msgid "Sets the current-context in a kubeconfig file"
#~ msgstr "kubeconfig 파일에서 현재-컨텍스트를 설정합니다"
# https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/unset.go#L47
#~ msgid "Unsets an individual value in a kubeconfig file"
#~ msgstr "kubeconfig 파일에서 단일값 설정을 해제합니다"
#~ msgid ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgid_plural ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgstr[0] ""
#~ "watch는 단일 리소스와 리소스 모음만을 지원합니다 - %d 개 자원을 발견하였습"
#~ "니다"

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,81 @@
# Test translations for unit tests.
# Copyright (C) 2017
# This file is distributed under the same license as the Kubernetes package.
# FIRST AUTHOR warmchang@outlook.com, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: hello-world\n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2021-07-07 20:15+0200\n"
"PO-Revision-Date: 2017-06-02 09:13+0800\n"
"Last-Translator: William Chang <warmchang@outlook.com>\n"
"Language-Team: \n"
"Language: zh\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.2\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_cluster.go:42
msgid "Delete the specified cluster from the kubeconfig"
msgstr "刪除 kubeconfig 檔案中指定的叢集(cluster)"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/delete_context.go:42
msgid "Delete the specified context from the kubeconfig"
msgstr "刪除 kubeconfig 檔案中指定的 context"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_contexts.go:72
msgid "Describe one or many contexts"
msgstr "描述一個或多個 context"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/get_clusters.go:41
msgid "Display clusters defined in the kubeconfig"
msgstr "顯示 kubeconfig 檔案中定義的叢集(cluster)"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/view.go:81
msgid "Display merged kubeconfig settings or a specified kubeconfig file"
msgstr "顯示合併的 kubeconfig 配置或一個指定的 kubeconfig 檔案"
#: staging/src/k8s.io/kubectl/pkg/cmd/config/config.go:42
msgid "Modify kubeconfig files"
msgstr "修改 kubeconfig 檔案"
#: staging/src/k8s.io/kubectl/pkg/cmd/annotate/annotate.go:135
msgid "Update the annotations on a resource"
msgstr "更新一個資源的注解(annotations)"
#~ msgid "Apply a configuration to a resource by filename or stdin"
#~ msgstr "通過檔案名或標準輸入流(stdin)對資源進行配置"
#~ msgid "Displays the current-context"
#~ msgstr "顯示目前的 context"
#~ msgid "Sets a cluster entry in kubeconfig"
#~ msgstr "設置 kubeconfig 檔案中的一個叢集(cluster)條目"
#~ msgid "Sets a context entry in kubeconfig"
#~ msgstr "設置 kubeconfig 檔案中的一個 context 條目"
#~ msgid "Sets a user entry in kubeconfig"
#~ msgstr "設置 kubeconfig 檔案中的一個使用者條目"
#~ msgid "Sets an individual value in a kubeconfig file"
#~ msgstr "設置 kubeconfig 檔案中的一個值"
#~ msgid "Sets the current-context in a kubeconfig file"
#~ msgstr "設置 kubeconfig 檔案中的目前 context"
#~ msgid "Unsets an individual value in a kubeconfig file"
#~ msgstr "取消設置 kubeconfig 檔案中的一個值"
#~ msgid ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgid_plural ""
#~ "watch is only supported on individual resources and resource collections "
#~ "- %d resources were found"
#~ msgstr[0] "一次只能 watch 一個資源或資料集合 - 找到了 %d 個資源"
#~ msgstr[1] "一次只能 watch 一個資源或資料集合 - 找到了 %d 個資源"

Binary file not shown.

View File

@@ -0,0 +1,28 @@
# Test translations for unit tests.
# Copyright (C) 2016
# This file is distributed under the same license as the Kubernetes package.
# FIRST AUTHOR brendan.d.burns@gmail.com, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: gettext-go-examples-hello\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-12 20:03+0000\n"
"PO-Revision-Date: 2016-12-13 21:35-0800\n"
"Last-Translator: Brendan Burns <brendan.d.burns@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Language-Team: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: en\n"
msgid "test_plural"
msgid_plural "test_plural"
msgstr[0] "there was %d item"
msgstr[1] "there were %d items"
msgid "test_string"
msgstr "foo"

Binary file not shown.

View File

@@ -0,0 +1,28 @@
# Test translations for unit tests.
# Copyright (C) 2016
# This file is distributed under the same license as the Kubernetes package.
# FIRST AUTHOR brendan.d.burns@gmail.com, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: gettext-go-examples-hello\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-12 20:03+0000\n"
"PO-Revision-Date: 2016-12-13 22:12-0800\n"
"Last-Translator: Brendan Burns <brendan.d.burns@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Language-Team: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: en\n"
msgid "test_plural"
msgid_plural "test_plural"
msgstr[0] "there was %d item"
msgstr[1] "there were %d items"
msgid "test_string"
msgstr "baz"

View File

@@ -1,6 +1,6 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- apelisse
- apelisse
reviewers:
- apelisse
- apelisse

View File

@@ -16,7 +16,7 @@ limitations under the License.
package openapi
import "github.com/go-openapi/spec"
import "k8s.io/kube-openapi/pkg/validation/spec"
// PrintColumnsKey is the key that defines which columns should be printed
const PrintColumnsKey = "x-kubernetes-print-columns"

View File

@@ -17,7 +17,7 @@ limitations under the License.
package openapi
import (
openapi_v2 "github.com/googleapis/gnostic/openapiv2"
openapi_v2 "github.com/google/gnostic/openapiv2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kube-openapi/pkg/util/proto"

View File

@@ -19,7 +19,7 @@ package openapi
import (
"sync"
openapi_v2 "github.com/googleapis/gnostic/openapiv2"
openapi_v2 "github.com/google/gnostic/openapiv2"
"k8s.io/client-go/discovery"
)

View File

@@ -43,12 +43,12 @@ func NewSchemaValidation(resources openapi.Resources) *SchemaValidation {
// ValidateBytes will validates the object against using the Resources
// object.
func (v *SchemaValidation) ValidateBytes(data []byte) error {
obj, err := parse(data)
obj, err := Parse(data)
if err != nil {
return err
}
gvk, errs := getObjectKind(obj)
gvk, errs := GetObjectKind(obj)
if errs != nil {
return utilerrors.NewAggregate(errs)
}
@@ -71,7 +71,7 @@ func (v *SchemaValidation) validateList(object interface{}) []error {
return []error{errors.New("invalid object to validate")}
}
for _, item := range fields["items"].([]interface{}) {
if gvk, errs := getObjectKind(item); errs != nil {
if gvk, errs := GetObjectKind(item); errs != nil {
allErrors = append(allErrors, errs...)
} else {
allErrors = append(allErrors, v.validateResource(item, gvk)...)
@@ -90,7 +90,7 @@ func (v *SchemaValidation) validateResource(obj interface{}, gvk schema.GroupVer
return validation.ValidateModel(obj, resource, gvk.Kind)
}
func parse(data []byte) (interface{}, error) {
func Parse(data []byte) (interface{}, error) {
var obj interface{}
out, err := yaml.ToJSON(data)
if err != nil {
@@ -102,7 +102,7 @@ func parse(data []byte) (interface{}, error) {
return obj, nil
}
func getObjectKind(object interface{}) (schema.GroupVersionKind, []error) {
func GetObjectKind(object interface{}) (schema.GroupVersionKind, []error) {
var listErrors []error
fields, ok := object.(map[string]interface{})
if !ok || fields == nil {

View File

@@ -0,0 +1,76 @@
/*
Copyright 2022 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 templates
import (
"bytes"
"fmt"
"io"
"strings"
"github.com/mitchellh/go-wordwrap"
flag "github.com/spf13/pflag"
)
const offset = 10
// HelpFlagPrinter is a printer that
// processes the help flag and print
// it to i/o writer
type HelpFlagPrinter struct {
wrapLimit uint
out io.Writer
}
// NewHelpFlagPrinter will initialize a HelpFlagPrinter given the
// i/o writer
func NewHelpFlagPrinter(out io.Writer, wrapLimit uint) *HelpFlagPrinter {
return &HelpFlagPrinter{
wrapLimit: wrapLimit,
out: out,
}
}
// PrintHelpFlag will beautify the help flags and print it out to p.out
func (p *HelpFlagPrinter) PrintHelpFlag(flag *flag.Flag) {
formatBuf := new(bytes.Buffer)
writeFlag(formatBuf, flag)
wrappedStr := formatBuf.String()
flagAndUsage := strings.Split(formatBuf.String(), "\n")
flagStr := flagAndUsage[0]
// if the flag usage is longer than one line, wrap it again
if len(flagAndUsage) > 1 {
nextLines := strings.Join(flagAndUsage[1:], " ")
wrappedUsages := wordwrap.WrapString(nextLines, p.wrapLimit-offset)
wrappedStr = flagStr + "\n" + wrappedUsages
}
appendTabStr := strings.ReplaceAll(wrappedStr, "\n", "\n\t")
fmt.Fprintf(p.out, appendTabStr+"\n\n")
}
// writeFlag will output the help flag based
// on the format provided by getFlagFormat to i/o writer
func writeFlag(out io.Writer, f *flag.Flag) {
deprecated := ""
if f.Deprecated != "" {
deprecated = fmt.Sprintf(" (DEPRECATED: %s)", f.Deprecated)
}
fmt.Fprintf(out, getFlagFormat(f), f.Shorthand, f.Name, f.DefValue, f.Usage, deprecated)
}

View File

@@ -23,10 +23,10 @@ import (
"text/template"
"unicode"
"k8s.io/kubectl/pkg/util/term"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"k8s.io/kubectl/pkg/util/term"
)
type FlagExposer interface {
@@ -160,7 +160,7 @@ func (t *templater) cmdGroupsString(c *cobra.Command) string {
cmds := []string{cmdGroup.Message}
for _, cmd := range cmdGroup.Commands {
if cmd.IsAvailableCommand() {
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
}
}
groups = append(groups, strings.Join(cmds, "\n"))
@@ -218,29 +218,40 @@ func (t *templater) usageLine(c *cobra.Command) string {
return usage
}
func flagsUsages(f *flag.FlagSet) string {
x := new(bytes.Buffer)
// flagsUsages will print out the kubectl help flags
func flagsUsages(f *flag.FlagSet) (string, error) {
flagBuf := new(bytes.Buffer)
wrapLimit, err := term.GetWordWrapperLimit()
if err != nil {
wrapLimit = 0
}
printer := NewHelpFlagPrinter(flagBuf, wrapLimit)
f.VisitAll(func(flag *flag.Flag) {
if flag.Hidden {
return
}
format := "--%s=%s: %s\n"
if flag.Value.Type() == "string" {
format = "--%s='%s': %s\n"
}
if len(flag.Shorthand) > 0 {
format = " -%s, " + format
} else {
format = " %s " + format
}
fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
printer.PrintHelpFlag(flag)
})
return x.String()
return flagBuf.String(), nil
}
// getFlagFormat will output the flag format
func getFlagFormat(f *flag.Flag) string {
var format string
format = "--%s=%s:\n%s%s"
if f.Value.Type() == "string" {
format = "--%s='%s':\n%s%s"
}
if len(f.Shorthand) > 0 {
format = " -%s, " + format
} else {
format = " %s" + format
}
return format
}
func rpad(s string, padding int) string {

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*

View File

@@ -17,11 +17,14 @@ limitations under the License.
package term
import (
"errors"
"io"
"os"
wordwrap "github.com/mitchellh/go-wordwrap"
"github.com/moby/term"
"k8s.io/client-go/tools/remotecommand"
)
type wordWrapWriter struct {
@@ -32,9 +35,11 @@ type wordWrapWriter struct {
// NewResponsiveWriter creates a Writer that detects the column width of the
// terminal we are in, and adjusts every line width to fit and use recommended
// terminal sizes for better readability. Does proper word wrapping automatically.
// if terminal width >= 120 columns use 120 columns
// if terminal width >= 100 columns use 100 columns
// if terminal width >= 80 columns use 80 columns
//
// if terminal width >= 120 columns use 120 columns
// if terminal width >= 100 columns use 100 columns
// if terminal width >= 80 columns use 80 columns
//
// In case we're not in a terminal or if it's smaller than 80 columns width,
// doesn't do any wrapping.
func NewResponsiveWriter(w io.Writer) io.Writer {
@@ -51,16 +56,7 @@ func NewResponsiveWriter(w io.Writer) io.Writer {
if terminalSize == nil {
return w
}
var limit uint
switch {
case terminalSize.Width >= 120:
limit = 120
case terminalSize.Width >= 100:
limit = 100
case terminalSize.Width >= 80:
limit = 80
}
limit := getTerminalLimitWidth(terminalSize)
return NewWordWrapWriter(w, limit)
}
@@ -74,6 +70,32 @@ func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
}
}
func getTerminalLimitWidth(terminalSize *remotecommand.TerminalSize) uint {
var limit uint
switch {
case terminalSize.Width >= 120:
limit = 120
case terminalSize.Width >= 100:
limit = 100
case terminalSize.Width >= 80:
limit = 80
}
return limit
}
func GetWordWrapperLimit() (uint, error) {
stdout := os.Stdout
fd := stdout.Fd()
if !term.IsTerminal(fd) {
return 0, errors.New("file descriptor is not a terminal")
}
terminalSize := GetSize(fd)
if terminalSize == nil {
return 0, errors.New("terminal size is nil")
}
return getTerminalLimitWidth(terminalSize), nil
}
func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
if w.limit == 0 {
return w.writer.Write(p)

View File

@@ -22,7 +22,11 @@ import (
"fmt"
ejson "github.com/exponent-io/jsonpath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/klog/v2"
schemavalidation "k8s.io/kubectl/pkg/util/openapi/validation"
)
// Schema is an interface that knows how to validate an API object serialized to a byte array.
@@ -101,3 +105,50 @@ func (c ConjunctiveSchema) ValidateBytes(data []byte) error {
}
return utilerrors.NewAggregate(list)
}
func NewParamVerifyingSchema(s Schema, verifier resource.Verifier, directive string) Schema {
return &paramVerifyingSchema{
schema: s,
verifier: verifier,
directive: directive,
}
}
// paramVerifyingSchema only performs validation
// based on the fieldValidation query param
// being unsupported by the apiserver, because
// server-side validation will be performed instead
// of client-side validation.
type paramVerifyingSchema struct {
schema Schema
verifier resource.Verifier
directive string
}
// ValidateBytes validates bytes per a ParamVerifyingSchema
func (c *paramVerifyingSchema) ValidateBytes(data []byte) error {
obj, err := schemavalidation.Parse(data)
if err != nil {
return err
}
gvk, errs := schemavalidation.GetObjectKind(obj)
if errs != nil {
return utilerrors.NewAggregate(errs)
}
err = c.verifier.HasSupport(gvk)
if resource.IsParamUnsupportedError(err) {
switch c.directive {
case metav1.FieldValidationStrict:
return c.schema.ValidateBytes(data)
case metav1.FieldValidationWarn:
klog.Warningf("cannot perform warn validation if server-side field validation is unsupported, skipping validation")
default:
// can't be reached
klog.Warningf("unexpected field validation directive: %s, skipping validation", c.directive)
}
return nil
}
return err
}