summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKubeEdge Bot <48982446+kubeedge-bot@users.noreply.github.com>2024-04-23 16:42:01 +0800
committerGitHub <noreply@github.com>2024-04-23 16:42:01 +0800
commit8defa9d91ffe1744feaffc60fe7647c55e12608a (patch)
tree482de9c92700cde3e803270b280e15156b71cc97
parentMerge pull request #5513 from micplus/feat/module-restart (diff)
parentWhen the edge node goes offline, add pod restart and status query functions f... (diff)
downloadkubeedge-8defa9d91ffe1744feaffc60fe7647c55e12608a.tar.gz
Merge pull request #5504 from luomengY/kectl
When the edge node goes offline, add pod restart and status query functions for the edge node
-rw-r--r--LICENSES/vendor/github.com/fvbommel/sortorder/LICENSE21
-rw-r--r--go.mod3
-rw-r--r--go.sum2
-rw-r--r--keadm/cmd/keadm/app/cmd/cmd_others.go3
-rw-r--r--keadm/cmd/keadm/app/cmd/common/constant.go7
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/ctl.go41
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/get/get.go35
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/get/pod.go181
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/restart/pod.go121
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/restart/restart.go34
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/restful/pod.go73
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/restful/request.go151
-rw-r--r--keadm/cmd/keadm/app/cmd/ctl/util/pod.go34
-rw-r--r--vendor/github.com/fvbommel/sortorder/.gitignore19
-rw-r--r--vendor/github.com/fvbommel/sortorder/LICENSE17
-rw-r--r--vendor/github.com/fvbommel/sortorder/README.md9
-rw-r--r--vendor/github.com/fvbommel/sortorder/doc.go5
-rw-r--r--vendor/github.com/fvbommel/sortorder/natsort.go76
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn.go262
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn_flags.go113
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/get.go821
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/get_flags.go170
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/humanreadable_flags.go138
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/skip_printer.go48
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/sorter.go424
-rw-r--r--vendor/k8s.io/kubectl/pkg/cmd/get/table_printer.go94
-rw-r--r--vendor/k8s.io/kubectl/pkg/rawhttp/raw.go95
-rw-r--r--vendor/k8s.io/kubectl/pkg/util/slice/slice.go57
-rw-r--r--vendor/modules.txt6
29 files changed, 3059 insertions, 1 deletions
diff --git a/LICENSES/vendor/github.com/fvbommel/sortorder/LICENSE b/LICENSES/vendor/github.com/fvbommel/sortorder/LICENSE
new file mode 100644
index 000000000..4eadd661c
--- /dev/null
+++ b/LICENSES/vendor/github.com/fvbommel/sortorder/LICENSE
@@ -0,0 +1,21 @@
+= vendor/github.com/fvbommel/sortorder licensed under: =
+
+The MIT License (MIT)
+Copyright (c) 2015 Frits van Bommel
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+= vendor/github.com/fvbommel/sortorder/LICENSE 9f7e1d7e8f527330ebb5f4c32e0f3e40
diff --git a/go.mod b/go.mod
index 41a4e18a2..e7d8e1347 100644
--- a/go.mod
+++ b/go.mod
@@ -68,6 +68,7 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/component-helpers v0.0.0
+ k8s.io/kubectl v0.28.6
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
)
@@ -127,6 +128,7 @@ require (
github.com/fatih/color v1.13.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
+ github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
github.com/go-logr/logr v1.2.4 // indirect
@@ -270,7 +272,6 @@ require (
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect
k8s.io/kms v0.28.6 //indirect
k8s.io/kube-scheduler v0.28.6 // indirect
- k8s.io/kubectl v0.28.6 // indirect
k8s.io/legacy-cloud-providers v0.0.0 // indirect
k8s.io/pod-security-admission v0.0.0 // indirect
k8s.io/system-validators v1.8.0 // indirect
diff --git a/go.sum b/go.sum
index 7a185b8a3..9faee47de 100644
--- a/go.sum
+++ b/go.sum
@@ -938,6 +938,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
+github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
diff --git a/keadm/cmd/keadm/app/cmd/cmd_others.go b/keadm/cmd/keadm/app/cmd/cmd_others.go
index 30004e279..ed3bf3b80 100644
--- a/keadm/cmd/keadm/app/cmd/cmd_others.go
+++ b/keadm/cmd/keadm/app/cmd/cmd_others.go
@@ -23,6 +23,7 @@ import (
"github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/beta"
"github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/cloud"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl"
"github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/debug"
"github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/deprecated"
"github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/edge"
@@ -91,5 +92,7 @@ func NewKubeedgeCommand() *cobra.Command {
cmds.AddCommand(edge.NewEdgeRollback())
+ cmds.AddCommand(ctl.NewCtl())
+
return cmds
}
diff --git a/keadm/cmd/keadm/app/cmd/common/constant.go b/keadm/cmd/keadm/app/cmd/common/constant.go
index b7fdc7012..05c07b55e 100644
--- a/keadm/cmd/keadm/app/cmd/common/constant.go
+++ b/keadm/cmd/keadm/app/cmd/common/constant.go
@@ -280,3 +280,10 @@ var (
// make it an var so it can be changed to adapt to windows(In rare cases, user name is Administrator)
DefaultKubeConfig = "/root/.kube/config"
)
+
+const (
+ FlagNameNamespace = "namespace"
+ FlagNameAllNamespaces = "all-namespaces"
+ FlagNameOutput = "output"
+ FlagNameLabelSelector = "selector"
+)
diff --git a/keadm/cmd/keadm/app/cmd/ctl/ctl.go b/keadm/cmd/keadm/app/cmd/ctl/ctl.go
new file mode 100644
index 000000000..06cdc8f74
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/ctl.go
@@ -0,0 +1,41 @@
+/*
+Copyright 2024 The KubeEdge 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 ctl
+
+import (
+ "github.com/spf13/cobra"
+
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl/get"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl/restart"
+)
+
+var (
+ ctlShortDescription = `Commands operating on the data plane at edge`
+)
+
+// NewCtl returns KubeEdge edge pod command.
+func NewCtl() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "ctl",
+ Short: ctlShortDescription,
+ Long: ctlShortDescription,
+ }
+
+ cmd.AddCommand(get.NewEdgeGet())
+ cmd.AddCommand(restart.NewEdgeRestart())
+ return cmd
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/get/get.go b/keadm/cmd/keadm/app/cmd/ctl/get/get.go
new file mode 100644
index 000000000..7408f1941
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/get/get.go
@@ -0,0 +1,35 @@
+/*
+Copyright 2024 The KubeEdge 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 get
+
+import "github.com/spf13/cobra"
+
+var (
+ edgeGetShortDescription = `Get resources in edge node`
+)
+
+// NewEdgeGet returns KubeEdge edge resources get command.
+func NewEdgeGet() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "get",
+ Short: edgeGetShortDescription,
+ Long: edgeGetShortDescription,
+ }
+
+ cmd.AddCommand(NewEdgePodGet())
+ return cmd
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/get/pod.go b/keadm/cmd/keadm/app/cmd/ctl/get/pod.go
new file mode 100644
index 000000000..d06974eb2
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/get/pod.go
@@ -0,0 +1,181 @@
+/*
+Copyright 2024 The KubeEdge 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 get
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/spf13/cobra"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/kubectl/pkg/cmd/get"
+ cmdutil "k8s.io/kubectl/pkg/cmd/util"
+ api "k8s.io/kubernetes/pkg/apis/core"
+ k8s_v1_api "k8s.io/kubernetes/pkg/apis/core/v1"
+ k8sprinters "k8s.io/kubernetes/pkg/printers"
+ printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
+ "k8s.io/kubernetes/pkg/printers/storage"
+
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl/restful"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/util"
+)
+
+var (
+ edgePodGetShortDescription = `Get pods in edge node`
+)
+
+type PodGetOptions struct {
+ Namespace string
+ LabelSelector string
+ AllNamespaces bool
+ Output string
+ PrintFlags *get.PrintFlags
+}
+
+// NewEdgePodGet returns KubeEdge edge pod command.
+func NewEdgePodGet() *cobra.Command {
+ podGetOptions := NewGetOpts()
+ cmd := &cobra.Command{
+ Use: "pod",
+ Short: edgePodGetShortDescription,
+ Long: edgePodGetShortDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ cmdutil.CheckErr(podGetOptions.getPods(args))
+ return nil
+ },
+ }
+ AddGetPodFlags(cmd, podGetOptions)
+ return cmd
+}
+
+func (o *PodGetOptions) getPods(args []string) error {
+ config, err := util.ParseEdgecoreConfig(common.EdgecoreConfigPath)
+ if err != nil {
+ return fmt.Errorf("get edge config failed with err:%v", err)
+ }
+ nodeName := config.Modules.Edged.HostnameOverride
+
+ var podListFilter *api.PodList
+ if len(args) > 0 {
+ podListFilter = &api.PodList{
+ Items: make([]api.Pod, 0, len(args)),
+ }
+ var podRequest *restful.PodRequest
+ for _, podName := range args {
+ podRequest = &restful.PodRequest{
+ Namespace: o.Namespace,
+ PodName: podName,
+ }
+ pod, err := podRequest.GetPod()
+ if err != nil {
+ fmt.Println(err.Error())
+ continue
+ }
+
+ if pod.Spec.NodeName == nodeName {
+ var apiPod api.Pod
+ if err := k8s_v1_api.Convert_v1_Pod_To_core_Pod(pod, &apiPod, nil); err != nil {
+ fmt.Printf("failed to covert pod with err:%v\n", err)
+ continue
+ }
+ podListFilter.Items = append(podListFilter.Items, apiPod)
+ } else {
+ fmt.Printf("can't to query pod: \"%s\" for node: \"%s\"\n", pod.Name, pod.Spec.NodeName)
+ }
+ }
+ } else {
+ podRequest := &restful.PodRequest{
+ Namespace: o.Namespace,
+ AllNamespaces: o.AllNamespaces,
+ LabelSelector: o.LabelSelector,
+ }
+ podList, err := podRequest.GetPods()
+ if err != nil {
+ return err
+ }
+
+ podListFilter = &api.PodList{
+ Items: make([]api.Pod, 0, len(podList.Items)),
+ }
+
+ for _, pod := range podList.Items {
+ if pod.Spec.NodeName == nodeName {
+ var apiPod api.Pod
+ if err := k8s_v1_api.Convert_v1_Pod_To_core_Pod(&pod, &apiPod, nil); err != nil {
+ return err
+ }
+ podListFilter.Items = append(podListFilter.Items, apiPod)
+ }
+ }
+ }
+
+ if len(podListFilter.Items) == 0 {
+ if len(args) > 0 {
+ return nil
+ }
+ fmt.Printf("No resources found in %s namespace.\n", o.Namespace)
+ return nil
+ }
+
+ table, err := ConvertDataToTable(podListFilter)
+ if err != nil {
+ return err
+ }
+
+ if o.AllNamespaces {
+ if err := o.PrintFlags.EnsureWithNamespace(); err != nil {
+ return err
+ }
+ }
+
+ printer, err := o.PrintFlags.ToPrinter()
+ if err := printer.PrintObj(table, os.Stdout); err != nil {
+ return err
+ }
+ return nil
+}
+
+func NewGetOpts() *PodGetOptions {
+ podGetOptions := &PodGetOptions{}
+ podGetOptions.Namespace = "default"
+ podGetOptions.PrintFlags = get.NewGetPrintFlags()
+ podGetOptions.PrintFlags.OutputFormat = &podGetOptions.Output
+ return podGetOptions
+}
+
+func AddGetPodFlags(cmd *cobra.Command, getOptions *PodGetOptions) {
+ cmd.Flags().StringVarP(&getOptions.Namespace, common.FlagNameNamespace, "n", getOptions.Namespace,
+ "Specify a namespace")
+ cmd.Flags().StringVarP(&getOptions.LabelSelector, common.FlagNameLabelSelector, "l", getOptions.LabelSelector,
+ "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
+
+ cmd.Flags().StringVarP(&getOptions.Output, common.FlagNameOutput, "o", getOptions.Output,
+ "Output format. One of: (json, yaml, name, go-template, go-template-file, template, templatefile, jsonpath, jsonpath-as-json, jsonpath-file, custom-columns, custom-columns-file, wide)")
+
+ cmd.Flags().BoolVarP(&getOptions.AllNamespaces, common.FlagNameAllNamespaces, "A", getOptions.AllNamespaces,
+ "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace")
+}
+
+func ConvertDataToTable(obj runtime.Object) (runtime.Object, error) {
+ to := metav1.TableOptions{}
+ tc := storage.TableConvertor{TableGenerator: k8sprinters.NewTableGenerator().With(printersinternal.AddHandlers)}
+
+ return tc.ConvertToTable(context.TODO(), obj, &to)
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/restart/pod.go b/keadm/cmd/keadm/app/cmd/ctl/restart/pod.go
new file mode 100644
index 000000000..cc77150d9
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/restart/pod.go
@@ -0,0 +1,121 @@
+/*
+Copyright 2024 The KubeEdge 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 restart
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/spf13/cobra"
+ oteltrace "go.opentelemetry.io/otel/trace"
+ runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
+ cmdutil "k8s.io/kubectl/pkg/cmd/util"
+ "k8s.io/kubernetes/pkg/kubelet/cri/remote"
+
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl/restful"
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/util"
+)
+
+type PodRestartOptions struct {
+ Namespace string
+}
+
+var (
+ edgePodRestartShortDescription = `Restart pods in edge node`
+)
+
+// NewEdgePodRestart returns KubeEdge delete edge pod command.
+func NewEdgePodRestart() *cobra.Command {
+ deleteOpts := NewRestartPodOpts()
+ cmd := &cobra.Command{
+ Use: "pod",
+ Short: edgePodRestartShortDescription,
+ Long: edgePodRestartShortDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ if len(args) <= 0 {
+ return fmt.Errorf("no pod specified for reboot")
+ }
+ cmdutil.CheckErr(deleteOpts.restartPod(args))
+ return nil
+ },
+ }
+ AddRestartPodFlags(cmd, deleteOpts)
+ return cmd
+}
+
+func NewRestartPodOpts() *PodRestartOptions {
+ podDeleteOptions := &PodRestartOptions{}
+ podDeleteOptions.Namespace = "default"
+ return podDeleteOptions
+}
+
+func AddRestartPodFlags(cmd *cobra.Command, RestartPodOptions *PodRestartOptions) {
+ cmd.Flags().StringVarP(&RestartPodOptions.Namespace, common.FlagNameNamespace, "n", RestartPodOptions.Namespace,
+ "Specify a namespace")
+}
+
+func (o *PodRestartOptions) restartPod(podNames []string) error {
+ for _, podName := range podNames {
+ podRequest := &restful.PodRequest{
+ Namespace: o.Namespace,
+ PodName: podName,
+ }
+ pod, err := podRequest.GetPod()
+ if err != nil {
+ fmt.Println(err.Error())
+ continue
+ }
+ config, err := util.ParseEdgecoreConfig(common.EdgecoreConfigPath)
+ if err != nil {
+ fmt.Printf("get edge config failed with err:%v\n", err)
+ continue
+ }
+ nodeName := config.Modules.Edged.HostnameOverride
+ if nodeName != pod.Spec.NodeName {
+ fmt.Printf("can't to restart pod: \"%s\" for node: \"%s\"\n", pod.Name, pod.Spec.NodeName)
+ continue
+ }
+ endpoint := config.Modules.Edged.TailoredKubeletConfig.ContainerRuntimeEndpoint
+ remoteRuntimeService, err := remote.NewRemoteRuntimeService(endpoint, time.Second*10, oteltrace.NewNoopTracerProvider())
+
+ var labelSelector = map[string]string{
+ "io.kubernetes.pod.name": pod.Name,
+ "io.kubernetes.pod.namespace": pod.Namespace,
+ }
+
+ filter := &runtimeapi.ContainerFilter{
+ LabelSelector: labelSelector,
+ }
+ containers, err := remoteRuntimeService.ListContainers(context.TODO(), filter)
+ if err != nil {
+ return err
+ }
+
+ for _, container := range containers {
+ containerID := container.Id
+ err := remoteRuntimeService.StopContainer(context.TODO(), containerID, 3)
+ if err != nil {
+ fmt.Printf("stop containerID:%s with err:%v\n", containerID, err)
+ } else {
+ fmt.Println(containerID)
+ }
+ }
+ }
+ return nil
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/restart/restart.go b/keadm/cmd/keadm/app/cmd/ctl/restart/restart.go
new file mode 100644
index 000000000..6e0df456c
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/restart/restart.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2024 The KubeEdge 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 restart
+
+import "github.com/spf13/cobra"
+
+var (
+ edgeRestartShortDescription = `Restart resources in edge node`
+)
+
+// NewEdgeRestart returns KubeEdge restart edge resources command.
+func NewEdgeRestart() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "restart",
+ Short: edgeRestartShortDescription,
+ Long: edgeRestartShortDescription,
+ }
+ cmd.AddCommand(NewEdgePodRestart())
+ return cmd
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/restful/pod.go b/keadm/cmd/keadm/app/cmd/ctl/restful/pod.go
new file mode 100644
index 000000000..6b9eabda1
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/restful/pod.go
@@ -0,0 +1,73 @@
+/*
+Copyright 2024 The KubeEdge 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 restful
+
+import (
+ "net/url"
+
+ corev1 "k8s.io/api/core/v1"
+)
+
+type PodRequest struct {
+ Namespace string
+ LabelSelector string
+ AllNamespaces bool
+ PodName string
+}
+
+func (podRequest *PodRequest) GetPod() (*corev1.Pod, error) {
+ request := Request{
+ Method: "GET",
+ Path: "/" + CoreAPIPrefix + "/" + CoreAPIGroupVersion.Version +
+ "/namespaces/" + podRequest.Namespace + "/pods/" + podRequest.PodName,
+ }
+
+ pod, err := request.ResponseToPod()
+ if err != nil {
+ return nil, err
+ }
+ return pod, nil
+}
+
+func (podRequest *PodRequest) GetPods() (*corev1.PodList, error) {
+ var request Request
+ if podRequest.AllNamespaces {
+ request = Request{
+ Method: "GET",
+ Path: "/" + CoreAPIPrefix + "/" + CoreAPIGroupVersion.Version + "/pods",
+ }
+ } else {
+ request = Request{
+ Method: "GET",
+ Path: "/" + CoreAPIPrefix + "/" + CoreAPIGroupVersion.Version +
+ "/namespaces/" + podRequest.Namespace + "/pods",
+ }
+ }
+
+ if podRequest.LabelSelector != "" {
+ values := url.Values{}
+ values.Set("labelSelector", podRequest.LabelSelector)
+ queryParams := values.Encode()
+ request.Path += "?" + queryParams
+ }
+
+ podList, err := request.ResponseToPodList()
+ if err != nil {
+ return nil, err
+ }
+ return podList, nil
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/restful/request.go b/keadm/cmd/keadm/app/cmd/ctl/restful/request.go
new file mode 100644
index 000000000..0e0f2c73b
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/restful/request.go
@@ -0,0 +1,151 @@
+/*
+Copyright 2024 The KubeEdge 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 restful
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common"
+ util2 "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/ctl/util"
+ keadutil "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/util"
+)
+
+var (
+ CoreAPIPrefix = "api"
+ CoreAPIGroupVersion = schema.GroupVersion{Group: "", Version: "v1"}
+ Prefix = "apis"
+)
+
+type Request struct {
+ Method string
+ Path string
+ Body io.Reader
+}
+
+func (req *Request) RestfulRequest() (*http.Response, error) {
+ var client http.Client
+ config, err := keadutil.ParseEdgecoreConfig(common.EdgecoreConfigPath)
+ if err != nil {
+ return nil, fmt.Errorf("get edge config failed with err:%v", err)
+ }
+ if config.Modules.MetaManager.MetaServer.Enable {
+ url := config.Modules.MetaManager.MetaServer.Server
+ ok, requireAuthorization := config.FeatureGates["requireAuthorization"]
+ if ok && requireAuthorization {
+ serverCrt := config.Modules.MetaManager.MetaServer.TLSCertFile
+ serverKey := config.Modules.MetaManager.MetaServer.TLSPrivateKeyFile
+ cert, err := tls.LoadX509KeyPair(serverCrt, serverKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load server certificate and private key with err:%v", err)
+ }
+
+ tlsCaFile := config.Modules.MetaManager.MetaServer.TLSCaFile
+ caCert, err := os.ReadFile(tlsCaFile)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load tlsCaFile with err:%v", err)
+ }
+
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ //ClientAuth: tls.RequireAndVerifyClientCert,
+ RootCAs: caCertPool,
+ }
+
+ url = "https://" + url
+ client = http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: tlsConfig,
+ },
+ }
+ } else {
+ url = "http://" + url
+ client = http.Client{}
+ }
+
+ request, err := http.NewRequest(req.Method, url+req.Path, req.Body)
+ if err != nil {
+ return nil, fmt.Errorf("restful format failed with err:%v", err)
+ }
+ response, err := client.Do(request)
+ if err != nil {
+ return nil, fmt.Errorf("restful failed with err:%v", err)
+ }
+
+ return response, nil
+ }
+ return nil, fmt.Errorf("metaserver don't open")
+}
+
+func (req *Request) ResponseToPodList() (*corev1.PodList, error) {
+ response, err := req.RestfulRequest()
+ if err != nil {
+ return nil, err
+ }
+
+ bodyBytes, err := io.ReadAll(response.Body)
+ if err != nil {
+ return nil, fmt.Errorf("read response's body failed with err:%v", err)
+ }
+
+ if response.StatusCode != http.StatusOK {
+ return nil, util2.GetErrMessage(bodyBytes)
+ }
+
+ var podList *corev1.PodList
+ err = json.Unmarshal(bodyBytes, &podList)
+ if err != nil {
+ return nil, fmt.Errorf("parsing response's body failed with err:%v", err)
+ }
+
+ return podList, err
+}
+
+func (req *Request) ResponseToPod() (*corev1.Pod, error) {
+ response, err := req.RestfulRequest()
+ if err != nil {
+ return nil, err
+ }
+
+ bodyBytes, err := io.ReadAll(response.Body)
+ if err != nil {
+ return nil, fmt.Errorf("read response's body failed with err:%v", err)
+ }
+
+ if response.StatusCode != http.StatusOK {
+ return nil, util2.GetErrMessage(bodyBytes)
+ }
+
+ var pod *corev1.Pod
+ err = json.Unmarshal(bodyBytes, &pod)
+ if err != nil {
+ return nil, fmt.Errorf("parsing response's body failed with err:%v", err)
+ }
+
+ return pod, err
+}
diff --git a/keadm/cmd/keadm/app/cmd/ctl/util/pod.go b/keadm/cmd/keadm/app/cmd/ctl/util/pod.go
new file mode 100644
index 000000000..015a8ca6b
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/ctl/util/pod.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2024 The KubeEdge 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 (
+ "encoding/json"
+ "errors"
+ "fmt"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func GetErrMessage(bodyBytes []byte) error {
+ var status metav1.Status
+ err := json.Unmarshal(bodyBytes, &status)
+ if err != nil {
+ return fmt.Errorf("parsing response's body failed with err:%v", err)
+ }
+ return errors.New(status.Message)
+}
diff --git a/vendor/github.com/fvbommel/sortorder/.gitignore b/vendor/github.com/fvbommel/sortorder/.gitignore
new file mode 100644
index 000000000..c021733e2
--- /dev/null
+++ b/vendor/github.com/fvbommel/sortorder/.gitignore
@@ -0,0 +1,19 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+# Folders
+_obj
+_test
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+_testmain.go
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/fvbommel/sortorder/LICENSE b/vendor/github.com/fvbommel/sortorder/LICENSE
new file mode 100644
index 000000000..5c695fb59
--- /dev/null
+++ b/vendor/github.com/fvbommel/sortorder/LICENSE
@@ -0,0 +1,17 @@
+The MIT License (MIT)
+Copyright (c) 2015 Frits van Bommel
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/fvbommel/sortorder/README.md b/vendor/github.com/fvbommel/sortorder/README.md
new file mode 100644
index 000000000..06779c885
--- /dev/null
+++ b/vendor/github.com/fvbommel/sortorder/README.md
@@ -0,0 +1,9 @@
+# sortorder [![PkgGoDev](https://pkg.go.dev/badge/github.com/fvbommel/sortorder)](https://pkg.go.dev/github.com/fvbommel/sortorder)
+
+ import "github.com/fvbommel/sortorder"
+
+Sort orders and comparison functions.
+
+Case-insensitive sort orders are in the `casefolded` sub-package
+because it pulls in the Unicode tables in the standard library,
+which can add significantly to the size of binaries.
diff --git a/vendor/github.com/fvbommel/sortorder/doc.go b/vendor/github.com/fvbommel/sortorder/doc.go
new file mode 100644
index 000000000..a7dd9585d
--- /dev/null
+++ b/vendor/github.com/fvbommel/sortorder/doc.go
@@ -0,0 +1,5 @@
+// Package sortorder implements sort orders and comparison functions.
+//
+// Currently, it only implements so-called "natural order", where integers
+// embedded in strings are compared by value.
+package sortorder // import "github.com/fvbommel/sortorder"
diff --git a/vendor/github.com/fvbommel/sortorder/natsort.go b/vendor/github.com/fvbommel/sortorder/natsort.go
new file mode 100644
index 000000000..e4f15110b
--- /dev/null
+++ b/vendor/github.com/fvbommel/sortorder/natsort.go
@@ -0,0 +1,76 @@
+package sortorder
+
+// Natural implements sort.Interface to sort strings in natural order. This
+// means that e.g. "abc2" < "abc12".
+//
+// Non-digit sequences and numbers are compared separately. The former are
+// compared bytewise, while digits are compared numerically (except that
+// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
+//
+// Limitation: only ASCII digits (0-9) are considered.
+type Natural []string
+
+func (n Natural) Len() int { return len(n) }
+func (n Natural) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
+func (n Natural) Less(i, j int) bool { return NaturalLess(n[i], n[j]) }
+
+func isDigit(b byte) bool { return '0' <= b && b <= '9' }
+
+// NaturalLess compares two strings using natural ordering. This means that e.g.
+// "abc2" < "abc12".
+//
+// Non-digit sequences and numbers are compared separately. The former are
+// compared bytewise, while digits are compared numerically (except that
+// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
+//
+// Limitation: only ASCII digits (0-9) are considered.
+func NaturalLess(str1, str2 string) bool {
+ idx1, idx2 := 0, 0
+ for idx1 < len(str1) && idx2 < len(str2) {
+ c1, c2 := str1[idx1], str2[idx2]
+ dig1, dig2 := isDigit(c1), isDigit(c2)
+ switch {
+ case dig1 != dig2: // Digits before other characters.
+ return dig1 // True if LHS is a digit, false if the RHS is one.
+ case !dig1: // && !dig2, because dig1 == dig2
+ // UTF-8 compares bytewise-lexicographically, no need to decode
+ // codepoints.
+ if c1 != c2 {
+ return c1 < c2
+ }
+ idx1++
+ idx2++
+ default: // Digits
+ // Eat zeros.
+ for ; idx1 < len(str1) && str1[idx1] == '0'; idx1++ {
+ }
+ for ; idx2 < len(str2) && str2[idx2] == '0'; idx2++ {
+ }
+ // Eat all digits.
+ nonZero1, nonZero2 := idx1, idx2
+ for ; idx1 < len(str1) && isDigit(str1[idx1]); idx1++ {
+ }
+ for ; idx2 < len(str2) && isDigit(str2[idx2]); idx2++ {
+ }
+ // If lengths of numbers with non-zero prefix differ, the shorter
+ // one is less.
+ if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 {
+ return len1 < len2
+ }
+ // If they're equally long, string comparison is correct.
+ if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 {
+ return nr1 < nr2
+ }
+ // Otherwise, the one with less zeros is less.
+ // Because everything up to the number is equal, comparing the index
+ // after the zeros is sufficient.
+ if nonZero1 != nonZero2 {
+ return nonZero1 < nonZero2
+ }
+ }
+ // They're identical so far, so continue comparing.
+ }
+ // So far they are identical. At least one is ended. If the other continues,
+ // it sorts last.
+ return len(str1) < len(str2)
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn.go b/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn.go
new file mode 100644
index 000000000..38024cfa5
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn.go
@@ -0,0 +1,262 @@
+/*
+Copyright 2014 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 get
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "regexp"
+ "strings"
+
+ "github.com/liggitt/tabwriter"
+
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/client-go/util/jsonpath"
+)
+
+var jsonRegexp = regexp.MustCompile(`^\{\.?([^{}]+)\}$|^\.?([^{}]+)$`)
+
+// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
+// - metadata.name (no leading '.' or curly braces '{...}'
+// - {metadata.name} (no leading '.')
+// - .metadata.name (no curly braces '{...}')
+// - {.metadata.name} (complete expression)
+//
+// And transforms them all into a valid jsonpath expression:
+//
+// {.metadata.name}
+func RelaxedJSONPathExpression(pathExpression string) (string, error) {
+ if len(pathExpression) == 0 {
+ return pathExpression, nil
+ }
+ submatches := jsonRegexp.FindStringSubmatch(pathExpression)
+ if submatches == nil {
+ return "", fmt.Errorf("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'")
+ }
+ if len(submatches) != 3 {
+ return "", fmt.Errorf("unexpected submatch list: %v", submatches)
+ }
+ var fieldSpec string
+ if len(submatches[1]) != 0 {
+ fieldSpec = submatches[1]
+ } else {
+ fieldSpec = submatches[2]
+ }
+ return fmt.Sprintf("{.%s}", fieldSpec), nil
+}
+
+// NewCustomColumnsPrinterFromSpec creates a custom columns printer from a comma separated list of <header>:<jsonpath-field-spec> pairs.
+// e.g. NAME:metadata.name,API_VERSION:apiVersion creates a printer that prints:
+//
+// NAME API_VERSION
+// foo bar
+func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder, noHeaders bool) (*CustomColumnsPrinter, error) {
+ if len(spec) == 0 {
+ return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
+ }
+ parts := strings.Split(spec, ",")
+ columns := make([]Column, len(parts))
+ for ix := range parts {
+ colSpec := strings.SplitN(parts[ix], ":", 2)
+ if len(colSpec) != 2 {
+ return nil, fmt.Errorf("unexpected custom-columns spec: %s, expected <header>:<json-path-expr>", parts[ix])
+ }
+ spec, err := RelaxedJSONPathExpression(colSpec[1])
+ if err != nil {
+ return nil, err
+ }
+ columns[ix] = Column{Header: colSpec[0], FieldSpec: spec}
+ }
+ return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: noHeaders}, nil
+}
+
+func splitOnWhitespace(line string) []string {
+ lineScanner := bufio.NewScanner(bytes.NewBufferString(line))
+ lineScanner.Split(bufio.ScanWords)
+ result := []string{}
+ for lineScanner.Scan() {
+ result = append(result, lineScanner.Text())
+ }
+ return result
+}
+
+// NewCustomColumnsPrinterFromTemplate creates a custom columns printer from a template stream. The template is expected
+// to consist of two lines, whitespace separated. The first line is the header line, the second line is the jsonpath field spec
+// For example, the template below:
+// NAME API_VERSION
+// {metadata.name} {apiVersion}
+func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
+ scanner := bufio.NewScanner(templateReader)
+ if !scanner.Scan() {
+ return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.")
+ }
+ headers := splitOnWhitespace(scanner.Text())
+
+ if !scanner.Scan() {
+ return nil, fmt.Errorf("invalid template, missing spec line. Expected format is one line of space separated headers, one line of space separated column specs.")
+ }
+ specs := splitOnWhitespace(scanner.Text())
+
+ if len(headers) != len(specs) {
+ return nil, fmt.Errorf("number of headers (%d) and field specifications (%d) don't match", len(headers), len(specs))
+ }
+
+ columns := make([]Column, len(headers))
+ for ix := range headers {
+ spec, err := RelaxedJSONPathExpression(specs[ix])
+ if err != nil {
+ return nil, err
+ }
+ columns[ix] = Column{
+ Header: headers[ix],
+ FieldSpec: spec,
+ }
+ }
+ return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: false}, nil
+}
+
+// Column represents a user specified column
+type Column struct {
+ // The header to print above the column, general style is ALL_CAPS
+ Header string
+ // The pointer to the field in the object to print in JSONPath form
+ // e.g. {.ObjectMeta.Name}, see pkg/util/jsonpath for more details.
+ FieldSpec string
+}
+
+// CustomColumnPrinter is a printer that knows how to print arbitrary columns
+// of data from templates specified in the `Columns` array
+type CustomColumnsPrinter struct {
+ Columns []Column
+ Decoder runtime.Decoder
+ NoHeaders bool
+ // lastType records type of resource printed last so that we don't repeat
+ // header while printing same type of resources.
+ lastType reflect.Type
+}
+
+func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
+ // we use reflect.Indirect here in order to obtain the actual value from a pointer.
+ // we need an actual value in order to retrieve the package path for an object.
+ // using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
+ if printers.InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
+ return fmt.Errorf(printers.InternalObjectPrinterErr)
+ }
+
+ if _, found := out.(*tabwriter.Writer); !found {
+ w := printers.GetNewTabWriter(out)
+ out = w
+ defer w.Flush()
+ }
+
+ t := reflect.TypeOf(obj)
+ if !s.NoHeaders && t != s.lastType {
+ headers := make([]string, len(s.Columns))
+ for ix := range s.Columns {
+ headers[ix] = s.Columns[ix].Header
+ }
+ fmt.Fprintln(out, strings.Join(headers, "\t"))
+ s.lastType = t
+ }
+ parsers := make([]*jsonpath.JSONPath, len(s.Columns))
+ for ix := range s.Columns {
+ parsers[ix] = jsonpath.New(fmt.Sprintf("column%d", ix)).AllowMissingKeys(true)
+ if err := parsers[ix].Parse(s.Columns[ix].FieldSpec); err != nil {
+ return err
+ }
+ }
+
+ if meta.IsListType(obj) {
+ objs, err := meta.ExtractList(obj)
+ if err != nil {
+ return err
+ }
+ for ix := range objs {
+ if err := s.printOneObject(objs[ix], parsers, out); err != nil {
+ return err
+ }
+ }
+ } else {
+ if err := s.printOneObject(obj, parsers, out); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {
+ columns := make([]string, len(parsers))
+ switch u := obj.(type) {
+ case *metav1.WatchEvent:
+ if printers.InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(u.Object.Object)).Type().PkgPath()) {
+ return fmt.Errorf(printers.InternalObjectPrinterErr)
+ }
+ unstructuredObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(u.Object.Object)
+ if err != nil {
+ return err
+ }
+ obj = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "type": u.Type,
+ "object": unstructuredObject,
+ },
+ }
+
+ case *runtime.Unknown:
+ if len(u.Raw) > 0 {
+ var err error
+ if obj, err = runtime.Decode(s.Decoder, u.Raw); err != nil {
+ return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.Raw)
+ }
+ }
+ }
+
+ for ix := range parsers {
+ parser := parsers[ix]
+
+ var values [][]reflect.Value
+ var err error
+ if unstructured, ok := obj.(runtime.Unstructured); ok {
+ values, err = parser.FindResults(unstructured.UnstructuredContent())
+ } else {
+ values, err = parser.FindResults(reflect.ValueOf(obj).Elem().Interface())
+ }
+
+ if err != nil {
+ return err
+ }
+ valueStrings := []string{}
+ if len(values) == 0 || len(values[0]) == 0 {
+ valueStrings = append(valueStrings, "<none>")
+ }
+ for arrIx := range values {
+ for valIx := range values[arrIx] {
+ valueStrings = append(valueStrings, printers.EscapeTerminal(fmt.Sprint(values[arrIx][valIx].Interface())))
+ }
+ }
+ columns[ix] = strings.Join(valueStrings, ",")
+ }
+ fmt.Fprintln(out, strings.Join(columns, "\t"))
+ return nil
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn_flags.go b/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn_flags.go
new file mode 100644
index 000000000..efd7fcf75
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/customcolumn_flags.go
@@ -0,0 +1,113 @@
+/*
+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 get
+
+import (
+ "fmt"
+ "os"
+ "sort"
+ "strings"
+
+ "github.com/spf13/cobra"
+
+ "k8s.io/cli-runtime/pkg/genericclioptions"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/kubectl/pkg/scheme"
+)
+
+var columnsFormats = map[string]bool{
+ "custom-columns-file": true,
+ "custom-columns": true,
+}
+
+// CustomColumnsPrintFlags provides default flags necessary for printing
+// custom resource columns from an inline-template or file.
+type CustomColumnsPrintFlags struct {
+ NoHeaders bool
+ TemplateArgument string
+}
+
+func (f *CustomColumnsPrintFlags) AllowedFormats() []string {
+ formats := make([]string, 0, len(columnsFormats))
+ for format := range columnsFormats {
+ formats = append(formats, format)
+ }
+ sort.Strings(formats)
+ return formats
+}
+
+// ToPrinter receives an templateFormat and returns a printer capable of
+// handling custom-column printing.
+// Returns false if the specified templateFormat does not match a supported format.
+// Supported format types can be found in pkg/printers/printers.go
+func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (printers.ResourcePrinter, error) {
+ if len(templateFormat) == 0 {
+ return nil, genericclioptions.NoCompatiblePrinterError{}
+ }
+
+ templateValue := ""
+
+ if len(f.TemplateArgument) == 0 {
+ for format := range columnsFormats {
+ format = format + "="
+ if strings.HasPrefix(templateFormat, format) {
+ templateValue = templateFormat[len(format):]
+ templateFormat = format[:len(format)-1]
+ break
+ }
+ }
+ } else {
+ templateValue = f.TemplateArgument
+ }
+
+ if _, supportedFormat := columnsFormats[templateFormat]; !supportedFormat {
+ return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()}
+ }
+
+ if len(templateValue) == 0 {
+ return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
+ }
+
+ // UniversalDecoder call must specify parameter versions; otherwise it will decode to internal versions.
+ decoder := scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
+
+ if templateFormat == "custom-columns-file" {
+ file, err := os.Open(templateValue)
+ if err != nil {
+ return nil, fmt.Errorf("error reading template %s, %v\n", templateValue, err)
+ }
+ defer file.Close()
+ p, err := NewCustomColumnsPrinterFromTemplate(file, decoder)
+ return p, err
+ }
+
+ return NewCustomColumnsPrinterFromSpec(templateValue, decoder, f.NoHeaders)
+}
+
+// AddFlags receives a *cobra.Command reference and binds
+// flags related to custom-columns printing
+func (f *CustomColumnsPrintFlags) AddFlags(c *cobra.Command) {}
+
+// NewCustomColumnsPrintFlags returns flags associated with
+// custom-column printing, with default values set.
+// NoHeaders and TemplateArgument should be set by callers.
+func NewCustomColumnsPrintFlags() *CustomColumnsPrintFlags {
+ return &CustomColumnsPrintFlags{
+ NoHeaders: false,
+ TemplateArgument: "",
+ }
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/get.go b/vendor/k8s.io/kubectl/pkg/cmd/get/get.go
new file mode 100644
index 000000000..f16586994
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/get.go
@@ -0,0 +1,821 @@
+/*
+Copyright 2014 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 get
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/url"
+ "strings"
+
+ "github.com/spf13/cobra"
+
+ corev1 "k8s.io/api/core/v1"
+ 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/apis/meta/v1/unstructured"
+ metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
+ "k8s.io/apimachinery/pkg/runtime"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/cli-runtime/pkg/genericiooptions"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/cli-runtime/pkg/resource"
+ kubernetesscheme "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ watchtools "k8s.io/client-go/tools/watch"
+ cmdutil "k8s.io/kubectl/pkg/cmd/util"
+ "k8s.io/kubectl/pkg/rawhttp"
+ "k8s.io/kubectl/pkg/scheme"
+ "k8s.io/kubectl/pkg/util/i18n"
+ "k8s.io/kubectl/pkg/util/interrupt"
+ "k8s.io/kubectl/pkg/util/slice"
+ "k8s.io/kubectl/pkg/util/templates"
+ utilpointer "k8s.io/utils/pointer"
+)
+
+// GetOptions contains the input to the get command.
+type GetOptions struct {
+ PrintFlags *PrintFlags
+ ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
+ IsHumanReadablePrinter bool
+
+ CmdParent string
+
+ resource.FilenameOptions
+
+ Raw string
+ Watch bool
+ WatchOnly bool
+ ChunkSize int64
+
+ OutputWatchEvents bool
+
+ LabelSelector string
+ FieldSelector string
+ AllNamespaces bool
+ Namespace string
+ ExplicitNamespace bool
+ Subresource string
+ SortBy string
+
+ ServerPrint bool
+
+ NoHeaders bool
+ IgnoreNotFound bool
+
+ genericiooptions.IOStreams
+}
+
+var (
+ getLong = templates.LongDesc(i18n.T(`
+ Display one or many resources.
+
+ Prints a table of the most important information about the specified resources.
+ You can filter the list using a label selector and the --selector flag. If the
+ desired resource type is namespaced you will only see results in your current
+ namespace unless you pass --all-namespaces.
+
+ By specifying the output as 'template' and providing a Go template as the value
+ of the --template flag, you can filter the attributes of the fetched resources.`))
+
+ getExample = templates.Examples(i18n.T(`
+ # List all pods in ps output format
+ kubectl get pods
+
+ # List all pods in ps output format with more information (such as node name)
+ kubectl get pods -o wide
+
+ # List a single replication controller with specified NAME in ps output format
+ kubectl get replicationcontroller web
+
+ # List deployments in JSON output format, in the "v1" version of the "apps" API group
+ kubectl get deployments.v1.apps -o json
+
+ # List a single pod in JSON output format
+ kubectl get -o json pod web-pod-13je7
+
+ # List a pod identified by type and name specified in "pod.yaml" in JSON output format
+ kubectl get -f pod.yaml -o json
+
+ # List resources from a directory with kustomization.yaml - e.g. dir/kustomization.yaml
+ kubectl get -k dir/
+
+ # Return only the phase value of the specified pod
+ kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}
+
+ # List resource information in custom columns
+ kubectl get pod test-pod -o custom-columns=CONTAINER:.spec.containers[0].name,IMAGE:.spec.containers[0].image
+
+ # List all replication controllers and services together in ps output format
+ kubectl get rc,services
+
+ # List one or more resources by their type and names
+ kubectl get rc/web service/frontend pods/web-pod-13je7
+
+ # List the 'status' subresource for a single pod
+ kubectl get pod web-pod-13je7 --subresource status`))
+)
+
+const (
+ useServerPrintColumns = "server-print"
+)
+
+var supportedSubresources = []string{"status", "scale"}
+
+// NewGetOptions returns a GetOptions with default chunk size 500.
+func NewGetOptions(parent string, streams genericiooptions.IOStreams) *GetOptions {
+ return &GetOptions{
+ PrintFlags: NewGetPrintFlags(),
+ CmdParent: parent,
+
+ IOStreams: streams,
+ ChunkSize: cmdutil.DefaultChunkSize,
+ ServerPrint: true,
+ }
+}
+
+// NewCmdGet creates a command object for the generic "get" action, which
+// retrieves one or more resources from a server.
+func NewCmdGet(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
+ o := NewGetOptions(parent, streams)
+
+ cmd := &cobra.Command{
+ Use: fmt.Sprintf("get [(-o|--output=)%s] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]", strings.Join(o.PrintFlags.AllowedFormats(), "|")),
+ DisableFlagsInUseLine: true,
+ Short: i18n.T("Display one or many resources"),
+ Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
+ Example: getExample,
+ // ValidArgsFunction is set when this function is called so that we have access to the util package
+ Run: func(cmd *cobra.Command, args []string) {
+ cmdutil.CheckErr(o.Complete(f, cmd, args))
+ cmdutil.CheckErr(o.Validate())
+ cmdutil.CheckErr(o.Run(f, args))
+ },
+ SuggestFor: []string{"list", "ps"},
+ }
+
+ o.PrintFlags.AddFlags(cmd)
+
+ cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
+ cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes.")
+ cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
+ cmd.Flags().BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.")
+ cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
+ cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
+ cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
+ addServerPrintColumnFlags(cmd, o)
+ cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to get from a server.")
+ cmdutil.AddChunkSizeFlag(cmd, &o.ChunkSize)
+ cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
+ cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, gets the subresource of the requested object.", supportedSubresources...)
+ return cmd
+}
+
+// Complete takes the command arguments and factory and infers any remaining options.
+func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
+ if len(o.Raw) > 0 {
+ if len(args) > 0 {
+ return fmt.Errorf("arguments may not be passed when --raw is specified")
+ }
+ return nil
+ }
+
+ var err error
+ o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace()
+ if err != nil {
+ return err
+ }
+ if o.AllNamespaces {
+ o.ExplicitNamespace = false
+ }
+
+ if o.PrintFlags.HumanReadableFlags.SortBy != nil {
+ o.SortBy = *o.PrintFlags.HumanReadableFlags.SortBy
+ }
+
+ o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
+
+ // TODO (soltysh): currently we don't support custom columns
+ // with server side print. So in these cases force the old behavior.
+ outputOption := cmd.Flags().Lookup("output").Value.String()
+ if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
+ o.ServerPrint = false
+ }
+
+ templateArg := ""
+ if o.PrintFlags.TemplateFlags != nil && o.PrintFlags.TemplateFlags.TemplateArgument != nil {
+ templateArg = *o.PrintFlags.TemplateFlags.TemplateArgument
+ }
+
+ // human readable printers have special conversion rules, so we determine if we're using one.
+ if (len(*o.PrintFlags.OutputFormat) == 0 && len(templateArg) == 0) || *o.PrintFlags.OutputFormat == "wide" {
+ o.IsHumanReadablePrinter = true
+ }
+
+ o.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
+ // make a new copy of current flags / opts before mutating
+ printFlags := o.PrintFlags.Copy()
+
+ if mapping != nil {
+ printFlags.SetKind(mapping.GroupVersionKind.GroupKind())
+ }
+ if withNamespace {
+ printFlags.EnsureWithNamespace()
+ }
+ if withKind {
+ printFlags.EnsureWithKind()
+ }
+
+ printer, err := printFlags.ToPrinter()
+ if err != nil {
+ return nil, err
+ }
+ printer, err = printers.NewTypeSetter(scheme.Scheme).WrapToPrinter(printer, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(o.SortBy) > 0 {
+ printer = &SortingPrinter{Delegate: printer, SortField: o.SortBy}
+ }
+ if outputObjects != nil {
+ printer = &skipPrinter{delegate: printer, output: outputObjects}
+ }
+ if o.ServerPrint {
+ printer = &TablePrinter{Delegate: printer}
+ }
+ return printer.PrintObj, nil
+ }
+
+ switch {
+ case o.Watch || o.WatchOnly:
+ if len(o.SortBy) > 0 {
+ fmt.Fprintf(o.IOStreams.ErrOut, "warning: --watch or --watch-only requested, --sort-by will be ignored\n")
+ }
+ default:
+ if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
+ fmt.Fprintf(o.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.SuggestAPIResources(o.CmdParent))
+ fullCmdName := cmd.Parent().CommandPath()
+ usageString := "Required resource not specified."
+ if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
+ usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
+ }
+
+ return cmdutil.UsageErrorf(cmd, usageString)
+ }
+ }
+
+ return nil
+}
+
+// Validate checks the set of flags provided by the user.
+func (o *GetOptions) Validate() error {
+ if len(o.Raw) > 0 {
+ if o.Watch || o.WatchOnly || len(o.LabelSelector) > 0 {
+ return fmt.Errorf("--raw may not be specified with other flags that filter the server request or alter the output")
+ }
+ if o.PrintFlags.OutputFormat != nil && len(*o.PrintFlags.OutputFormat) > 0 {
+ return fmt.Errorf("--raw and --output are mutually exclusive")
+ }
+ if _, err := url.ParseRequestURI(o.Raw); err != nil {
+ return fmt.Errorf("--raw must be a valid URL path: %v", err)
+ }
+ }
+ if o.PrintFlags.HumanReadableFlags.ShowLabels != nil && *o.PrintFlags.HumanReadableFlags.ShowLabels && o.PrintFlags.OutputFormat != nil {
+ outputOption := *o.PrintFlags.OutputFormat
+ if outputOption != "" && outputOption != "wide" {
+ return fmt.Errorf("--show-labels option cannot be used with %s printer", outputOption)
+ }
+ }
+ if o.OutputWatchEvents && !(o.Watch || o.WatchOnly) {
+ return fmt.Errorf("--output-watch-events option can only be used with --watch or --watch-only")
+ }
+ if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) {
+ return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources)
+ }
+ return nil
+}
+
+// OriginalPositioner and NopPositioner is required for swap/sort operations of data in table format
+type OriginalPositioner interface {
+ OriginalPosition(int) int
+}
+
+// NopPositioner and OriginalPositioner is required for swap/sort operations of data in table format
+type NopPositioner struct{}
+
+// OriginalPosition returns the original position from NopPositioner object
+func (t *NopPositioner) OriginalPosition(ix int) int {
+ return ix
+}
+
+// RuntimeSorter holds the required objects to perform sorting of runtime objects
+type RuntimeSorter struct {
+ field string
+ decoder runtime.Decoder
+ objects []runtime.Object
+ positioner OriginalPositioner
+}
+
+// Sort performs the sorting of runtime objects
+func (r *RuntimeSorter) Sort() error {
+ // a list is only considered "sorted" if there are 0 or 1 items in it
+ // AND (if 1 item) the item is not a Table object
+ if len(r.objects) == 0 {
+ return nil
+ }
+ if len(r.objects) == 1 {
+ _, isTable := r.objects[0].(*metav1.Table)
+ if !isTable {
+ return nil
+ }
+ }
+
+ includesTable := false
+ includesRuntimeObjs := false
+
+ for _, obj := range r.objects {
+ switch t := obj.(type) {
+ case *metav1.Table:
+ includesTable = true
+
+ if sorter, err := NewTableSorter(t, r.field); err != nil {
+ return err
+ } else if err := sorter.Sort(); err != nil {
+ return err
+ }
+ default:
+ includesRuntimeObjs = true
+ }
+ }
+
+ // we use a NopPositioner when dealing with Table objects
+ // because the objects themselves are not swapped, but rather
+ // the rows in each object are swapped / sorted.
+ r.positioner = &NopPositioner{}
+
+ if includesRuntimeObjs && includesTable {
+ return fmt.Errorf("sorting is not supported on mixed Table and non-Table object lists")
+ }
+ if includesTable {
+ return nil
+ }
+
+ // if not dealing with a Table response from the server, assume
+ // all objects are runtime.Object as usual, and sort using old method.
+ var err error
+ if r.positioner, err = SortObjects(r.decoder, r.objects, r.field); err != nil {
+ return err
+ }
+ return nil
+}
+
+// OriginalPosition returns the original position of a runtime object
+func (r *RuntimeSorter) OriginalPosition(ix int) int {
+ if r.positioner == nil {
+ return 0
+ }
+ return r.positioner.OriginalPosition(ix)
+}
+
+// WithDecoder allows custom decoder to be set for testing
+func (r *RuntimeSorter) WithDecoder(decoder runtime.Decoder) *RuntimeSorter {
+ r.decoder = decoder
+ return r
+}
+
+// NewRuntimeSorter returns a new instance of RuntimeSorter
+func NewRuntimeSorter(objects []runtime.Object, sortBy string) *RuntimeSorter {
+ parsedField, err := RelaxedJSONPathExpression(sortBy)
+ if err != nil {
+ parsedField = sortBy
+ }
+
+ return &RuntimeSorter{
+ field: parsedField,
+ decoder: kubernetesscheme.Codecs.UniversalDecoder(),
+ objects: objects,
+ }
+}
+
+func (o *GetOptions) transformRequests(req *rest.Request) {
+ if !o.ServerPrint || !o.IsHumanReadablePrinter {
+ return
+ }
+
+ req.SetHeader("Accept", strings.Join([]string{
+ fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName),
+ fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName),
+ "application/json",
+ }, ","))
+
+ // if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
+ if len(o.SortBy) > 0 {
+ req.Param("includeObject", "Object")
+ }
+}
+
+// Run performs the get operation.
+// TODO: remove the need to pass these arguments, like other commands.
+func (o *GetOptions) Run(f cmdutil.Factory, args []string) error {
+ if len(o.Raw) > 0 {
+ restClient, err := f.RESTClient()
+ if err != nil {
+ return err
+ }
+ return rawhttp.RawGet(restClient, o.IOStreams, o.Raw)
+ }
+ if o.Watch || o.WatchOnly {
+ return o.watch(f, args)
+ }
+
+ chunkSize := o.ChunkSize
+ if len(o.SortBy) > 0 {
+ // TODO(juanvallejo): in the future, we could have the client use chunking
+ // to gather all results, then sort them all at the end to reduce server load.
+ chunkSize = 0
+ }
+
+ r := f.NewBuilder().
+ Unstructured().
+ NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
+ FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
+ LabelSelectorParam(o.LabelSelector).
+ FieldSelectorParam(o.FieldSelector).
+ Subresource(o.Subresource).
+ RequestChunksOf(chunkSize).
+ ResourceTypeOrNameArgs(true, args...).
+ ContinueOnError().
+ Latest().
+ Flatten().
+ TransformRequests(o.transformRequests).
+ Do()
+
+ if o.IgnoreNotFound {
+ r.IgnoreErrors(apierrors.IsNotFound)
+ }
+ if err := r.Err(); err != nil {
+ return err
+ }
+
+ if !o.IsHumanReadablePrinter {
+ return o.printGeneric(r)
+ }
+
+ allErrs := []error{}
+ errs := sets.NewString()
+ infos, err := r.Infos()
+ if err != nil {
+ allErrs = append(allErrs, err)
+ }
+ printWithKind := multipleGVKsRequested(infos)
+
+ objs := make([]runtime.Object, len(infos))
+ for ix := range infos {
+ objs[ix] = infos[ix].Object
+ }
+
+ var positioner OriginalPositioner
+ if len(o.SortBy) > 0 {
+ sorter := NewRuntimeSorter(objs, o.SortBy)
+ if err := sorter.Sort(); err != nil {
+ return err
+ }
+ positioner = sorter
+ }
+
+ var printer printers.ResourcePrinter
+ var lastMapping *meta.RESTMapping
+
+ // track if we write any output
+ trackingWriter := &trackingWriterWrapper{Delegate: o.Out}
+ // output an empty line separating output
+ separatorWriter := &separatorWriterWrapper{Delegate: trackingWriter}
+
+ w := printers.GetNewTabWriter(separatorWriter)
+ allResourcesNamespaced := !o.AllNamespaces
+ for ix := range objs {
+ var mapping *meta.RESTMapping
+ var info *resource.Info
+ if positioner != nil {
+ info = infos[positioner.OriginalPosition(ix)]
+ mapping = info.Mapping
+ } else {
+ info = infos[ix]
+ mapping = info.Mapping
+ }
+
+ allResourcesNamespaced = allResourcesNamespaced && info.Namespaced()
+ printWithNamespace := o.AllNamespaces
+
+ if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
+ printWithNamespace = false
+ }
+
+ if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
+ w.Flush()
+ w.SetRememberedWidths(nil)
+
+ // add linebreaks between resource groups (if there is more than one)
+ // when it satisfies all following 3 conditions:
+ // 1) it's not the first resource group
+ // 2) it has row header
+ // 3) we've written output since the last time we started a new set of headers
+ if lastMapping != nil && !o.NoHeaders && trackingWriter.Written > 0 {
+ separatorWriter.SetReady(true)
+ }
+
+ printer, err = o.ToPrinter(mapping, nil, printWithNamespace, printWithKind)
+ if err != nil {
+ if !errs.Has(err.Error()) {
+ errs.Insert(err.Error())
+ allErrs = append(allErrs, err)
+ }
+ continue
+ }
+
+ lastMapping = mapping
+ }
+
+ printer.PrintObj(info.Object, w)
+ }
+ w.Flush()
+ if trackingWriter.Written == 0 && !o.IgnoreNotFound && len(allErrs) == 0 {
+ // if we wrote no output, and had no errors, and are not ignoring NotFound, be sure we output something
+ if allResourcesNamespaced {
+ fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
+ } else {
+ fmt.Fprintln(o.ErrOut, "No resources found")
+ }
+ }
+ return utilerrors.NewAggregate(allErrs)
+}
+
+type trackingWriterWrapper struct {
+ Delegate io.Writer
+ Written int
+}
+
+func (t *trackingWriterWrapper) Write(p []byte) (n int, err error) {
+ t.Written += len(p)
+ return t.Delegate.Write(p)
+}
+
+type separatorWriterWrapper struct {
+ Delegate io.Writer
+ Ready bool
+}
+
+func (s *separatorWriterWrapper) Write(p []byte) (n int, err error) {
+ // If we're about to write non-empty bytes and `s` is ready,
+ // we prepend an empty line to `p` and reset `s.Read`.
+ if len(p) != 0 && s.Ready {
+ fmt.Fprintln(s.Delegate)
+ s.Ready = false
+ }
+ return s.Delegate.Write(p)
+}
+
+func (s *separatorWriterWrapper) SetReady(state bool) {
+ s.Ready = state
+}
+
+// watch starts a client-side watch of one or more resources.
+// TODO: remove the need for arguments here.
+func (o *GetOptions) watch(f cmdutil.Factory, args []string) error {
+ r := f.NewBuilder().
+ Unstructured().
+ NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
+ FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
+ LabelSelectorParam(o.LabelSelector).
+ FieldSelectorParam(o.FieldSelector).
+ RequestChunksOf(o.ChunkSize).
+ ResourceTypeOrNameArgs(true, args...).
+ SingleResourceType().
+ Latest().
+ TransformRequests(o.transformRequests).
+ Do()
+ if err := r.Err(); err != nil {
+ return err
+ }
+ infos, err := r.Infos()
+ if err != nil {
+ return err
+ }
+ if multipleGVKsRequested(infos) {
+ return i18n.Errorf("watch is only supported on individual resources and resource collections - more than 1 resource was found")
+ }
+
+ info := infos[0]
+ mapping := info.ResourceMapping()
+ outputObjects := utilpointer.BoolPtr(!o.WatchOnly)
+ printer, err := o.ToPrinter(mapping, outputObjects, o.AllNamespaces, false)
+ if err != nil {
+ return err
+ }
+ obj, err := r.Object()
+ if err != nil {
+ return err
+ }
+
+ // watching from resourceVersion 0, starts the watch at ~now and
+ // will return an initial watch event. Starting form ~now, rather
+ // the rv of the object will insure that we start the watch from
+ // inside the watch window, which the rv of the object might not be.
+ rv := "0"
+ isList := meta.IsListType(obj)
+ if isList {
+ // the resourceVersion of list objects is ~now but won't return
+ // an initial watch event
+ rv, err = meta.NewAccessor().ResourceVersion(obj)
+ if err != nil {
+ return err
+ }
+ }
+
+ writer := printers.GetNewTabWriter(o.Out)
+
+ // print the current object
+ var objsToPrint []runtime.Object
+ if isList {
+ objsToPrint, _ = meta.ExtractList(obj)
+ } else {
+ objsToPrint = append(objsToPrint, obj)
+ }
+ for _, objToPrint := range objsToPrint {
+ if o.OutputWatchEvents {
+ objToPrint = &metav1.WatchEvent{Type: string(watch.Added), Object: runtime.RawExtension{Object: objToPrint}}
+ }
+ if err := printer.PrintObj(objToPrint, writer); err != nil {
+ return fmt.Errorf("unable to output the provided object: %v", err)
+ }
+ }
+ writer.Flush()
+ if isList {
+ // we can start outputting objects now, watches started from lists don't emit synthetic added events
+ *outputObjects = true
+ } else {
+ // suppress output, since watches started for individual items emit a synthetic ADDED event first
+ *outputObjects = false
+ }
+
+ // print watched changes
+ w, err := r.Watch(rv)
+ if err != nil {
+ return err
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ intr := interrupt.New(nil, cancel)
+ intr.Run(func() error {
+ _, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
+ objToPrint := e.Object
+ if o.OutputWatchEvents {
+ objToPrint = &metav1.WatchEvent{Type: string(e.Type), Object: runtime.RawExtension{Object: objToPrint}}
+ }
+ if err := printer.PrintObj(objToPrint, writer); err != nil {
+ return false, err
+ }
+ writer.Flush()
+ // after processing at least one event, start outputting objects
+ *outputObjects = true
+ return false, nil
+ })
+ return err
+ })
+ return nil
+}
+
+func (o *GetOptions) printGeneric(r *resource.Result) error {
+ // we flattened the data from the builder, so we have individual items, but now we'd like to either:
+ // 1. if there is more than one item, combine them all into a single list
+ // 2. if there is a single item and that item is a list, leave it as its specific list
+ // 3. if there is a single item and it is not a list, leave it as a single item
+ var errs []error
+ singleItemImplied := false
+ infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
+ if err != nil {
+ if singleItemImplied {
+ return err
+ }
+ errs = append(errs, err)
+ }
+
+ if len(infos) == 0 && o.IgnoreNotFound {
+ return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
+ }
+
+ printer, err := o.ToPrinter(nil, nil, false, false)
+ if err != nil {
+ return err
+ }
+
+ var obj runtime.Object
+ if !singleItemImplied || len(infos) != 1 {
+ // we have zero or multple items, so coerce all items into a list.
+ // we don't want an *unstructured.Unstructured list yet, as we
+ // may be dealing with non-unstructured objects. Compose all items
+ // into an corev1.List, and then decode using an unstructured scheme.
+ list := corev1.List{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "List",
+ APIVersion: "v1",
+ },
+ ListMeta: metav1.ListMeta{},
+ }
+ for _, info := range infos {
+ list.Items = append(list.Items, runtime.RawExtension{Object: info.Object})
+ }
+
+ listData, err := json.Marshal(list)
+ if err != nil {
+ return err
+ }
+
+ converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, listData)
+ if err != nil {
+ return err
+ }
+
+ obj = converted
+ } else {
+ obj = infos[0].Object
+ }
+
+ isList := meta.IsListType(obj)
+ if isList {
+ items, err := meta.ExtractList(obj)
+ if err != nil {
+ return err
+ }
+
+ // take the items and create a new list for display
+ list := &unstructured.UnstructuredList{
+ Object: map[string]interface{}{
+ "kind": "List",
+ "apiVersion": "v1",
+ "metadata": map[string]interface{}{},
+ },
+ }
+ if listMeta, err := meta.ListAccessor(obj); err == nil {
+ list.Object["metadata"] = map[string]interface{}{
+ "resourceVersion": listMeta.GetResourceVersion(),
+ }
+ }
+
+ for _, item := range items {
+ list.Items = append(list.Items, *item.(*unstructured.Unstructured))
+ }
+ if err := printer.PrintObj(list, o.Out); err != nil {
+ errs = append(errs, err)
+ }
+ return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
+ }
+
+ if printErr := printer.PrintObj(obj, o.Out); printErr != nil {
+ errs = append(errs, printErr)
+ }
+
+ return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
+}
+
+func addServerPrintColumnFlags(cmd *cobra.Command, opt *GetOptions) {
+ cmd.Flags().BoolVar(&opt.ServerPrint, useServerPrintColumns, opt.ServerPrint, "If true, have the server return the appropriate table output. Supports extension APIs and CRDs.")
+}
+
+func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
+ return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
+}
+
+func multipleGVKsRequested(infos []*resource.Info) bool {
+ if len(infos) < 2 {
+ return false
+ }
+ gvk := infos[0].Mapping.GroupVersionKind
+ for _, info := range infos {
+ if info.Mapping.GroupVersionKind != gvk {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/get_flags.go b/vendor/k8s.io/kubectl/pkg/cmd/get/get_flags.go
new file mode 100644
index 000000000..e5eb98b21
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/get_flags.go
@@ -0,0 +1,170 @@
+/*
+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 get
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/spf13/cobra"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/cli-runtime/pkg/genericclioptions"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/kubectl/pkg/cmd/util"
+)
+
+// PrintFlags composes common printer flag structs
+// used in the Get command.
+type PrintFlags struct {
+ JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
+ NamePrintFlags *genericclioptions.NamePrintFlags
+ CustomColumnsFlags *CustomColumnsPrintFlags
+ HumanReadableFlags *HumanPrintFlags
+ TemplateFlags *genericclioptions.KubeTemplatePrintFlags
+
+ NoHeaders *bool
+ OutputFormat *string
+}
+
+// SetKind sets the Kind option of humanreadable flags
+func (f *PrintFlags) SetKind(kind schema.GroupKind) {
+ f.HumanReadableFlags.SetKind(kind)
+}
+
+// EnsureWithNamespace ensures that humanreadable flags return
+// a printer capable of printing with a "namespace" column.
+func (f *PrintFlags) EnsureWithNamespace() error {
+ return f.HumanReadableFlags.EnsureWithNamespace()
+}
+
+// EnsureWithKind ensures that humanreadable flags return
+// a printer capable of including resource kinds.
+func (f *PrintFlags) EnsureWithKind() error {
+ return f.HumanReadableFlags.EnsureWithKind()
+}
+
+// Copy returns a copy of PrintFlags for mutation
+func (f *PrintFlags) Copy() PrintFlags {
+ printFlags := *f
+ return printFlags
+}
+
+// AllowedFormats is the list of formats in which data can be displayed
+func (f *PrintFlags) AllowedFormats() []string {
+ formats := f.JSONYamlPrintFlags.AllowedFormats()
+ formats = append(formats, f.NamePrintFlags.AllowedFormats()...)
+ formats = append(formats, f.TemplateFlags.AllowedFormats()...)
+ formats = append(formats, f.CustomColumnsFlags.AllowedFormats()...)
+ formats = append(formats, f.HumanReadableFlags.AllowedFormats()...)
+ return formats
+}
+
+// ToPrinter attempts to find a composed set of PrintFlags suitable for
+// returning a printer based on current flag values.
+func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
+ outputFormat := ""
+ if f.OutputFormat != nil {
+ outputFormat = *f.OutputFormat
+ }
+
+ noHeaders := false
+ if f.NoHeaders != nil {
+ noHeaders = *f.NoHeaders
+ }
+ f.HumanReadableFlags.NoHeaders = noHeaders
+ f.CustomColumnsFlags.NoHeaders = noHeaders
+
+ // for "get.go" we want to support a --template argument given, even when no --output format is provided
+ if f.TemplateFlags.TemplateArgument != nil && len(*f.TemplateFlags.TemplateArgument) > 0 && len(outputFormat) == 0 {
+ outputFormat = "go-template"
+ }
+
+ if p, err := f.TemplateFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
+ return p, err
+ }
+
+ if f.TemplateFlags.TemplateArgument != nil {
+ f.CustomColumnsFlags.TemplateArgument = *f.TemplateFlags.TemplateArgument
+ }
+
+ if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
+ return p, err
+ }
+
+ if p, err := f.HumanReadableFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
+ return p, err
+ }
+
+ if p, err := f.CustomColumnsFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
+ return p, err
+ }
+
+ if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
+ return p, err
+ }
+
+ return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()}
+}
+
+// AddFlags receives a *cobra.Command reference and binds
+// flags related to humanreadable and template printing.
+func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
+ f.JSONYamlPrintFlags.AddFlags(cmd)
+ f.NamePrintFlags.AddFlags(cmd)
+ f.TemplateFlags.AddFlags(cmd)
+ f.HumanReadableFlags.AddFlags(cmd)
+ f.CustomColumnsFlags.AddFlags(cmd)
+
+ if f.OutputFormat != nil {
+ cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf(`Output format. One of: (%s). See custom columns [https://kubernetes.io/docs/reference/kubectl/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [https://kubernetes.io/docs/reference/kubectl/jsonpath/].`, strings.Join(f.AllowedFormats(), ", ")))
+ util.CheckErr(cmd.RegisterFlagCompletionFunc(
+ "output",
+ func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ var comps []string
+ for _, format := range f.AllowedFormats() {
+ if strings.HasPrefix(format, toComplete) {
+ comps = append(comps, format)
+ }
+ }
+ return comps, cobra.ShellCompDirectiveNoFileComp
+ },
+ ))
+ }
+ if f.NoHeaders != nil {
+ cmd.Flags().BoolVar(f.NoHeaders, "no-headers", *f.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
+ }
+}
+
+// NewGetPrintFlags returns flags associated with humanreadable,
+// template, and "name" printing, with default values set.
+func NewGetPrintFlags() *PrintFlags {
+ outputFormat := ""
+ noHeaders := false
+
+ return &PrintFlags{
+ OutputFormat: &outputFormat,
+ NoHeaders: &noHeaders,
+
+ JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(),
+ NamePrintFlags: genericclioptions.NewNamePrintFlags(""),
+ TemplateFlags: genericclioptions.NewKubeTemplatePrintFlags(),
+
+ HumanReadableFlags: NewHumanPrintFlags(),
+ CustomColumnsFlags: NewCustomColumnsPrintFlags(),
+ }
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/humanreadable_flags.go b/vendor/k8s.io/kubectl/pkg/cmd/get/humanreadable_flags.go
new file mode 100644
index 000000000..5fcf34d8c
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/humanreadable_flags.go
@@ -0,0 +1,138 @@
+/*
+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 get
+
+import (
+ "github.com/spf13/cobra"
+ "k8s.io/cli-runtime/pkg/genericclioptions"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/cli-runtime/pkg/printers"
+)
+
+// HumanPrintFlags provides default flags necessary for printing.
+// Given the following flag values, a printer can be requested that knows
+// how to handle printing based on these values.
+type HumanPrintFlags struct {
+ ShowKind *bool
+ ShowLabels *bool
+ SortBy *string
+ ColumnLabels *[]string
+
+ // get.go-specific values
+ NoHeaders bool
+
+ Kind schema.GroupKind
+ WithNamespace bool
+}
+
+// SetKind sets the Kind option
+func (f *HumanPrintFlags) SetKind(kind schema.GroupKind) {
+ f.Kind = kind
+}
+
+// EnsureWithKind sets the "Showkind" humanreadable option to true.
+func (f *HumanPrintFlags) EnsureWithKind() error {
+ showKind := true
+ f.ShowKind = &showKind
+ return nil
+}
+
+// EnsureWithNamespace sets the "WithNamespace" humanreadable option to true.
+func (f *HumanPrintFlags) EnsureWithNamespace() error {
+ f.WithNamespace = true
+ return nil
+}
+
+// AllowedFormats returns more customized formating options
+func (f *HumanPrintFlags) AllowedFormats() []string {
+ return []string{"wide"}
+}
+
+// ToPrinter receives an outputFormat and returns a printer capable of
+// handling human-readable output.
+func (f *HumanPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
+ if len(outputFormat) > 0 && outputFormat != "wide" {
+ return nil, genericclioptions.NoCompatiblePrinterError{Options: f, AllowedFormats: f.AllowedFormats()}
+ }
+
+ showKind := false
+ if f.ShowKind != nil {
+ showKind = *f.ShowKind
+ }
+
+ showLabels := false
+ if f.ShowLabels != nil {
+ showLabels = *f.ShowLabels
+ }
+
+ columnLabels := []string{}
+ if f.ColumnLabels != nil {
+ columnLabels = *f.ColumnLabels
+ }
+
+ p := printers.NewTablePrinter(printers.PrintOptions{
+ Kind: f.Kind,
+ WithKind: showKind,
+ NoHeaders: f.NoHeaders,
+ Wide: outputFormat == "wide",
+ WithNamespace: f.WithNamespace,
+ ColumnLabels: columnLabels,
+ ShowLabels: showLabels,
+ })
+
+ // TODO(juanvallejo): handle sorting here
+
+ return p, nil
+}
+
+// AddFlags receives a *cobra.Command reference and binds
+// flags related to human-readable printing to it
+func (f *HumanPrintFlags) AddFlags(c *cobra.Command) {
+ if f.ShowLabels != nil {
+ c.Flags().BoolVar(f.ShowLabels, "show-labels", *f.ShowLabels, "When printing, show all labels as the last column (default hide labels column)")
+ }
+ if f.SortBy != nil {
+ c.Flags().StringVar(f.SortBy, "sort-by", *f.SortBy, "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
+ }
+ if f.ColumnLabels != nil {
+ c.Flags().StringSliceVarP(f.ColumnLabels, "label-columns", "L", *f.ColumnLabels, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
+ }
+ if f.ShowKind != nil {
+ c.Flags().BoolVar(f.ShowKind, "show-kind", *f.ShowKind, "If present, list the resource type for the requested object(s).")
+ }
+}
+
+// NewHumanPrintFlags returns flags associated with
+// human-readable printing, with default values set.
+func NewHumanPrintFlags() *HumanPrintFlags {
+ showLabels := false
+ sortBy := ""
+ showKind := false
+ columnLabels := []string{}
+
+ return &HumanPrintFlags{
+ NoHeaders: false,
+ WithNamespace: false,
+ ColumnLabels: &columnLabels,
+
+ Kind: schema.GroupKind{},
+ ShowLabels: &showLabels,
+ SortBy: &sortBy,
+ ShowKind: &showKind,
+ }
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/skip_printer.go b/vendor/k8s.io/kubectl/pkg/cmd/get/skip_printer.go
new file mode 100644
index 000000000..95a804315
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/skip_printer.go
@@ -0,0 +1,48 @@
+/*
+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 get
+
+import (
+ "io"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/cli-runtime/pkg/printers"
+)
+
+// skipPrinter allows conditionally suppressing object output via the output field.
+// table objects are suppressed by setting their Rows to nil (allowing column definitions to propagate to the delegate).
+// non-table objects are suppressed by not calling the delegate at all.
+type skipPrinter struct {
+ delegate printers.ResourcePrinter
+ output *bool
+}
+
+func (p *skipPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
+ if *p.output {
+ return p.delegate.PrintObj(obj, writer)
+ }
+
+ table, isTable := obj.(*metav1.Table)
+ if !isTable {
+ return nil
+ }
+
+ table = table.DeepCopy()
+ table.Rows = nil
+ return p.delegate.PrintObj(table, writer)
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/sorter.go b/vendor/k8s.io/kubectl/pkg/cmd/get/sorter.go
new file mode 100644
index 000000000..9f9e2b804
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/sorter.go
@@ -0,0 +1,424 @@
+/*
+Copyright 2014 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 get
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+
+ "k8s.io/klog/v2"
+
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/client-go/util/jsonpath"
+ "k8s.io/utils/integer"
+
+ "github.com/fvbommel/sortorder"
+)
+
+// SortingPrinter sorts list types before delegating to another printer.
+// Non-list types are simply passed through
+type SortingPrinter struct {
+ SortField string
+ Delegate printers.ResourcePrinter
+ Decoder runtime.Decoder
+}
+
+func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
+ if table, isTable := obj.(*metav1.Table); isTable && len(table.Rows) > 1 {
+ parsedField, err := RelaxedJSONPathExpression(s.SortField)
+ if err != nil {
+ parsedField = s.SortField
+ }
+
+ if sorter, err := NewTableSorter(table, parsedField); err != nil {
+ return err
+ } else if err := sorter.Sort(); err != nil {
+ return err
+ }
+ return s.Delegate.PrintObj(table, out)
+ }
+
+ if meta.IsListType(obj) {
+ if err := s.sortObj(obj); err != nil {
+ return err
+ }
+ return s.Delegate.PrintObj(obj, out)
+ }
+
+ return s.Delegate.PrintObj(obj, out)
+}
+
+func (s *SortingPrinter) sortObj(obj runtime.Object) error {
+ objs, err := meta.ExtractList(obj)
+ if err != nil {
+ return err
+ }
+ if len(objs) == 0 {
+ return nil
+ }
+
+ sorter, err := SortObjects(s.Decoder, objs, s.SortField)
+ if err != nil {
+ return err
+ }
+
+ switch list := obj.(type) {
+ case *corev1.List:
+ outputList := make([]runtime.RawExtension, len(objs))
+ for ix := range objs {
+ outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
+ }
+ list.Items = outputList
+ return nil
+ }
+ return meta.SetList(obj, objs)
+}
+
+// SortObjects sorts the runtime.Object based on fieldInput and returns RuntimeSort that implements
+// the golang sort interface
+func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
+ for ix := range objs {
+ item := objs[ix]
+ switch u := item.(type) {
+ case *runtime.Unknown:
+ var err error
+ // decode runtime.Unknown to runtime.Unstructured for sorting.
+ // we don't actually want the internal versions of known types.
+ if objs[ix], _, err = decoder.Decode(u.Raw, nil, &unstructured.Unstructured{}); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ field, err := RelaxedJSONPathExpression(fieldInput)
+ if err != nil {
+ return nil, err
+ }
+
+ parser := jsonpath.New("sorting").AllowMissingKeys(true)
+ if err := parser.Parse(field); err != nil {
+ return nil, err
+ }
+
+ // We don't do any model validation here, so we traverse all objects to be sorted
+ // and, if the field is valid to at least one of them, we consider it to be a
+ // valid field; otherwise error out.
+ // Note that this requires empty fields to be considered later, when sorting.
+ var fieldFoundOnce bool
+ for _, obj := range objs {
+ values, err := findJSONPathResults(parser, obj)
+ if err != nil {
+ return nil, err
+ }
+ if len(values) > 0 && len(values[0]) > 0 {
+ fieldFoundOnce = true
+ break
+ }
+ }
+ if !fieldFoundOnce {
+ return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
+ }
+
+ sorter := NewRuntimeSort(field, objs)
+ sort.Sort(sorter)
+ return sorter, nil
+}
+
+// RuntimeSort is an implementation of the golang sort interface that knows how to sort
+// lists of runtime.Object
+type RuntimeSort struct {
+ field string
+ objs []runtime.Object
+ origPosition []int
+}
+
+// NewRuntimeSort creates a new RuntimeSort struct that implements golang sort interface
+func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
+ sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
+ for ix := range objs {
+ sorter.origPosition[ix] = ix
+ }
+ return sorter
+}
+
+func (r *RuntimeSort) Len() int {
+ return len(r.objs)
+}
+
+func (r *RuntimeSort) Swap(i, j int) {
+ r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
+ r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
+}
+
+func isLess(i, j reflect.Value) (bool, error) {
+ switch i.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return i.Int() < j.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return i.Uint() < j.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return i.Float() < j.Float(), nil
+ case reflect.String:
+ return sortorder.NaturalLess(i.String(), j.String()), nil
+ case reflect.Pointer:
+ return isLess(i.Elem(), j.Elem())
+ case reflect.Struct:
+ // sort metav1.Time
+ in := i.Interface()
+ if t, ok := in.(metav1.Time); ok {
+ time := j.Interface().(metav1.Time)
+ return t.Before(&time), nil
+ }
+ // sort resource.Quantity
+ if iQuantity, ok := in.(resource.Quantity); ok {
+ jQuantity := j.Interface().(resource.Quantity)
+ return iQuantity.Cmp(jQuantity) < 0, nil
+ }
+ // fallback to the fields comparison
+ for idx := 0; idx < i.NumField(); idx++ {
+ less, err := isLess(i.Field(idx), j.Field(idx))
+ if err != nil || !less {
+ return less, err
+ }
+ }
+ return true, nil
+ case reflect.Array, reflect.Slice:
+ // note: the length of i and j may be different
+ for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
+ less, err := isLess(i.Index(idx), j.Index(idx))
+ if err != nil || !less {
+ return less, err
+ }
+ }
+ return true, nil
+ case reflect.Interface:
+ if i.IsNil() && j.IsNil() {
+ return false, nil
+ } else if i.IsNil() {
+ return true, nil
+ } else if j.IsNil() {
+ return false, nil
+ }
+ switch itype := i.Interface().(type) {
+ case uint8:
+ if jtype, ok := j.Interface().(uint8); ok {
+ return itype < jtype, nil
+ }
+ case uint16:
+ if jtype, ok := j.Interface().(uint16); ok {
+ return itype < jtype, nil
+ }
+ case uint32:
+ if jtype, ok := j.Interface().(uint32); ok {
+ return itype < jtype, nil
+ }
+ case uint64:
+ if jtype, ok := j.Interface().(uint64); ok {
+ return itype < jtype, nil
+ }
+ case int8:
+ if jtype, ok := j.Interface().(int8); ok {
+ return itype < jtype, nil
+ }
+ case int16:
+ if jtype, ok := j.Interface().(int16); ok {
+ return itype < jtype, nil
+ }
+ case int32:
+ if jtype, ok := j.Interface().(int32); ok {
+ return itype < jtype, nil
+ }
+ case int64:
+ if jtype, ok := j.Interface().(int64); ok {
+ return itype < jtype, nil
+ }
+ case uint:
+ if jtype, ok := j.Interface().(uint); ok {
+ return itype < jtype, nil
+ }
+ case int:
+ if jtype, ok := j.Interface().(int); ok {
+ return itype < jtype, nil
+ }
+ case float32:
+ if jtype, ok := j.Interface().(float32); ok {
+ return itype < jtype, nil
+ }
+ case float64:
+ if jtype, ok := j.Interface().(float64); ok {
+ return itype < jtype, nil
+ }
+ case string:
+ if jtype, ok := j.Interface().(string); ok {
+ // check if it's a Quantity
+ itypeQuantity, err := resource.ParseQuantity(itype)
+ if err != nil {
+ return sortorder.NaturalLess(itype, jtype), nil
+ }
+ jtypeQuantity, err := resource.ParseQuantity(jtype)
+ if err != nil {
+ return sortorder.NaturalLess(itype, jtype), nil
+ }
+ // Both strings are quantity
+ return itypeQuantity.Cmp(jtypeQuantity) < 0, nil
+ }
+ default:
+ return false, fmt.Errorf("unsortable type: %T", itype)
+ }
+ return false, fmt.Errorf("unsortable interface: %v", i.Kind())
+
+ default:
+ return false, fmt.Errorf("unsortable type: %v", i.Kind())
+ }
+}
+
+func (r *RuntimeSort) Less(i, j int) bool {
+ iObj := r.objs[i]
+ jObj := r.objs[j]
+
+ var iValues [][]reflect.Value
+ var jValues [][]reflect.Value
+ var err error
+
+ parser := jsonpath.New("sorting").AllowMissingKeys(true)
+ err = parser.Parse(r.field)
+ if err != nil {
+ panic(err)
+ }
+
+ iValues, err = findJSONPathResults(parser, iObj)
+ if err != nil {
+ klog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
+ }
+
+ jValues, err = findJSONPathResults(parser, jObj)
+ if err != nil {
+ klog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
+ }
+
+ if len(iValues) == 0 || len(iValues[0]) == 0 {
+ return true
+ }
+ if len(jValues) == 0 || len(jValues[0]) == 0 {
+ return false
+ }
+ iField := iValues[0][0]
+ jField := jValues[0][0]
+
+ less, err := isLess(iField, jField)
+ if err != nil {
+ klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
+ }
+ return less
+}
+
+// OriginalPosition returns the starting (original) position of a particular index.
+// e.g. If OriginalPosition(0) returns 5 than the
+// item currently at position 0 was at position 5 in the original unsorted array.
+func (r *RuntimeSort) OriginalPosition(ix int) int {
+ if ix < 0 || ix > len(r.origPosition) {
+ return -1
+ }
+ return r.origPosition[ix]
+}
+
+type TableSorter struct {
+ field string
+ obj *metav1.Table
+ parsedRows [][][]reflect.Value
+}
+
+func (t *TableSorter) Len() int {
+ return len(t.obj.Rows)
+}
+
+func (t *TableSorter) Swap(i, j int) {
+ t.obj.Rows[i], t.obj.Rows[j] = t.obj.Rows[j], t.obj.Rows[i]
+ t.parsedRows[i], t.parsedRows[j] = t.parsedRows[j], t.parsedRows[i]
+}
+
+func (t *TableSorter) Less(i, j int) bool {
+ iValues := t.parsedRows[i]
+ jValues := t.parsedRows[j]
+
+ if len(iValues) == 0 || len(iValues[0]) == 0 {
+ return true
+ }
+ if len(jValues) == 0 || len(jValues[0]) == 0 {
+ return false
+ }
+
+ iField := iValues[0][0]
+ jField := jValues[0][0]
+
+ less, err := isLess(iField, jField)
+ if err != nil {
+ klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", t.field, t.parsedRows, iField.Kind().String(), err)
+ }
+ return less
+}
+
+func (t *TableSorter) Sort() error {
+ sort.Sort(t)
+ return nil
+}
+
+func NewTableSorter(table *metav1.Table, field string) (*TableSorter, error) {
+ var parsedRows [][][]reflect.Value
+
+ parser := jsonpath.New("sorting").AllowMissingKeys(true)
+ err := parser.Parse(field)
+ if err != nil {
+ return nil, fmt.Errorf("sorting error: %v", err)
+ }
+
+ fieldFoundOnce := false
+ for i := range table.Rows {
+ parsedRow, err := findJSONPathResults(parser, table.Rows[i].Object.Object)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to get values for %#v using %s (%#v)", parsedRow, field, err)
+ }
+ parsedRows = append(parsedRows, parsedRow)
+ if len(parsedRow) > 0 && len(parsedRow[0]) > 0 {
+ fieldFoundOnce = true
+ }
+ }
+
+ if len(table.Rows) > 0 && !fieldFoundOnce {
+ return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
+ }
+
+ return &TableSorter{
+ obj: table,
+ field: field,
+ parsedRows: parsedRows,
+ }, nil
+}
+func findJSONPathResults(parser *jsonpath.JSONPath, from runtime.Object) ([][]reflect.Value, error) {
+ if unstructuredObj, ok := from.(*unstructured.Unstructured); ok {
+ return parser.FindResults(unstructuredObj.Object)
+ }
+ return parser.FindResults(reflect.ValueOf(from).Elem().Interface())
+}
diff --git a/vendor/k8s.io/kubectl/pkg/cmd/get/table_printer.go b/vendor/k8s.io/kubectl/pkg/cmd/get/table_printer.go
new file mode 100644
index 000000000..920722737
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/cmd/get/table_printer.go
@@ -0,0 +1,94 @@
+/*
+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 get
+
+import (
+ "fmt"
+ "io"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/cli-runtime/pkg/printers"
+ "k8s.io/klog/v2"
+)
+
+// TablePrinter decodes table objects into typed objects before delegating to another printer.
+// Non-table types are simply passed through
+type TablePrinter struct {
+ Delegate printers.ResourcePrinter
+}
+
+func (t *TablePrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
+ table, err := decodeIntoTable(obj)
+ if err == nil {
+ return t.Delegate.PrintObj(table, writer)
+ }
+ // if we are unable to decode server response into a v1beta1.Table,
+ // fallback to client-side printing with whatever info the server returned.
+ klog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
+ return t.Delegate.PrintObj(obj, writer)
+}
+
+var recognizedTableVersions = map[schema.GroupVersionKind]bool{
+ metav1beta1.SchemeGroupVersion.WithKind("Table"): true,
+ metav1.SchemeGroupVersion.WithKind("Table"): true,
+}
+
+// assert the types are identical, since we're decoding both types into a metav1.Table
+var _ metav1.Table = metav1beta1.Table{}
+var _ metav1beta1.Table = metav1.Table{}
+
+func decodeIntoTable(obj runtime.Object) (runtime.Object, error) {
+ event, isEvent := obj.(*metav1.WatchEvent)
+ if isEvent {
+ obj = event.Object.Object
+ }
+
+ if !recognizedTableVersions[obj.GetObjectKind().GroupVersionKind()] {
+ return nil, fmt.Errorf("attempt to decode non-Table object")
+ }
+
+ unstr, ok := obj.(*unstructured.Unstructured)
+ if !ok {
+ return nil, fmt.Errorf("attempt to decode non-Unstructured object")
+ }
+ table := &metav1.Table{}
+ if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil {
+ return nil, err
+ }
+
+ for i := range table.Rows {
+ row := &table.Rows[i]
+ if row.Object.Raw == nil || row.Object.Object != nil {
+ continue
+ }
+ converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw)
+ if err != nil {
+ return nil, err
+ }
+ row.Object.Object = converted
+ }
+
+ if isEvent {
+ event.Object.Object = table
+ return event, nil
+ }
+ return table, nil
+}
diff --git a/vendor/k8s.io/kubectl/pkg/rawhttp/raw.go b/vendor/k8s.io/kubectl/pkg/rawhttp/raw.go
new file mode 100644
index 000000000..57d73cde0
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/rawhttp/raw.go
@@ -0,0 +1,95 @@
+/*
+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 rawhttp
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "os"
+
+ "k8s.io/cli-runtime/pkg/genericiooptions"
+ "k8s.io/client-go/rest"
+)
+
+// RawPost uses the REST client to POST content
+func RawPost(restClient *rest.RESTClient, streams genericiooptions.IOStreams, url, filename string) error {
+ return raw(restClient, streams, url, filename, "POST")
+}
+
+// RawPut uses the REST client to PUT content
+func RawPut(restClient *rest.RESTClient, streams genericiooptions.IOStreams, url, filename string) error {
+ return raw(restClient, streams, url, filename, "PUT")
+}
+
+// RawGet uses the REST client to GET content
+func RawGet(restClient *rest.RESTClient, streams genericiooptions.IOStreams, url string) error {
+ return raw(restClient, streams, url, "", "GET")
+}
+
+// RawDelete uses the REST client to DELETE content
+func RawDelete(restClient *rest.RESTClient, streams genericiooptions.IOStreams, url, filename string) error {
+ return raw(restClient, streams, url, filename, "DELETE")
+}
+
+// raw makes a simple HTTP request to the provided path on the server using the default credentials.
+func raw(restClient *rest.RESTClient, streams genericiooptions.IOStreams, url, filename, requestType string) error {
+ var data io.Reader
+ switch {
+ case len(filename) == 0:
+ data = bytes.NewBuffer([]byte{})
+
+ case filename == "-":
+ data = streams.In
+
+ default:
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ data = f
+ }
+
+ var request *rest.Request
+ switch requestType {
+ case "GET":
+ request = restClient.Get().RequestURI(url)
+ case "PUT":
+ request = restClient.Put().RequestURI(url).Body(data)
+ case "POST":
+ request = restClient.Post().RequestURI(url).Body(data)
+ case "DELETE":
+ request = restClient.Delete().RequestURI(url).Body(data)
+
+ default:
+ return fmt.Errorf("unknown requestType: %q", requestType)
+ }
+
+ stream, err := request.Stream(context.TODO())
+ if err != nil {
+ return err
+ }
+ defer stream.Close()
+
+ _, err = io.Copy(streams.Out, stream)
+ if err != nil && err != io.EOF {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/k8s.io/kubectl/pkg/util/slice/slice.go b/vendor/k8s.io/kubectl/pkg/util/slice/slice.go
new file mode 100644
index 000000000..d02bb3458
--- /dev/null
+++ b/vendor/k8s.io/kubectl/pkg/util/slice/slice.go
@@ -0,0 +1,57 @@
+/*
+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 slice
+
+import (
+ "sort"
+)
+
+// SortInts64 sorts []int64 in increasing order
+func SortInts64(a []int64) { sort.Slice(a, func(i, j int) bool { return a[i] < a[j] }) }
+
+// ContainsString checks if a given slice of strings contains the provided string.
+// If a modifier func is provided, it is called with the slice item before the comparation.
+func ContainsString(slice []string, s string, modifier func(s string) string) bool {
+ for _, item := range slice {
+ if item == s {
+ return true
+ }
+ if modifier != nil && modifier(item) == s {
+ return true
+ }
+ }
+ return false
+}
+
+// ToSet returns a single slice containing the unique values from one or more slices. The order of the items in the
+// result is not guaranteed.
+func ToSet[T comparable](slices ...[]T) []T {
+ if len(slices) == 0 {
+ return nil
+ }
+ m := map[T]struct{}{}
+ for _, slice := range slices {
+ for _, value := range slice {
+ m[value] = struct{}{}
+ }
+ }
+ result := []T{}
+ for k := range m {
+ result = append(result, k)
+ }
+ return result
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b0b614d7a..dc6bd9a40 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -301,6 +301,9 @@ github.com/felixge/httpsnoop
# github.com/fsnotify/fsnotify v1.6.0
## explicit; go 1.16
github.com/fsnotify/fsnotify
+# github.com/fvbommel/sortorder v1.1.0
+## explicit; go 1.13
+github.com/fvbommel/sortorder
# github.com/go-errors/errors v1.4.2
## explicit; go 1.14
github.com/go-errors/errors
@@ -2171,13 +2174,16 @@ k8s.io/kube-scheduler/config/v1beta3
k8s.io/kube-scheduler/extender/v1
# k8s.io/kubectl v0.28.6 => github.com/kubeedge/kubernetes/staging/src/k8s.io/kubectl v1.28.6-kubeedge1
## explicit; go 1.20
+k8s.io/kubectl/pkg/cmd/get
k8s.io/kubectl/pkg/cmd/util
+k8s.io/kubectl/pkg/rawhttp
k8s.io/kubectl/pkg/scale
k8s.io/kubectl/pkg/scheme
k8s.io/kubectl/pkg/util/i18n
k8s.io/kubectl/pkg/util/interrupt
k8s.io/kubectl/pkg/util/openapi
k8s.io/kubectl/pkg/util/podutils
+k8s.io/kubectl/pkg/util/slice
k8s.io/kubectl/pkg/util/templates
k8s.io/kubectl/pkg/util/term
k8s.io/kubectl/pkg/validation