feat: kubesphere 4.0 (#6115)

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

* feat: kubesphere 4.0

Signed-off-by: ci-bot <ci-bot@kubesphere.io>

---------

Signed-off-by: ci-bot <ci-bot@kubesphere.io>
Co-authored-by: ks-ci-bot <ks-ci-bot@example.com>
Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
KubeSphere CI Bot
2024-09-06 11:05:52 +08:00
committed by GitHub
parent b5015ec7b9
commit 447a51f08b
8557 changed files with 546695 additions and 1146174 deletions

View File

@@ -24,8 +24,10 @@ import (
"fmt"
"os"
"strings"
"sync"
"github.com/chai2010/gettext-go"
gettext "github.com/chai2010/gettext-go"
"k8s.io/klog/v2"
)
@@ -52,6 +54,56 @@ var knownTranslations = map[string][]string{
},
}
var (
lazyLoadTranslationsOnce sync.Once
LoadTranslationsFunc = func() error {
return LoadTranslations("kubectl", nil)
}
translationsLoaded bool
)
// SetLoadTranslationsFunc sets the function called to lazy load translations.
// It must be called in an init() func that occurs BEFORE any i18n.T() calls are made by any package. You can
// accomplish this by creating a separate package containing your init() func, and then importing that package BEFORE
// any other packages that call i18n.T().
//
// Example Usage:
//
// package myi18n
//
// import "k8s.io/kubectl/pkg/util/i18n"
//
// func init() {
// if err := i18n.SetLoadTranslationsFunc(loadCustomTranslations); err != nil {
// panic(err)
// }
// }
//
// func loadCustomTranslations() error {
// // Load your custom translations here...
// }
//
// And then in your main or root command package, import your custom package like this:
//
// import (
// // Other imports that don't need i18n...
// _ "example.com/myapp/myi18n"
// // Other imports that do need i18n...
// )
func SetLoadTranslationsFunc(f func() error) error {
if translationsLoaded {
return errors.New("translations have already been loaded")
}
LoadTranslationsFunc = func() error {
if err := f(); err != nil {
return err
}
translationsLoaded = true
return nil
}
return nil
}
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
@@ -128,13 +180,26 @@ func LoadTranslations(root string, getLanguageFn func() string) error {
gettext.BindLocale(gettext.New("k8s", root+".zip", buf.Bytes()))
gettext.SetDomain("k8s")
gettext.SetLanguage(langStr)
translationsLoaded = true
return nil
}
func lazyLoadTranslations() {
lazyLoadTranslationsOnce.Do(func() {
if translationsLoaded {
return
}
if err := LoadTranslationsFunc(); err != nil {
klog.Warning("Failed to load translations")
}
})
}
// 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 {
lazyLoadTranslations()
if len(args) == 0 {
return gettext.PGettext("", defaultValue)
}

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright 2017 The Kubernetes Authors.
#

View File

@@ -712,14 +712,10 @@ msgid ""
"\n"
"\t\t# Create an interactive debugging session in pod mypod and immediately "
"attach to it.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug mypod -it --image=busybox\n"
"\n"
"\t\t# Create a debug container named debugger using a custom automated "
"debugging image.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug --image=myproj/debug-tools -c debugger mypod\n"
"\n"
"\t\t# Create a copy of mypod adding a debug container and attach to it\n"
@@ -746,14 +742,10 @@ msgstr ""
"\n"
"\t\t# Create an interactive debugging session in pod mypod and immediately "
"attach to it.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug mypod -it --image=busybox\n"
"\n"
"\t\t# Create a debug container named debugger using a custom automated "
"debugging image.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug --image=myproj/debug-tools -c debugger mypod\n"
"\n"
"\t\t# Create a copy of mypod adding a debug container and attach to it\n"

View File

@@ -712,14 +712,10 @@ msgid ""
"\n"
"\t\t# Create an interactive debugging session in pod mypod and immediately "
"attach to it.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug mypod -it --image=busybox\n"
"\n"
"\t\t# Create a debug container named debugger using a custom automated "
"debugging image.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug --image=myproj/debug-tools -c debugger mypod\n"
"\n"
"\t\t# Create a copy of mypod adding a debug container and attach to it\n"
@@ -746,14 +742,10 @@ msgstr ""
"\n"
"\t\t# Create an interactive debugging session in pod mypod and immediately "
"attach to it.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug mypod -it --image=busybox\n"
"\n"
"\t\t# Create a debug container named debugger using a custom automated "
"debugging image.\n"
"\t\t# (requires the EphemeralContainers feature to be enabled in the "
"cluster)\n"
"\t\tkubectl debug --image=myproj/debug-tools -c debugger mypod\n"
"\n"
"\t\t# Create a copy of mypod adding a debug container and attach to it\n"

