|
| 1 | +package olmv1 |
| 2 | + |
| 3 | +import ( |
| 4 | + "cmp" |
| 5 | + "fmt" |
| 6 | + "os" |
| 7 | + "slices" |
| 8 | + "text/tabwriter" |
| 9 | + "time" |
| 10 | + |
| 11 | + "github.com/blang/semver/v4" |
| 12 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 13 | + "k8s.io/apimachinery/pkg/util/duration" |
| 14 | + |
| 15 | + catalogdv1 "github.com/operator-framework/catalogd/api/v1" |
| 16 | + olmv1 "github.com/operator-framework/operator-controller/api/v1" |
| 17 | +) |
| 18 | + |
| 19 | +func printFormattedOperators(extensions ...olmv1.ClusterExtension) { |
| 20 | + tw := tabwriter.NewWriter(os.Stdout, 3, 4, 2, ' ', 0) |
| 21 | + _, _ = fmt.Fprint(tw, "NAME\tINSTALLED BUNDLE\tVERSION\tSOURCE TYPE\tINSTALLED\tPROGRESSING\tAGE\n") |
| 22 | + |
| 23 | + sortOperators(extensions) |
| 24 | + for _, ext := range extensions { |
| 25 | + age := time.Since(ext.CreationTimestamp.Time) |
| 26 | + _, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", |
| 27 | + ext.Name, |
| 28 | + ext.Status.Install.Bundle.Name, |
| 29 | + ext.Status.Install.Bundle.Version, |
| 30 | + ext.Spec.Source.SourceType, |
| 31 | + status(ext.Status.Conditions, olmv1.TypeInstalled), |
| 32 | + status(ext.Status.Conditions, olmv1.TypeProgressing), |
| 33 | + duration.HumanDuration(age), |
| 34 | + ) |
| 35 | + } |
| 36 | + _ = tw.Flush() |
| 37 | +} |
| 38 | + |
| 39 | +func printFormattedCatalogs(catalogs ...catalogdv1.ClusterCatalog) { |
| 40 | + tw := tabwriter.NewWriter(os.Stdout, 3, 4, 2, ' ', 0) |
| 41 | + _, _ = fmt.Fprint(tw, "NAME\tAVAILABILITY\tPRIORITY\tLASTUNPACKED\tSERVING\tAGE\n") |
| 42 | + |
| 43 | + sortCatalogs(catalogs) |
| 44 | + for _, cat := range catalogs { |
| 45 | + age := time.Since(cat.CreationTimestamp.Time) |
| 46 | + lastUnpacked := time.Since(cat.Status.LastUnpacked.Time) |
| 47 | + _, _ = fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t%s\t%s\n", |
| 48 | + cat.Name, |
| 49 | + string(cat.Spec.AvailabilityMode), |
| 50 | + cat.Spec.Priority, |
| 51 | + duration.HumanDuration(lastUnpacked), |
| 52 | + status(cat.Status.Conditions, catalogdv1.TypeServing), |
| 53 | + duration.HumanDuration(age), |
| 54 | + ) |
| 55 | + } |
| 56 | + _ = tw.Flush() |
| 57 | +} |
| 58 | + |
| 59 | +// sortOperators sorts operators in place and uses the following sorting order: |
| 60 | +// name (asc), version (desc) |
| 61 | +func sortOperators(extensions []olmv1.ClusterExtension) { |
| 62 | + slices.SortFunc(extensions, func(a, b olmv1.ClusterExtension) int { |
| 63 | + return cmp.Or( |
| 64 | + cmp.Compare(a.Name, b.Name), |
| 65 | + -semver.MustParse(a.Status.Install.Bundle.Version).Compare(semver.MustParse(b.Status.Install.Bundle.Version)), |
| 66 | + ) |
| 67 | + }) |
| 68 | +} |
| 69 | + |
| 70 | +// sortCatalogs sorts catalogs in place and uses the following sorting order: |
| 71 | +// availability (asc), priority (desc), name (asc) |
| 72 | +func sortCatalogs(catalogs []catalogdv1.ClusterCatalog) { |
| 73 | + slices.SortFunc(catalogs, func(a, b catalogdv1.ClusterCatalog) int { |
| 74 | + return cmp.Or( |
| 75 | + cmp.Compare(a.Spec.AvailabilityMode, b.Spec.AvailabilityMode), |
| 76 | + -cmp.Compare(a.Spec.Priority, b.Spec.Priority), |
| 77 | + cmp.Compare(a.Name, b.Name), |
| 78 | + ) |
| 79 | + }) |
| 80 | +} |
| 81 | + |
| 82 | +func status(conditions []metav1.Condition, typ string) string { |
| 83 | + for _, condition := range conditions { |
| 84 | + if condition.Type == typ { |
| 85 | + return string(condition.Status) |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + return "Unknown" |
| 90 | +} |
0 commit comments