mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-18 22:14:29 +00:00
Initial commit
Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GetOptionalStringFlag returns the value of the specified flag from a
|
||||
// cobra command, or the zero value ("") if the flag was not specified.
|
||||
func GetOptionalStringFlag(cmd *cobra.Command, flagName string) string {
|
||||
return GetStringFlag(cmd, flagName, false)
|
||||
}
|
||||
|
||||
// GetStringFlag returns the value of the specified flag from a
|
||||
// cobra command. If the flag is not specified and fatalIfMissing is true,
|
||||
// this function logs a fatal error and calls os.Exit(255).
|
||||
func GetStringFlag(cmd *cobra.Command, flagName string, fatalIfMissing bool) string {
|
||||
s, err := cmd.Flags().GetString(flagName)
|
||||
if err != nil && fatalIfMissing {
|
||||
glog.Fatalf("error accessing flag %q for command %s: %v", flagName, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetOptionalBoolFlag returns the value of the specified flag from a
|
||||
// cobra command, or the zero value (false) if the flag was not specified.
|
||||
func GetOptionalBoolFlag(cmd *cobra.Command, flagName string) bool {
|
||||
return GetBoolFlag(cmd, flagName, false)
|
||||
}
|
||||
|
||||
// GetBoolFlag returns the value of the specified flag from a
|
||||
// cobra command. If the flag is not specified and fatalIfMissing is true,
|
||||
// this function logs a fatal error and calls os.Exit(255).
|
||||
func GetBoolFlag(cmd *cobra.Command, flagName string, fatalIfMissing bool) bool {
|
||||
b, err := cmd.Flags().GetBool(flagName)
|
||||
if err != nil && fatalIfMissing {
|
||||
glog.Fatalf("error accessing flag %q for command %s: %v", flagName, cmd.Name(), err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// GetOptionalStringArrayFlag returns the value of the specified flag from a
|
||||
// cobra command, or the zero value if the flag was not specified.
|
||||
func GetOptionalStringArrayFlag(cmd *cobra.Command, flagName string) []string {
|
||||
return GetStringArrayFlag(cmd, flagName, false)
|
||||
}
|
||||
|
||||
// GetStringArrayFlag returns the value of the specified flag from a
|
||||
// cobra command. If the flag is not specified and fatalIfMissing is true,
|
||||
// this function logs a fatal error and calls os.Exit(255).
|
||||
func GetStringArrayFlag(cmd *cobra.Command, flagName string, fatalIfMissing bool) []string {
|
||||
f := cmd.Flag(flagName)
|
||||
if f == nil {
|
||||
if fatalIfMissing {
|
||||
glog.Fatalf("error accessing flag %q for command %s: not specified", flagName, cmd.Name())
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
v := f.Value.(*StringArray)
|
||||
return *v
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringArray is a Cobra-compatible named type for defining a
|
||||
// string slice flag.
|
||||
type StringArray []string
|
||||
|
||||
// NewStringArray returns a StringArray for a provided
|
||||
// slice of values.
|
||||
func NewStringArray(initial ...string) StringArray {
|
||||
return StringArray(initial)
|
||||
}
|
||||
|
||||
// String returns a comma-separated list of the items
|
||||
// in the string array.
|
||||
func (sa *StringArray) String() string {
|
||||
return strings.Join(*sa, ",")
|
||||
}
|
||||
|
||||
// Set comma-splits the provided string and assigns
|
||||
// the results to the receiver. It returns an error if
|
||||
// the string is not parseable.
|
||||
func (sa *StringArray) Set(s string) error {
|
||||
*sa = strings.Split(s, ",")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of the
|
||||
// StringArray type.
|
||||
func (sa *StringArray) Type() string {
|
||||
return "stringArray"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// LabelSelector is a Cobra-compatible wrapper for defining
|
||||
// a Kubernetes label-selector flag.
|
||||
type LabelSelector struct {
|
||||
LabelSelector *metav1.LabelSelector
|
||||
}
|
||||
|
||||
// String returns a string representation of the label
|
||||
// selector flag.
|
||||
func (ls *LabelSelector) String() string {
|
||||
return metav1.FormatLabelSelector(ls.LabelSelector)
|
||||
}
|
||||
|
||||
// Set parses the provided string and assigns the result
|
||||
// to the label-selector receiver. It returns an error if
|
||||
// the string is not parseable.
|
||||
func (ls *LabelSelector) Set(s string) error {
|
||||
parsed, err := metav1.ParseToLabelSelector(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ls.LabelSelector = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of the
|
||||
// LabelSelector type.
|
||||
func (ls *LabelSelector) Type() string {
|
||||
return "labelSelector"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Map is a Cobra-compatible wrapper for defining a flag containing
|
||||
// map data (i.e. a collection of key-value pairs).
|
||||
type Map struct {
|
||||
data map[string]string
|
||||
entryDelimiter string
|
||||
keyValueDelimiter string
|
||||
}
|
||||
|
||||
// NewMap returns a Map using the default delimiters ("=" between keys and
|
||||
// values, and "," between map entries, e.g. k1=v1,k2=v2)
|
||||
func NewMap() Map {
|
||||
m := Map{
|
||||
data: make(map[string]string),
|
||||
}
|
||||
|
||||
return m.WithEntryDelimiter(",").WithKeyValueDelimiter("=")
|
||||
}
|
||||
|
||||
// WithEntryDelimiter sets the delimiter to be used between map
|
||||
// entries.
|
||||
//
|
||||
// For example, in "k1=v1&k2=v2", the entry delimiter is "&"
|
||||
func (m Map) WithEntryDelimiter(delimiter string) Map {
|
||||
m.entryDelimiter = delimiter
|
||||
return m
|
||||
}
|
||||
|
||||
// WithKeyValueDelimiter sets the delimiter to be used between
|
||||
// keys and values.
|
||||
//
|
||||
// For example, in "k1=v1&k2=v2", the key-value delimiter is "="
|
||||
func (m Map) WithKeyValueDelimiter(delimiter string) Map {
|
||||
m.keyValueDelimiter = delimiter
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns a string representation of the Map flag.
|
||||
func (m *Map) String() string {
|
||||
var a []string
|
||||
for k, v := range m.data {
|
||||
a = append(a, fmt.Sprintf("%s%s%s", k, m.keyValueDelimiter, v))
|
||||
}
|
||||
return strings.Join(a, m.entryDelimiter)
|
||||
}
|
||||
|
||||
// Set parses the provided string according to the delimiters and
|
||||
// assigns the result to the Map receiver. It returns an error if
|
||||
// the string is not parseable.
|
||||
func (m *Map) Set(s string) error {
|
||||
for _, part := range strings.Split(s, m.entryDelimiter) {
|
||||
kvs := strings.SplitN(part, m.keyValueDelimiter, 2)
|
||||
if len(kvs) != 2 {
|
||||
return fmt.Errorf("error parsing %q", part)
|
||||
}
|
||||
m.data[kvs[0]] = kvs[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of the
|
||||
// Map type.
|
||||
func (m *Map) Type() string {
|
||||
return "mapStringString"
|
||||
}
|
||||
|
||||
// Data returns the underlying golang map storing
|
||||
// the flag data.
|
||||
func (m *Map) Data() map[string]string {
|
||||
return m.data
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
|
||||
"github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
backupColumns = []string{"NAME", "STATUS", "CREATED", "EXPIRES", "SELECTOR"}
|
||||
)
|
||||
|
||||
func printBackupList(list *v1.BackupList, w io.Writer, options printers.PrintOptions) error {
|
||||
sortBackupsByPrefixAndTimestamp(list)
|
||||
|
||||
for i := range list.Items {
|
||||
if err := printBackup(&list.Items[i], w, options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortBackupsByPrefixAndTimestamp(list *v1.BackupList) {
|
||||
// sort by default alphabetically, but if backups stem from a common schedule
|
||||
// (detected by the presence of a 14-digit timestamp suffix), then within that
|
||||
// group, sort by newest to oldest (i.e. prefix ASC, suffix DESC)
|
||||
timestampSuffix := regexp.MustCompile("-[0-9]{14}$")
|
||||
|
||||
sort.Slice(list.Items, func(i, j int) bool {
|
||||
iSuffixIndex := timestampSuffix.FindStringIndex(list.Items[i].Name)
|
||||
jSuffixIndex := timestampSuffix.FindStringIndex(list.Items[j].Name)
|
||||
|
||||
// one/both don't have a timestamp suffix, so sort alphabetically
|
||||
if iSuffixIndex == nil || jSuffixIndex == nil {
|
||||
return list.Items[i].Name < list.Items[j].Name
|
||||
}
|
||||
|
||||
// different prefixes, so sort alphabetically
|
||||
if list.Items[i].Name[0:iSuffixIndex[0]] != list.Items[j].Name[0:jSuffixIndex[0]] {
|
||||
return list.Items[i].Name < list.Items[j].Name
|
||||
}
|
||||
|
||||
// same prefixes, so sort based on suffix (desc)
|
||||
return list.Items[i].Name[iSuffixIndex[0]:] >= list.Items[j].Name[jSuffixIndex[0]:]
|
||||
})
|
||||
}
|
||||
|
||||
func printBackup(backup *v1.Backup, w io.Writer, options printers.PrintOptions) error {
|
||||
name := printers.FormatResourceName(options.Kind, backup.Name, options.WithKind)
|
||||
|
||||
if options.WithNamespace {
|
||||
if _, err := fmt.Fprintf(w, "%s\t", backup.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
expiration := backup.Status.Expiration.Time
|
||||
if expiration.IsZero() && backup.Spec.TTL.Duration > 0 {
|
||||
expiration = backup.CreationTimestamp.Add(backup.Spec.TTL.Duration)
|
||||
}
|
||||
|
||||
status := backup.Status.Phase
|
||||
if status == "" {
|
||||
status = v1.BackupPhaseNew
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s", name, status, backup.CreationTimestamp.Time, humanReadableTimeFromNow(expiration), metav1.FormatLabelSelector(backup.Spec.LabelSelector)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, printers.AppendLabels(backup.Labels, options.ColumnLabels)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := fmt.Fprint(w, printers.AppendAllLabels(options.ShowLabels, backup.Labels))
|
||||
return err
|
||||
}
|
||||
|
||||
func humanReadableTimeFromNow(when time.Time) string {
|
||||
if when.IsZero() {
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
switch {
|
||||
case when == now || when.After(now):
|
||||
return printers.ShortHumanDuration(when.Sub(now))
|
||||
default:
|
||||
return fmt.Sprintf("%s ago", printers.ShortHumanDuration(now.Sub(when)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 output
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
)
|
||||
|
||||
func TestSortBackups(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
backupList *v1.BackupList
|
||||
expected []v1.Backup
|
||||
}{
|
||||
{
|
||||
name: "non-timestamped backups",
|
||||
backupList: &v1.BackupList{Items: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "a"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "c"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "b"}},
|
||||
}},
|
||||
expected: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "a"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "b"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "c"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timestamped backups",
|
||||
backupList: &v1.BackupList{Items: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030405"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030406"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030407"}},
|
||||
}},
|
||||
expected: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030407"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030406"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030405"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-timestamped and timestamped backups",
|
||||
backupList: &v1.BackupList{Items: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030405"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030406"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "a"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030407"}},
|
||||
}},
|
||||
expected: []v1.Backup{
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "a"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030407"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030406"}},
|
||||
v1.Backup{ObjectMeta: metav1.ObjectMeta{Name: "schedule-20170102030405"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sortBackupsByPrefixAndTimestamp(test.backupList)
|
||||
|
||||
if assert.Equal(t, len(test.backupList.Items), len(test.expected)) {
|
||||
for i := range test.expected {
|
||||
assert.Equal(t, test.expected[i].Name, test.backupList.Items[i].Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
|
||||
"github.com/heptio/ark/pkg/cmd/util/flag"
|
||||
"github.com/heptio/ark/pkg/generated/clientset/scheme"
|
||||
"github.com/heptio/ark/pkg/util/encode"
|
||||
)
|
||||
|
||||
// BindFlags defines a set of output-specific flags within the provided
|
||||
// FlagSet.
|
||||
func BindFlags(flags *pflag.FlagSet) {
|
||||
flags.StringP("output", "o", "table", "Output display format. For create commands, display the object but do not send it to the server. Valid formats are 'table', 'json', and 'yaml'.")
|
||||
labelColumns := flag.NewStringArray()
|
||||
flags.Var(&labelColumns, "label-columns", "a comma-separated list of labels to be displayed as columns")
|
||||
flags.Bool("show-labels", false, "show labels in the last column")
|
||||
}
|
||||
|
||||
// ClearOutputFlagDefault sets the current and default value
|
||||
// of the "output" flag to the empty string.
|
||||
func ClearOutputFlagDefault(cmd *cobra.Command) {
|
||||
f := cmd.Flag("output")
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
f.DefValue = ""
|
||||
f.Value.Set("")
|
||||
}
|
||||
|
||||
// GetOutputFlagValue returns the value of the "output" flag
|
||||
// in the provided command, or the zero value if not present.
|
||||
func GetOutputFlagValue(cmd *cobra.Command) string {
|
||||
return flag.GetOptionalStringFlag(cmd, "output")
|
||||
}
|
||||
|
||||
// GetLabelColumnsValues returns the value of the "label-columns" flag
|
||||
// in the provided command, or the zero value if not present.
|
||||
func GetLabelColumnsValues(cmd *cobra.Command) []string {
|
||||
return flag.GetOptionalStringArrayFlag(cmd, "label-columns")
|
||||
}
|
||||
|
||||
// GetShowLabelsValue returns the value of the "show-labels" flag
|
||||
// in the provided command, or the zero value if not present.
|
||||
func GetShowLabelsValue(cmd *cobra.Command) bool {
|
||||
return flag.GetOptionalBoolFlag(cmd, "show-labels")
|
||||
}
|
||||
|
||||
// ValidateFlags returns an error if any of the output-related flags
|
||||
// were specified with invalid values, or nil otherwise.
|
||||
func ValidateFlags(cmd *cobra.Command) error {
|
||||
if err := validateOutputFlag(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateOutputFlag(cmd *cobra.Command) error {
|
||||
output := GetOutputFlagValue(cmd)
|
||||
switch output {
|
||||
case "", "table", "json", "yaml":
|
||||
default:
|
||||
return fmt.Errorf("invalid output format %q - valid values are 'table', 'json', and 'yaml'", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintWithFormat prints the provided object in the format specified by
|
||||
// the command's flags.
|
||||
func PrintWithFormat(c *cobra.Command, obj runtime.Object) (bool, error) {
|
||||
format := GetOutputFlagValue(c)
|
||||
if format == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "table":
|
||||
return printTable(c, obj)
|
||||
case "json", "yaml":
|
||||
return printEncoded(obj, format)
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("unsupported output format %q; valid values are 'table', 'json', and 'yaml'", format)
|
||||
}
|
||||
|
||||
func printEncoded(obj runtime.Object, format string) (bool, error) {
|
||||
// assume we're printing obj
|
||||
toPrint := obj
|
||||
|
||||
if meta.IsListType(obj) {
|
||||
list, _ := meta.ExtractList(obj)
|
||||
if len(list) == 1 {
|
||||
// if obj was a list and there was only 1 item, just print that 1 instead of a list
|
||||
toPrint = list[0]
|
||||
}
|
||||
}
|
||||
|
||||
encoded, err := encode.Encode(toPrint, format)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
fmt.Println(string(encoded))
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) {
|
||||
printer, err := NewPrinter(cmd)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
printer.Handler(backupColumns, nil, printBackup)
|
||||
printer.Handler(backupColumns, nil, printBackupList)
|
||||
printer.Handler(restoreColumns, nil, printRestore)
|
||||
printer.Handler(restoreColumns, nil, printRestoreList)
|
||||
printer.Handler(scheduleColumns, nil, printSchedule)
|
||||
printer.Handler(scheduleColumns, nil, printScheduleList)
|
||||
|
||||
err = printer.PrintObj(obj, os.Stdout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// NewPrinter returns a printer for doing human-readable table printing of
|
||||
// Ark objects.
|
||||
func NewPrinter(cmd *cobra.Command) (*printers.HumanReadablePrinter, error) {
|
||||
encoder, err := encode.EncoderFor("json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options := printers.PrintOptions{
|
||||
NoHeaders: flag.GetOptionalBoolFlag(cmd, "no-headers"),
|
||||
ShowLabels: GetShowLabelsValue(cmd),
|
||||
ColumnLabels: GetLabelColumnsValues(cmd),
|
||||
}
|
||||
|
||||
printer := printers.NewHumanReadablePrinter(
|
||||
encoder,
|
||||
scheme.Codecs.UniversalDecoder(api.SchemeGroupVersion),
|
||||
options,
|
||||
)
|
||||
|
||||
return printer, nil
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
|
||||
"github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
restoreColumns = []string{"NAME", "BACKUP", "STATUS", "WARNINGS", "ERRORS", "CREATED", "SELECTOR"}
|
||||
)
|
||||
|
||||
func printRestoreList(list *v1.RestoreList, w io.Writer, options printers.PrintOptions) error {
|
||||
for i := range list.Items {
|
||||
if err := printRestore(&list.Items[i], w, options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printRestore(restore *v1.Restore, w io.Writer, options printers.PrintOptions) error {
|
||||
name := printers.FormatResourceName(options.Kind, restore.Name, options.WithKind)
|
||||
|
||||
if options.WithNamespace {
|
||||
if _, err := fmt.Fprintf(w, "%s\t", restore.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
status := restore.Status.Phase
|
||||
if status == "" {
|
||||
status = v1.RestorePhaseNew
|
||||
}
|
||||
|
||||
warnings := len(restore.Status.Warnings.Ark) + len(restore.Status.Warnings.Cluster)
|
||||
for _, w := range restore.Status.Warnings.Namespaces {
|
||||
warnings += len(w)
|
||||
}
|
||||
errors := len(restore.Status.Errors.Ark) + len(restore.Status.Errors.Cluster)
|
||||
for _, e := range restore.Status.Errors.Namespaces {
|
||||
errors += len(e)
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%d\t%s\t%s", name, restore.Spec.BackupName, status, warnings, errors, restore.CreationTimestamp.Time, metav1.FormatLabelSelector(restore.Spec.LabelSelector)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, printers.AppendLabels(restore.Labels, options.ColumnLabels)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := fmt.Fprint(w, printers.AppendAllLabels(options.ShowLabels, restore.Labels))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2017 Heptio Inc.
|
||||
|
||||
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 output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
|
||||
"github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
scheduleColumns = []string{"NAME", "STATUS", "CREATED", "SCHEDULE", "BACKUP TTL", "LAST BACKUP", "SELECTOR"}
|
||||
)
|
||||
|
||||
func printScheduleList(list *v1.ScheduleList, w io.Writer, options printers.PrintOptions) error {
|
||||
for i := range list.Items {
|
||||
if err := printSchedule(&list.Items[i], w, options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printSchedule(schedule *v1.Schedule, w io.Writer, options printers.PrintOptions) error {
|
||||
name := printers.FormatResourceName(options.Kind, schedule.Name, options.WithKind)
|
||||
|
||||
if options.WithNamespace {
|
||||
if _, err := fmt.Fprintf(w, "%s\t", schedule.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
status := schedule.Status.Phase
|
||||
if status == "" {
|
||||
status = v1.SchedulePhaseNew
|
||||
}
|
||||
|
||||
_, err := fmt.Fprintf(
|
||||
w,
|
||||
"%s\t%s\t%s\t%s\t%s\t%s\t%s",
|
||||
name,
|
||||
status,
|
||||
schedule.CreationTimestamp.Time,
|
||||
schedule.Spec.Schedule,
|
||||
schedule.Spec.Template.TTL.Duration,
|
||||
humanReadableTimeFromNow(schedule.Status.LastBackup.Time),
|
||||
metav1.FormatLabelSelector(schedule.Spec.Template.LabelSelector),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, printers.AppendLabels(schedule.Labels, options.ColumnLabels)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprint(w, printers.AppendAllLabels(options.ShowLabels, schedule.Labels))
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user