View File

@@ -2167,7 +2167,7 @@ msgstr "Kubectl controlla il gestore cluster di Kubernetes"
#~ msgstr ""
#~ "\n"
#~ "\t\tCrea un autoscaler che automaticamente sceglie e imposta il numero di "
#~ "pod che vengono eseguiti in un cluster di kubernets.\n"
#~ "pod che vengono eseguiti in un cluster di kubernetes.\n"
#~ "\n"
#~ "\t\tEsegue una ricerca di un Deployment, ReplicaSet o "
#~ "ReplicationController per nome e crea un autoscaler che utilizza la "

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,7 @@ msgid ""
msgstr ""
"\n"
"\t\t # 显示所有节点的指标\n"
"\t\t kubectl top ode\n"
"\t\t kubectl top node\n"
"\n"
"\t\t # 显示指定节点的指标\n"
"\t\t kubectl top node NODE_NAME"

View File

@@ -1,27 +0,0 @@
/*
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 openapi
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"
// GetPrintColumns looks for the open API extension for the display columns.
func GetPrintColumns(extensions spec.Extensions) (string, bool) {
return extensions.GetString(PrintColumnsKey)
}

View File

@@ -17,13 +17,20 @@ limitations under the License.
package openapi
import (
openapi_v2 "github.com/google/gnostic/openapiv2"
openapi_v2 "github.com/google/gnostic-models/openapiv2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kube-openapi/pkg/util/proto"
"sigs.k8s.io/yaml"
)
// OpenAPIResourcesGetter represents a function to return
// OpenAPI V2 resource specifications. Used for lazy-loading
// these resource specifications.
type OpenAPIResourcesGetter interface {
OpenAPISchema() (Resources, error)
}
// Resources interface describe a resources provider, that can give you
// resource based on group-version-kind.
type Resources interface {

View File

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

View File

@@ -1,140 +0,0 @@
/*
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 validation
import (
"errors"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/kube-openapi/pkg/util/proto/validation"
"k8s.io/kubectl/pkg/util/openapi"
)
// SchemaValidation validates the object against an OpenAPI schema.
type SchemaValidation struct {
resources openapi.Resources
}
// NewSchemaValidation creates a new SchemaValidation that can be used
// to validate objects.
func NewSchemaValidation(resources openapi.Resources) *SchemaValidation {
return &SchemaValidation{
resources: resources,
}
}
// ValidateBytes will validates the object against using the Resources
// object.
func (v *SchemaValidation) ValidateBytes(data []byte) error {
obj, err := Parse(data)
if err != nil {
return err
}
gvk, errs := GetObjectKind(obj)
if errs != nil {
return utilerrors.NewAggregate(errs)
}
if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
return utilerrors.NewAggregate(v.validateList(obj))
}
return utilerrors.NewAggregate(v.validateResource(obj, gvk))
}
func (v *SchemaValidation) validateList(object interface{}) []error {
fields, ok := object.(map[string]interface{})
if !ok || fields == nil {
return []error{errors.New("invalid object to validate")}
}
allErrors := []error{}
if _, ok := fields["items"].([]interface{}); !ok {
return []error{errors.New("invalid object to validate")}
}
for _, item := range fields["items"].([]interface{}) {
if gvk, errs := GetObjectKind(item); errs != nil {
allErrors = append(allErrors, errs...)
} else {
allErrors = append(allErrors, v.validateResource(item, gvk)...)
}
}
return allErrors
}
func (v *SchemaValidation) validateResource(obj interface{}, gvk schema.GroupVersionKind) []error {
resource := v.resources.LookupResource(gvk)
if resource == nil {
// resource is not present, let's just skip validation.
return nil
}
return validation.ValidateModel(obj, resource, gvk.Kind)
}
func Parse(data []byte) (interface{}, error) {
var obj interface{}
out, err := yaml.ToJSON(data)
if err != nil {
return nil, err
}
if err := json.Unmarshal(out, &obj); err != nil {
return nil, err
}
return obj, nil
}
func GetObjectKind(object interface{}) (schema.GroupVersionKind, []error) {
var listErrors []error
fields, ok := object.(map[string]interface{})
if !ok || fields == nil {
listErrors = append(listErrors, errors.New("invalid object to validate"))
return schema.GroupVersionKind{}, listErrors
}
var group string
var version string
apiVersion := fields["apiVersion"]
if apiVersion == nil {
listErrors = append(listErrors, errors.New("apiVersion not set"))
} else if _, ok := apiVersion.(string); !ok {
listErrors = append(listErrors, errors.New("apiVersion isn't string type"))
} else {
gv, err := schema.ParseGroupVersion(apiVersion.(string))
if err != nil {
listErrors = append(listErrors, err)
} else {
group = gv.Group
version = gv.Version
}
}
kind := fields["kind"]
if kind == nil {
listErrors = append(listErrors, errors.New("kind not set"))
} else if _, ok := kind.(string); !ok {
listErrors = append(listErrors, errors.New("kind isn't string type"))
}
if listErrors != nil {
return schema.GroupVersionKind{}, listErrors
}
return schema.GroupVersionKind{Group: group, Version: version, Kind: kind.(string)}, nil
}

View File

@@ -1,172 +0,0 @@
/*
Copyright 2018 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 resource
import (
"fmt"
"math"
"strconv"
"strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets"
)
// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all
// containers of the pod. If pod overhead is non-nil, the pod overhead is added to the
// total container resource requests and to the total container limits which have a
// non-zero quantity.
func PodRequestsAndLimits(pod *corev1.Pod) (reqs, limits corev1.ResourceList) {
reqs, limits = corev1.ResourceList{}, corev1.ResourceList{}
for _, container := range pod.Spec.Containers {
addResourceList(reqs, container.Resources.Requests)
addResourceList(limits, container.Resources.Limits)
}
// init containers define the minimum of any resource
for _, container := range pod.Spec.InitContainers {
maxResourceList(reqs, container.Resources.Requests)
maxResourceList(limits, container.Resources.Limits)
}
// Add overhead for running a pod to the sum of requests and to non-zero limits:
if pod.Spec.Overhead != nil {
addResourceList(reqs, pod.Spec.Overhead)
for name, quantity := range pod.Spec.Overhead {
if value, ok := limits[name]; ok && !value.IsZero() {
value.Add(quantity)
limits[name] = value
}
}
}
return
}
// addResourceList adds the resources in newList to list
func addResourceList(list, new corev1.ResourceList) {
for name, quantity := range new {
if value, ok := list[name]; !ok {
list[name] = quantity.DeepCopy()
} else {
value.Add(quantity)
list[name] = value
}
}
}
// maxResourceList sets list to the greater of list/newList for every resource
// either list
func maxResourceList(list, new corev1.ResourceList) {
for name, quantity := range new {
if value, ok := list[name]; !ok {
list[name] = quantity.DeepCopy()
continue
} else {
if quantity.Cmp(value) > 0 {
list[name] = quantity.DeepCopy()
}
}
}
}
// ExtractContainerResourceValue extracts the value of a resource
// in an already known container
func ExtractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *corev1.Container) (string, error) {
divisor := resource.Quantity{}
if divisor.Cmp(fs.Divisor) == 0 {
divisor = resource.MustParse("1")
} else {
divisor = fs.Divisor
}
switch fs.Resource {
case "limits.cpu":
return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
case "limits.memory":
return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
case "limits.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
case "requests.cpu":
return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
case "requests.memory":
return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
case "requests.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
}
// handle extended standard resources with dynamic names
// example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
if strings.HasPrefix(fs.Resource, "requests.") {
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
if IsHugePageResourceName(resourceName) {
return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
}
}
if strings.HasPrefix(fs.Resource, "limits.") {
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
if IsHugePageResourceName(resourceName) {
return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
}
}
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
}
// convertResourceCPUToString converts cpu value to the format of divisor and returns
// ceiling of the value.
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
return strconv.FormatInt(c, 10), nil
}
// convertResourceMemoryToString converts memory value to the format of divisor and returns
// ceiling of the value.
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil
}
// convertResourceHugePagesToString converts hugepages value to the format of divisor and returns
// ceiling of the value.
func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil
}
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
// ceiling of the value.
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil
}
var standardContainerResources = sets.NewString(
string(corev1.ResourceCPU),
string(corev1.ResourceMemory),
string(corev1.ResourceEphemeralStorage),
)
// IsStandardContainerResourceName returns true if the container can make a resource request
// for the specified resource
func IsStandardContainerResourceName(str string) bool {
return standardContainerResources.Has(str) || IsHugePageResourceName(corev1.ResourceName(str))
}
// IsHugePageResourceName returns true if the resource name has the huge page
// resource prefix.
func IsHugePageResourceName(name corev1.ResourceName) bool {
return strings.HasPrefix(string(name), corev1.ResourceHugePagesPrefix)
}

View File

@@ -58,7 +58,9 @@ func (r *ASCIIRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
lines := []string{}
for _, line := range strings.Split(string(node.Literal), linebreak) {
trimmed := strings.Trim(line, " \t")
indented := r.Indentation + trimmed
// Adding 4 times of indentation will let blackfriday to accept
// this literal as Code or CodeBlock again in next invocation
indented := strings.Repeat(r.Indentation, 4) + trimmed
lines = append(lines, indented)
}
w.Write([]byte(strings.Join(lines, linebreak)))

View File

@@ -126,6 +126,7 @@ func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncM
"isRootCmd": templater.isRootCmd,
"optionsCmdFor": templater.optionsCmdFor,
"usageLine": templater.usageLine,
"reverseParentsNames": templater.reverseParentsNames,
"exposed": func(c *cobra.Command) *flag.FlagSet {
exposed := flag.NewFlagSet("exposed", flag.ContinueOnError)
if len(exposedFlags) > 0 {
@@ -172,6 +173,15 @@ func (t *templater) rootCmdName(c *cobra.Command) string {
return t.rootCmd(c).CommandPath()
}
func (t *templater) reverseParentsNames(c *cobra.Command) []string {
reverseParentsNames := []string{}
parents := t.parents(c)
for i := len(parents) - 1; i >= 0; i-- {
reverseParentsNames = append(reverseParentsNames, parents[i].Name())
}
return reverseParentsNames
}
func (t *templater) isRootCmd(c *cobra.Command) bool {
return t.rootCmd(c) == c
}

View File

@@ -28,7 +28,8 @@ const (
`{{$visibleFlags := visibleFlags (flagsNotIntersected .LocalFlags .PersistentFlags)}}` +
`{{$explicitlyExposedFlags := exposed .}}` +
`{{$optionsCmdFor := optionsCmdFor .}}` +
`{{$usageLine := usageLine .}}`
`{{$usageLine := usageLine .}}` +
`{{$reverseParentsNames := reverseParentsNames .}}`
// SectionAliases is the help template section that displays command aliases.
SectionAliases = `{{if gt .Aliases 0}}Aliases:
@@ -61,7 +62,7 @@ const (
{{end}}`
// SectionTipsHelp is the help template section that displays the '--help' hint.
SectionTipsHelp = `{{if .HasSubCommands}}Use "{{$rootCmd}} <command> --help" for more information about a given command.
SectionTipsHelp = `{{if .HasSubCommands}}Use "{{range $reverseParentsNames}}{{.}} {{end}}<command> --help" for more information about a given command.
{{end}}`
// SectionTipsGlobalOptions is the help template section that displays the 'options' hint for displaying global flags.

View File

@@ -19,7 +19,8 @@ package term
import (
"io"
"os"
"runtime"
"k8s.io/cli-runtime/pkg/printers"
"github.com/moby/term"
@@ -56,46 +57,23 @@ type TTY struct {
// IsTerminalIn returns true if t.In is a terminal. Does not check /dev/tty
// even if TryDev is set.
func (t TTY) IsTerminalIn() bool {
return IsTerminal(t.In)
return printers.IsTerminal(t.In)
}
// IsTerminalOut returns true if t.Out is a terminal. Does not check /dev/tty
// even if TryDev is set.
func (t TTY) IsTerminalOut() bool {
return IsTerminal(t.Out)
return printers.IsTerminal(t.Out)
}
// IsTerminal returns whether the passed object is a terminal or not
func IsTerminal(i interface{}) bool {
_, terminal := term.GetFdInfo(i)
return terminal
}
// IsTerminal returns whether the passed object is a terminal or not.
// Deprecated: use printers.IsTerminal instead.
var IsTerminal = printers.IsTerminal
// AllowsColorOutput returns true if the specified writer is a terminal and
// the process environment indicates color output is supported and desired.
func AllowsColorOutput(w io.Writer) bool {
if !IsTerminal(w) {
return false
}
// https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
if os.Getenv("TERM") == "dumb" {
return false
}
// https://no-color.org/
if _, nocolor := os.LookupEnv("NO_COLOR"); nocolor {
return false
}
// On Windows WT_SESSION is set by the modern terminal component.
// Older terminals have poor support for UTF-8, VT escape codes, etc.
if runtime.GOOS == "windows" && os.Getenv("WT_SESSION") == "" {
return false
}
return true
}
// Deprecated: use printers.AllowsColorOutput instead.
var AllowsColorOutput = printers.AllowsColorOutput
// Safe invokes the provided function and will attempt to ensure that when the
// function returns (or a termination signal is sent) that the terminal state