Merge pull request #61496 from juanvallejo/jvallejo/add-success-printer-flags

Automatic merge from submit-queue (batch tested with PRs 61546, 61038, 61575, 60779, 61496). 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>.

wire through name/success print flags

**Release note**:
```release-note
NONE
```

Begin implementing pieces needed to retrieve name/success printers from a set of flags.
Proposal: https://docs.google.com/document/d/19ZZFVe9oD1KQmk5uExggRWtRl_hKGfYnBXvHZJlgEro/edit#heading=h.pnvbfi14v4zz

cc @soltysh @deads2k @pwittrock
This commit is contained in:
Kubernetes Submit Queue 2018-03-26 18:56:11 -07:00 committed by GitHub
commit 7d4bf1c338
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 243 additions and 7 deletions

View File

@ -16,6 +16,7 @@ go_library(
"json_yaml_flags.go",
"jsonpath.go",
"name.go",
"name_flags.go",
"printers.go",
"tabwriter.go",
"template.go",
@ -23,6 +24,7 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/printers",
deps = [
"//pkg/kubectl/scheme:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
@ -44,6 +46,7 @@ go_test(
srcs = [
"customcolumn_test.go",
"json_yaml_flags_test.go",
"name_flags_test.go",
],
deps = [
":go_default_library",

View File

@ -30,6 +30,15 @@ import (
// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object.
type NamePrinter struct {
// DryRun indicates whether the "(dry run)" message
// should be appended to the finalized "successful"
// message printed about an action on an object.
DryRun bool
// Operation describes the name of the action that
// took place on an object, to be included in the
// finalized "successful" message.
Operation string
Decoders []runtime.Decoder
Typer runtime.ObjectTyper
}
@ -64,7 +73,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
}
}
return printObj(w, name, GetObjectGroupKind(obj, p.Typer))
return printObj(w, name, p.Operation, p.DryRun, GetObjectGroupKind(obj, p.Typer))
}
func GetObjectGroupKind(obj runtime.Object, typer runtime.ObjectTyper) schema.GroupKind {
@ -94,17 +103,25 @@ func GetObjectGroupKind(obj runtime.Object, typer runtime.ObjectTyper) schema.Gr
return schema.GroupKind{Kind: "<unknown>"}
}
func printObj(w io.Writer, name string, groupKind schema.GroupKind) error {
func printObj(w io.Writer, name string, operation string, dryRun bool, groupKind schema.GroupKind) error {
if len(groupKind.Kind) == 0 {
return fmt.Errorf("missing kind for resource with name %v", name)
}
dryRunMsg := ""
if dryRun {
dryRunMsg = " (dry run)"
}
if len(operation) > 0 {
operation = " " + operation
}
if len(groupKind.Group) == 0 {
fmt.Fprintf(w, "%s/%s\n", strings.ToLower(groupKind.Kind), name)
fmt.Fprintf(w, "%s/%s%s%s\n", strings.ToLower(groupKind.Kind), name, operation, dryRunMsg)
return nil
}
fmt.Fprintf(w, "%s.%s/%s\n", strings.ToLower(groupKind.Kind), groupKind.Group, name)
fmt.Fprintf(w, "%s.%s/%s%s%s\n", strings.ToLower(groupKind.Kind), groupKind.Group, name, operation, dryRunMsg)
return nil
}

View File

@ -0,0 +1,80 @@
/*
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 (
"fmt"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
// NamePrintFlags provides default flags necessary for printing
// a resource's fully-qualified Kind.group/name, or a successful
// message about that resource if an Operation is provided.
type NamePrintFlags struct {
// DryRun indicates whether the "(dry run)" message
// should be appended to the finalized "successful"
// message printed about an action on an object.
DryRun bool
// Operation describes the name of the action that
// took place on an object, to be included in the
// finalized "successful" message.
Operation string
}
// ToPrinter receives an outputFormat and returns a printer capable of
// handling --output=name printing.
// Returns false if the specified outputFormat does not match a supported format.
// Supported format types can be found in pkg/printers/printers.go
func (f *NamePrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, bool, error) {
decoders := []runtime.Decoder{scheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}
namePrinter := &NamePrinter{
Operation: f.Operation,
DryRun: f.DryRun,
Typer: scheme.Scheme,
Decoders: decoders,
}
outputFormat = strings.ToLower(outputFormat)
switch outputFormat {
case "name":
return namePrinter, true, nil
case "":
return nil, false, fmt.Errorf("missing output format")
default:
return nil, false, nil
}
}
// AddFlags receives a *cobra.Command reference and binds
// flags related to name printing to it
func (f *NamePrintFlags) AddFlags(c *cobra.Command) {}
// NewNamePrintFlags returns flags associated with
// --name printing, with default values set.
func NewNamePrintFlags(operation string, dryRun bool) *NamePrintFlags {
return &NamePrintFlags{
Operation: operation,
DryRun: dryRun,
}
}

View File

@ -0,0 +1,130 @@
/*
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_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"strings"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/printers"
)
func TestNamePrinterSupportsExpectedFormats(t *testing.T) {
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
customColumnsFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer func(tempFile *os.File) {
tempFile.Close()
os.Remove(tempFile.Name())
}(customColumnsFile)
fmt.Fprintf(customColumnsFile, "NAME\n.metadata.name")
testCases := []struct {
name string
outputFormat string
operation string
dryRun bool
expectedError string
expectedOutput string
expectNoMatch bool
}{
{
name: "valid \"name\" output format with no operation prints resource name",
outputFormat: "name",
expectedOutput: "pod/foo",
},
{
name: "valid \"name\" output format and an operation prints success message",
outputFormat: "name",
operation: "patched",
expectedOutput: "pod/foo patched",
},
{
name: "valid \"name\" output format and an operation prints success message with dry run",
outputFormat: "name",
operation: "patched",
dryRun: true,
expectedOutput: "pod/foo patched (dry run)",
},
{
name: "operation and no valid \"name\" output does not match a printer",
operation: "patched",
dryRun: true,
expectNoMatch: true,
},
{
name: "no printer is matched on an invalid outputFormat",
outputFormat: "invalid",
expectNoMatch: true,
},
{
name: "printer should not match on any other format supported by another printer",
outputFormat: "go-template",
expectNoMatch: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
printFlags := printers.NamePrintFlags{
Operation: tc.operation,
DryRun: tc.dryRun,
}
p, matched, err := printFlags.ToPrinter(tc.outputFormat)
if tc.expectNoMatch {
if matched {
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
}
return
}
if !matched {
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
}
if len(tc.expectedError) > 0 {
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
out := bytes.NewBuffer([]byte{})
err = p.PrintObj(testObject, out)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !strings.Contains(out.String(), tc.expectedOutput) {
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
}
})
}
}

View File

@ -47,10 +47,16 @@ func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, deco
printer = p
case "name":
printer = &NamePrinter{
Typer: typer,
Decoders: decoders,
nameFlags := NewNamePrintFlags("", false)
namePrinter, matched, err := nameFlags.ToPrinter(format)
if !matched {
return nil, fmt.Errorf("unable to match a name printer to handle current print options")
}
if err != nil {
return nil, err
}
printer = namePrinter
case "template", "go-template":
if len(formatArgument) == 0 {