Initial commit

Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
This commit is contained in:
Andy Goldstein
2017-08-02 13:27:17 -04:00
commit 2fe501f527
2024 changed files with 948288 additions and 0 deletions
+77
View File
@@ -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
}
+51
View File
@@ -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"
}
+51
View File
@@ -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"
}
+93
View File
@@ -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
}
+115
View File
@@ -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)
}
}
})
}
}
+175
View File
@@ -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
}
+74
View File
@@ -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
}
+78
View File
@@ -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
}