mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #63367 from juanvallejo/jvallejo/fail-printing-on-internal-obj-given
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. fail printing on internal obj **Release note**: ```release-note NONE ``` Returns an error on `PrintObj` methods if an internal object is given. cc @deads2k @soltysh
This commit is contained in:
commit
ec8db4e266
@ -155,6 +155,7 @@ go_test(
|
|||||||
"apply_test.go",
|
"apply_test.go",
|
||||||
"attach_test.go",
|
"attach_test.go",
|
||||||
"clusterinfo_dump_test.go",
|
"clusterinfo_dump_test.go",
|
||||||
|
"cmd_printing_test.go",
|
||||||
"cmd_test.go",
|
"cmd_test.go",
|
||||||
"convert_test.go",
|
"convert_test.go",
|
||||||
"cp_test.go",
|
"cp_test.go",
|
||||||
|
221
pkg/kubectl/cmd/cmd_printing_test.go
Normal file
221
pkg/kubectl/cmd/cmd_printing_test.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
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 cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIllegalPackageSourceCheckerThroughPrintFlags(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
expectInternalObjErr bool
|
||||||
|
output string
|
||||||
|
obj runtime.Object
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "success printer: object containing package path with no forbidden prefix returns no error",
|
||||||
|
expectInternalObjErr: false,
|
||||||
|
obj: externalPod(),
|
||||||
|
output: "",
|
||||||
|
expectedOutput: "pod/foo succeeded\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "name printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
output: "name",
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "json printer: json printer is wrapped in a versioned printer - internal obj should be converted with no error",
|
||||||
|
expectInternalObjErr: false,
|
||||||
|
output: "json",
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "yaml printer: yaml printer is wrapped in a versioned printer - internal obj should be converted with no error",
|
||||||
|
expectInternalObjErr: false,
|
||||||
|
output: "yaml",
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
printFlags := printers.NewPrintFlags("succeeded", legacyscheme.Scheme)
|
||||||
|
printFlags.OutputFormat = &tc.output
|
||||||
|
|
||||||
|
printer, err := printFlags.ToPrinter()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
err = printer.PrintObj(tc.obj, output)
|
||||||
|
if err != nil {
|
||||||
|
if !tc.expectInternalObjErr {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !printers.IsInternalObjectError(err) {
|
||||||
|
t.Fatalf("unexpected error - expecting internal object printer error, got %q", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectInternalObjErr {
|
||||||
|
t.Fatalf("expected internal object printer error, but got no error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.expectedOutput) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectedOutput != output.String() {
|
||||||
|
t.Fatalf("unexpected output: expecting %q, got %q", tc.expectedOutput, output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIllegalPackageSourceCheckerDirectlyThroughPrinters(t *testing.T) {
|
||||||
|
jsonPathPrinter, err := printers.NewJSONPathPrinter("{ .metadata.name }")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
goTemplatePrinter, err := printers.NewGoTemplatePrinter([]byte("{{ .metadata.name }}"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
customColumns, err := printers.NewCustomColumnsPrinterFromSpec("NAME:.metadata.name", scheme.Codecs.UniversalDecoder(), true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
expectInternalObjErr bool
|
||||||
|
printer printers.ResourcePrinter
|
||||||
|
obj runtime.Object
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "json printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: &printers.JSONPrinter{},
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "yaml printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: &printers.YAMLPrinter{},
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jsonpath printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: jsonPathPrinter,
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go-template printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: goTemplatePrinter,
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go-template printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: goTemplatePrinter,
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-columns printer: object containing package path beginning with forbidden prefix is rejected",
|
||||||
|
expectInternalObjErr: true,
|
||||||
|
printer: customColumns,
|
||||||
|
obj: internalPod(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
err := tc.printer.PrintObj(tc.obj, output)
|
||||||
|
if err != nil {
|
||||||
|
if !tc.expectInternalObjErr {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !printers.IsInternalObjectError(err) {
|
||||||
|
t.Fatalf("unexpected error - expecting internal object printer error, got %q", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectInternalObjErr {
|
||||||
|
t.Fatalf("expected internal object printer error, but got no error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.expectedOutput) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectedOutput != output.String() {
|
||||||
|
t.Fatalf("unexpected output: expecting %q, got %q", tc.expectedOutput, output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func internalPod() *api.Pod {
|
||||||
|
return &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: api.PodStatus{
|
||||||
|
Phase: api.PodRunning,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func externalPod() *v1.Pod {
|
||||||
|
return &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -20,13 +20,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
kapierrors "k8s.io/apimachinery/pkg/api/errors"
|
kapierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -194,7 +194,9 @@ func validateArguments(cmd *cobra.Command, filenames, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
o.OldName = args[0]
|
o.OldName = args[0]
|
||||||
|
}
|
||||||
o.DryRun = cmdutil.GetDryRunFlag(cmd)
|
o.DryRun = cmdutil.GetDryRunFlag(cmd)
|
||||||
o.OutputFormat = cmdutil.GetFlagString(cmd, "output")
|
o.OutputFormat = cmdutil.GetFlagString(cmd, "output")
|
||||||
o.KeepOldName = len(args) == 1
|
o.KeepOldName = len(args) == 1
|
||||||
@ -399,6 +401,7 @@ func (o *RollingUpdateOptions) Run() error {
|
|||||||
if replicasDefaulted {
|
if replicasDefaulted {
|
||||||
newRc.Spec.Replicas = oldRc.Spec.Replicas
|
newRc.Spec.Replicas = oldRc.Spec.Replicas
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.DryRun {
|
if o.DryRun {
|
||||||
oldRcData := &bytes.Buffer{}
|
oldRcData := &bytes.Buffer{}
|
||||||
newRcData := &bytes.Buffer{}
|
newRcData := &bytes.Buffer{}
|
||||||
@ -410,10 +413,10 @@ func (o *RollingUpdateOptions) Run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := printer.PrintObj(oldRc, oldRcData); err != nil {
|
if err := printer.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(oldRc, nil), oldRcData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := printer.PrintObj(newRc, newRcData); err != nil {
|
if err := printer.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(newRc, nil), newRcData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,7 +465,7 @@ func (o *RollingUpdateOptions) Run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printer.PrintObj(newRc, o.Out)
|
return printer.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(newRc, nil), o.Out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findNewName(args []string, oldRc *api.ReplicationController) string {
|
func findNewName(args []string, oldRc *api.ReplicationController) string {
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -32,8 +33,6 @@ import (
|
|||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
"k8s.io/client-go/util/homedir"
|
"k8s.io/client-go/util/homedir"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/transport"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/util/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,12 +86,14 @@ filegroup(
|
|||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"flags_test.go",
|
||||||
"humanreadable_test.go",
|
"humanreadable_test.go",
|
||||||
"template_test.go",
|
"template_test.go",
|
||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
@ -151,6 +151,13 @@ type CustomColumnsPrinter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
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 internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
if w, found := out.(*tabwriter.Writer); !found {
|
if w, found := out.(*tabwriter.Writer); !found {
|
||||||
w = GetNewTabWriter(out)
|
w = GetNewTabWriter(out)
|
||||||
out = w
|
out = w
|
||||||
|
@ -18,12 +18,25 @@ package printers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
internalObjectPrinterErr = "a versioned object must be passed to a printer"
|
||||||
|
|
||||||
|
// disallowedPackagePrefixes contains regular expression templates
|
||||||
|
// for object package paths that are not allowed by printers.
|
||||||
|
disallowedPackagePrefixes = []string{
|
||||||
|
"k8s.io/kubernetes/pkg/apis/",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var internalObjectPreventer = &illegalPackageSourceChecker{disallowedPackagePrefixes}
|
||||||
|
|
||||||
type NoCompatiblePrinterError struct {
|
type NoCompatiblePrinterError struct {
|
||||||
OutputFormat *string
|
OutputFormat *string
|
||||||
Options interface{}
|
Options interface{}
|
||||||
@ -47,6 +60,14 @@ func IsNoCompatiblePrinterError(err error) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsInternalObjectError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return err.Error() == internalObjectPrinterErr
|
||||||
|
}
|
||||||
|
|
||||||
// PrintFlags composes common printer flag structs
|
// PrintFlags composes common printer flag structs
|
||||||
// used across all commands, and provides a method
|
// used across all commands, and provides a method
|
||||||
// of retrieving a known printer based on flag values provided.
|
// of retrieving a known printer based on flag values provided.
|
||||||
@ -111,3 +132,22 @@ func NewPrintFlags(operation string, scheme runtime.ObjectConvertor) *PrintFlags
|
|||||||
NamePrintFlags: NewNamePrintFlags(operation, scheme),
|
NamePrintFlags: NewNamePrintFlags(operation, scheme),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// illegalPackageSourceChecker compares a given
|
||||||
|
// object's package path, and determines if the
|
||||||
|
// object originates from a disallowed source.
|
||||||
|
type illegalPackageSourceChecker struct {
|
||||||
|
// disallowedPrefixes is a slice of disallowed package path
|
||||||
|
// prefixes for a given runtime.Object that we are printing.
|
||||||
|
disallowedPrefixes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *illegalPackageSourceChecker) IsForbidden(pkgPath string) bool {
|
||||||
|
for _, forbiddenPrefix := range c.disallowedPrefixes {
|
||||||
|
if strings.HasPrefix(pkgPath, forbiddenPrefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
66
pkg/printers/flags_test.go
Normal file
66
pkg/printers/flags_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
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 printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIllegalPackageSourceChecker(t *testing.T) {
|
||||||
|
disallowedPrefixes := []string{
|
||||||
|
"foo/bar",
|
||||||
|
"k8s.io/foo/bar/vendor/k8s.io/baz/buz",
|
||||||
|
"bar/foo/baz",
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
pkgPath string
|
||||||
|
shouldBeAllowed bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "package path beginning with forbidden prefix is rejected",
|
||||||
|
pkgPath: "foo/bar/baz/buz",
|
||||||
|
shouldBeAllowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "package path not fully matching forbidden prefix is allowed",
|
||||||
|
pkgPath: "bar/foo",
|
||||||
|
shouldBeAllowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "package path containing forbidden prefix (not as prefix) is allowed",
|
||||||
|
pkgPath: "k8s.io/bar/foo/baz/etc",
|
||||||
|
shouldBeAllowed: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := &illegalPackageSourceChecker{disallowedPrefixes}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
if checker.IsForbidden(tc.pkgPath) {
|
||||||
|
if tc.shouldBeAllowed {
|
||||||
|
t.Fatalf("expected package path %q to have been allowed", tc.pkgPath)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.shouldBeAllowed {
|
||||||
|
t.Fatalf("expected package path %q to have been rejected", tc.pkgPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -252,19 +252,19 @@ func testPrinter(t *testing.T, printer printers.ResourcePrinter, unmarshalFunc f
|
|||||||
t.Errorf("Test data and unmarshaled data are not equal: %v", diff.ObjectDiff(poutput, testData))
|
t.Errorf("Test data and unmarshaled data are not equal: %v", diff.ObjectDiff(poutput, testData))
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := &api.Pod{
|
obj := &v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||||
}
|
}
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
printer.PrintObj(obj, buf)
|
printer.PrintObj(obj, buf)
|
||||||
var objOut api.Pod
|
var objOut v1.Pod
|
||||||
// Verify that given function runs without error.
|
// Verify that given function runs without error.
|
||||||
err = unmarshalFunc(buf.Bytes(), &objOut)
|
err = unmarshalFunc(buf.Bytes(), &objOut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %#v", err)
|
t.Fatalf("unexpected error: %#v", err)
|
||||||
}
|
}
|
||||||
// Use real decode function to undo the versioning process.
|
// Use real decode function to undo the versioning process.
|
||||||
objOut = api.Pod{}
|
objOut = v1.Pod{}
|
||||||
if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil {
|
if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ func TestTemplatePanic(t *testing.T) {
|
|||||||
t.Fatalf("tmpl fail: %v", err)
|
t.Fatalf("tmpl fail: %v", err)
|
||||||
}
|
}
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
err = printer.PrintObj(&api.Pod{}, buffer)
|
err = printer.PrintObj(&v1.Pod{}, buffer)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected that template to crash")
|
t.Fatalf("expected that template to crash")
|
||||||
}
|
}
|
||||||
@ -374,7 +374,7 @@ func TestNamePrinter(t *testing.T) {
|
|||||||
expect string
|
expect string
|
||||||
}{
|
}{
|
||||||
"singleObject": {
|
"singleObject": {
|
||||||
&api.Pod{
|
&v1.Pod{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
},
|
},
|
||||||
@ -567,11 +567,7 @@ func TestPrinters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
jsonpathPrinter = printers.NewVersionedPrinter(jsonpathPrinter, legacyscheme.Scheme, legacyscheme.Scheme, v1.SchemeGroupVersion)
|
jsonpathPrinter = printers.NewVersionedPrinter(jsonpathPrinter, legacyscheme.Scheme, legacyscheme.Scheme, v1.SchemeGroupVersion)
|
||||||
|
|
||||||
allPrinters := map[string]printers.ResourcePrinter{
|
genericPrinters := map[string]printers.ResourcePrinter{
|
||||||
"humanReadable": printers.NewHumanReadablePrinter(nil, printers.PrintOptions{
|
|
||||||
NoHeaders: true,
|
|
||||||
}),
|
|
||||||
"humanReadableHeaders": printers.NewHumanReadablePrinter(nil, printers.PrintOptions{}),
|
|
||||||
"json": &printers.JSONPrinter{},
|
"json": &printers.JSONPrinter{},
|
||||||
"yaml": &printers.YAMLPrinter{},
|
"yaml": &printers.YAMLPrinter{},
|
||||||
"template": templatePrinter,
|
"template": templatePrinter,
|
||||||
@ -582,16 +578,14 @@ func TestPrinters(t *testing.T) {
|
|||||||
Decoders: []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme},
|
Decoders: []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
AddHandlers((allPrinters["humanReadable"]).(*printers.HumanReadablePrinter))
|
|
||||||
AddHandlers((allPrinters["humanReadableHeaders"]).(*printers.HumanReadablePrinter))
|
|
||||||
objects := map[string]runtime.Object{
|
objects := map[string]runtime.Object{
|
||||||
"pod": &api.Pod{ObjectMeta: om("pod")},
|
"pod": &v1.Pod{ObjectMeta: om("pod")},
|
||||||
"emptyPodList": &api.PodList{},
|
"emptyPodList": &v1.PodList{},
|
||||||
"nonEmptyPodList": &api.PodList{Items: []api.Pod{{}}},
|
"nonEmptyPodList": &v1.PodList{Items: []v1.Pod{{}}},
|
||||||
"endpoints": &api.Endpoints{
|
"endpoints": &v1.Endpoints{
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []v1.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
|
Addresses: []v1.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
|
||||||
Ports: []api.EndpointPort{{Port: 8080}},
|
Ports: []v1.EndpointPort{{Port: 8080}},
|
||||||
}}},
|
}}},
|
||||||
}
|
}
|
||||||
// map of printer name to set of objects it should fail on.
|
// map of printer name to set of objects it should fail on.
|
||||||
@ -600,7 +594,29 @@ func TestPrinters(t *testing.T) {
|
|||||||
"jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"),
|
"jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for pName, p := range allPrinters {
|
for pName, p := range genericPrinters {
|
||||||
|
for oName, obj := range objects {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if err := p.PrintObj(obj, b); err != nil {
|
||||||
|
if set, found := expectedErrors[pName]; found && set.Has(oName) {
|
||||||
|
// expected error
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Errorf("printer '%v', object '%v'; error: '%v'", pName, oName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a humanreadable printer deals with internal-versioned objects
|
||||||
|
humanReadablePrinter := map[string]printers.ResourcePrinter{
|
||||||
|
"humanReadable": printers.NewHumanReadablePrinter(nil, printers.PrintOptions{
|
||||||
|
NoHeaders: true,
|
||||||
|
}),
|
||||||
|
"humanReadableHeaders": printers.NewHumanReadablePrinter(nil, printers.PrintOptions{}),
|
||||||
|
}
|
||||||
|
AddHandlers((humanReadablePrinter["humanReadable"]).(*printers.HumanReadablePrinter))
|
||||||
|
AddHandlers((humanReadablePrinter["humanReadableHeaders"]).(*printers.HumanReadablePrinter))
|
||||||
|
for pName, p := range humanReadablePrinter {
|
||||||
for oName, obj := range objects {
|
for oName, obj := range objects {
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
if err := p.PrintObj(obj, b); err != nil {
|
if err := p.PrintObj(obj, b); err != nil {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
@ -28,11 +29,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON.
|
// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON.
|
||||||
type JSONPrinter struct {
|
type JSONPrinter struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
|
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
|
||||||
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *JSONPrinter) PrintObj(obj runtime.Object, w 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 internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *runtime.Unknown:
|
case *runtime.Unknown:
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@ -64,6 +71,13 @@ type YAMLPrinter struct {
|
|||||||
|
|
||||||
// PrintObj prints the data as YAML.
|
// PrintObj prints the data as YAML.
|
||||||
func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *YAMLPrinter) PrintObj(obj runtime.Object, w 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 internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *runtime.Unknown:
|
case *runtime.Unknown:
|
||||||
data, err := yaml.JSONToYAML(obj.Raw)
|
data, err := yaml.JSONToYAML(obj.Raw)
|
||||||
|
@ -107,11 +107,21 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
|
|||||||
if err := j.Parse(tmpl); err != nil {
|
if err := j.Parse(tmpl); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &JSONPathPrinter{tmpl, j}, nil
|
return &JSONPathPrinter{
|
||||||
|
rawTemplate: tmpl,
|
||||||
|
JSONPath: j,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintObj formats the obj with the JSONPath Template.
|
// PrintObj formats the obj with the JSONPath Template.
|
||||||
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w 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 internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
var queryObj interface{} = obj
|
var queryObj interface{} = obj
|
||||||
if meta.IsListType(obj) {
|
if meta.IsListType(obj) {
|
||||||
data, err := json.Marshal(obj)
|
data, err := json.Marshal(obj)
|
||||||
|
@ -19,6 +19,7 @@ package printers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -45,6 +46,13 @@ type NamePrinter struct {
|
|||||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
|
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
|
||||||
// and print "resource/name" pair. If the object is a List, print all items in it.
|
// and print "resource/name" pair. If the object is a List, print all items in it.
|
||||||
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||||
|
// we use reflect.Indirect here in order to obtain the actual value from a pointer.
|
||||||
|
// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
|
||||||
|
// we need an actual value in order to retrieve the package path for an object.
|
||||||
|
if internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
if meta.IsListType(obj) {
|
if meta.IsListType(obj) {
|
||||||
items, err := meta.ExtractList(obj)
|
items, err := meta.ExtractList(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -59,6 +60,10 @@ func (p *GoTemplatePrinter) AllowMissingKeys(allow bool) {
|
|||||||
|
|
||||||
// PrintObj formats the obj with the Go Template.
|
// PrintObj formats the obj with the Go Template.
|
||||||
func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||||
|
if internalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||||
|
return fmt.Errorf(internalObjectPrinterErr)
|
||||||
|
}
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
var err error
|
var err error
|
||||||
data, err = json.Marshal(obj)
|
data, err = json.Marshal(obj)
|
||||||
|
@ -21,8 +21,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTemplate(t *testing.T) {
|
func TestTemplate(t *testing.T) {
|
||||||
@ -33,8 +33,8 @@ func TestTemplate(t *testing.T) {
|
|||||||
expectErr func(error) (string, bool)
|
expectErr func(error) (string, bool)
|
||||||
}{
|
}{
|
||||||
"support base64 decoding of secret data": {
|
"support base64 decoding of secret data": {
|
||||||
template: "{{ .Data.username | base64decode }}",
|
template: "{{ .data.username | base64decode }}",
|
||||||
obj: &api.Secret{
|
obj: &v1.Secret{
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"username": []byte("hunter"),
|
"username": []byte("hunter"),
|
||||||
},
|
},
|
||||||
@ -42,7 +42,7 @@ func TestTemplate(t *testing.T) {
|
|||||||
expectOut: "hunter",
|
expectOut: "hunter",
|
||||||
},
|
},
|
||||||
"test error path for base64 decoding": {
|
"test error path for base64 decoding": {
|
||||||
template: "{{ .Data.username | base64decode }}",
|
template: "{{ .data.username | base64decode }}",
|
||||||
obj: &badlyMarshaledSecret{},
|
obj: &badlyMarshaledSecret{},
|
||||||
expectErr: func(err error) (string, bool) {
|
expectErr: func(err error) (string, bool) {
|
||||||
matched := strings.Contains(err.Error(), "base64 decode")
|
matched := strings.Contains(err.Error(), "base64 decode")
|
||||||
@ -89,9 +89,9 @@ func TestTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type badlyMarshaledSecret struct {
|
type badlyMarshaledSecret struct {
|
||||||
api.Secret
|
v1.Secret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a badlyMarshaledSecret) MarshalJSON() ([]byte, error) {
|
func (a badlyMarshaledSecret) MarshalJSON() ([]byte, error) {
|
||||||
return []byte(`{"apiVersion":"v1","Data":{"username":"--THIS IS NOT BASE64--"},"kind":"Secret"}`), nil
|
return []byte(`{"apiVersion":"v1","data":{"username":"--THIS IS NOT BASE64--"},"kind":"Secret"}`), nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user