diff --git a/cmd/flux/get_source_bucket.go b/cmd/flux/get_source_bucket.go index 8c39c221..69a97039 100644 --- a/cmd/flux/get_source_bucket.go +++ b/cmd/flux/get_source_bucket.go @@ -17,19 +17,11 @@ limitations under the License. package main import ( - "context" - "os" + sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "strconv" "strings" - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "github.com/spf13/cobra" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) var getSourceBucketCmd = &cobra.Command{ @@ -42,70 +34,31 @@ var getSourceBucketCmd = &cobra.Command{ # List buckets from all namespaces flux get sources helm --all-namespaces `, - RunE: getSourceBucketCmdRun, + RunE: getCommand{ + apiType: bucketType, + list: &bucketListAdapter{&sourcev1.BucketList{}}, + }.run, } func init() { getSourceCmd.AddCommand(getSourceBucketCmd) } -func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return err - } - - var listOpts []client.ListOption - if !getArgs.allNamespaces { - listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) - } - var list sourcev1.BucketList - err = kubeClient.List(ctx, &list, listOpts...) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no bucket sources found in %s namespace", rootArgs.namespace) - return nil +func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool) []string { + item := a.Items[i] + var revision string + if item.GetArtifact() != nil { + revision = item.GetArtifact().Revision } + status, msg := statusAndMessage(item.Status.Conditions) + return append(nameColumns(&item, includeNamespace), + status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) +} - header := []string{"Name", "Ready", "Message", "Revision", "Suspended"} - if getArgs.allNamespaces { - header = append([]string{"Namespace"}, header...) - } - var rows [][]string - for _, source := range list.Items { - var row []string - var revision string - if source.GetArtifact() != nil { - revision = source.GetArtifact().Revision - } - if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil { - row = []string{ - source.GetName(), - string(c.Status), - c.Message, - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } else { - row = []string{ - source.GetName(), - string(metav1.ConditionFalse), - "waiting to be reconciled", - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } - if getArgs.allNamespaces { - row = append([]string{source.Namespace}, row...) - } - rows = append(rows, row) +func (a bucketListAdapter) headers(includeNamespace bool) []string { + headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"} + if includeNamespace { + headers = append([]string{"Namespace"}, headers...) } - utils.PrintTable(os.Stdout, header, rows) - return nil + return headers } diff --git a/cmd/flux/get_source_chart.go b/cmd/flux/get_source_chart.go index 8f56fdc8..d46cafd6 100644 --- a/cmd/flux/get_source_chart.go +++ b/cmd/flux/get_source_chart.go @@ -17,19 +17,11 @@ limitations under the License. package main import ( - "context" - "os" "strconv" "strings" - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "github.com/spf13/cobra" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) var getSourceHelmChartCmd = &cobra.Command{ @@ -42,70 +34,31 @@ var getSourceHelmChartCmd = &cobra.Command{ # List Helm charts from all namespaces flux get sources chart --all-namespaces `, - RunE: getSourceHelmChartCmdRun, + RunE: getCommand{ + apiType: bucketType, + list: &helmChartListAdapter{&sourcev1.HelmChartList{}}, + }.run, } func init() { getSourceCmd.AddCommand(getSourceHelmChartCmd) } -func getSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return err - } - - var listOpts []client.ListOption - if !getArgs.allNamespaces { - listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) - } - var list sourcev1.HelmChartList - err = kubeClient.List(ctx, &list, listOpts...) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no chart sources found in %s namespace", rootArgs.namespace) - return nil +func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool) []string { + item := a.Items[i] + var revision string + if item.GetArtifact() != nil { + revision = item.GetArtifact().Revision } + status, msg := statusAndMessage(item.Status.Conditions) + return append(nameColumns(&item, includeNamespace), + status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) +} - header := []string{"Name", "Ready", "Message", "Revision", "Suspended"} - if getArgs.allNamespaces { - header = append([]string{"Namespace"}, header...) - } - var rows [][]string - for _, source := range list.Items { - var row []string - var revision string - if source.GetArtifact() != nil { - revision = source.GetArtifact().Revision - } - if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil { - row = []string{ - source.GetName(), - string(c.Status), - c.Message, - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } else { - row = []string{ - source.GetName(), - string(metav1.ConditionFalse), - "waiting to be reconciled", - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } - if getArgs.allNamespaces { - row = append([]string{source.Namespace}, row...) - } - rows = append(rows, row) +func (a helmChartListAdapter) headers(includeNamespace bool) []string { + headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"} + if includeNamespace { + headers = append([]string{"Namespace"}, headers...) } - utils.PrintTable(os.Stdout, header, rows) - return nil + return headers } diff --git a/cmd/flux/get_source_git.go b/cmd/flux/get_source_git.go index 9d629671..b4dfdea3 100644 --- a/cmd/flux/get_source_git.go +++ b/cmd/flux/get_source_git.go @@ -17,19 +17,11 @@ limitations under the License. package main import ( - "context" - "os" "strconv" "strings" - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "github.com/spf13/cobra" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) var getSourceGitCmd = &cobra.Command{ @@ -42,70 +34,31 @@ var getSourceGitCmd = &cobra.Command{ # List Git repositories from all namespaces flux get sources git --all-namespaces `, - RunE: getSourceGitCmdRun, + RunE: getCommand{ + apiType: bucketType, + list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}}, + }.run, } func init() { getSourceCmd.AddCommand(getSourceGitCmd) } -func getSourceGitCmdRun(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return err - } - - var listOpts []client.ListOption - if !getArgs.allNamespaces { - listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) - } - var list sourcev1.GitRepositoryList - err = kubeClient.List(ctx, &list, listOpts...) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no git sources found in %s namespace", rootArgs.namespace) - return nil +func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string { + item := a.Items[i] + var revision string + if item.GetArtifact() != nil { + revision = item.GetArtifact().Revision } + status, msg := statusAndMessage(item.Status.Conditions) + return append(nameColumns(&item, includeNamespace), + status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) +} - header := []string{"Name", "Ready", "Message", "Revision", "Suspended"} - if getArgs.allNamespaces { - header = append([]string{"Namespace"}, header...) - } - var rows [][]string - for _, source := range list.Items { - var row []string - var revision string - if source.GetArtifact() != nil { - revision = source.GetArtifact().Revision - } - if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil { - row = []string{ - source.GetName(), - string(c.Status), - c.Message, - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } else { - row = []string{ - source.GetName(), - string(metav1.ConditionFalse), - "waiting to be reconciled", - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } - if getArgs.allNamespaces { - row = append([]string{source.Namespace}, row...) - } - rows = append(rows, row) +func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string { + headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"} + if includeNamespace { + headers = append([]string{"Namespace"}, headers...) } - utils.PrintTable(os.Stdout, header, rows) - return nil + return headers } diff --git a/cmd/flux/get_source_helm.go b/cmd/flux/get_source_helm.go index 9deaa271..f4602b7b 100644 --- a/cmd/flux/get_source_helm.go +++ b/cmd/flux/get_source_helm.go @@ -17,19 +17,11 @@ limitations under the License. package main import ( - "context" - "os" "strconv" "strings" - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "github.com/spf13/cobra" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) var getSourceHelmCmd = &cobra.Command{ @@ -42,70 +34,31 @@ var getSourceHelmCmd = &cobra.Command{ # List Helm repositories from all namespaces flux get sources helm --all-namespaces `, - RunE: getSourceHelmCmdRun, + RunE: getCommand{ + apiType: bucketType, + list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}}, + }.run, } func init() { getSourceCmd.AddCommand(getSourceHelmCmd) } -func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return err - } - - var listOpts []client.ListOption - if !getArgs.allNamespaces { - listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) - } - var list sourcev1.HelmRepositoryList - err = kubeClient.List(ctx, &list, listOpts...) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no helm sources found in %s namespace", rootArgs.namespace) - return nil +func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string { + item := a.Items[i] + var revision string + if item.GetArtifact() != nil { + revision = item.GetArtifact().Revision } + status, msg := statusAndMessage(item.Status.Conditions) + return append(nameColumns(&item, includeNamespace), + status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) +} - header := []string{"Name", "Ready", "Message", "Revision", "Suspended"} - if getArgs.allNamespaces { - header = append([]string{"Namespace"}, header...) - } - var rows [][]string - for _, source := range list.Items { - var row []string - var revision string - if source.GetArtifact() != nil { - revision = source.GetArtifact().Revision - } - if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil { - row = []string{ - source.GetName(), - string(c.Status), - c.Message, - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } else { - row = []string{ - source.GetName(), - string(metav1.ConditionFalse), - "waiting to be reconciled", - revision, - strings.Title(strconv.FormatBool(source.Spec.Suspend)), - } - } - if getArgs.allNamespaces { - row = append([]string{source.Namespace}, row...) - } - rows = append(rows, row) +func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string { + headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"} + if includeNamespace { + headers = append([]string{"Namespace"}, headers...) } - utils.PrintTable(os.Stdout, header, rows) - return nil + return headers } diff --git a/cmd/flux/source.go b/cmd/flux/source.go new file mode 100644 index 00000000..411282b7 --- /dev/null +++ b/cmd/flux/source.go @@ -0,0 +1,143 @@ +/* +Copyright 2021 The Flux 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 main + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" +) + +// These are general-purpose adapters for attaching methods to, for +// the various commands. The *List adapters implement len(), since +// it's used in at least a couple of commands. + +// sourcev1.Bucket + +var bucketType = apiType{ + kind: sourcev1.BucketKind, + humanKind: "source bucket", +} + +type bucketAdapter struct { + *sourcev1.Bucket +} + +func (a bucketAdapter) asClientObject() client.Object { + return a.Bucket +} + +// sourcev1.BucketList + +type bucketListAdapter struct { + *sourcev1.BucketList +} + +func (a bucketListAdapter) asClientList() client.ObjectList { + return a.BucketList +} + +func (a bucketListAdapter) len() int { + return len(a.BucketList.Items) +} + +// sourcev1.HelmChart + +var helmChartType = apiType{ + kind: sourcev1.HelmChartKind, + humanKind: "source chart", +} + +type helmChartAdapter struct { + *sourcev1.HelmChart +} + +func (a helmChartAdapter) asClientObject() client.Object { + return a.HelmChart +} + +// sourcev1.ImagePolicyList + +type helmChartListAdapter struct { + *sourcev1.HelmChartList +} + +func (a helmChartListAdapter) asClientList() client.ObjectList { + return a.HelmChartList +} + +func (a helmChartListAdapter) len() int { + return len(a.HelmChartList.Items) +} + +// sourcev1.GitRepository + +var gitRepositoryType = apiType{ + kind: sourcev1.GitRepositoryKind, + humanKind: "source git", +} + +type gitRepositoryAdapter struct { + *sourcev1.GitRepository +} + +func (a gitRepositoryAdapter) asClientObject() client.Object { + return a.GitRepository +} + +// sourcev1.GitRepositoryList + +type gitRepositoryListAdapter struct { + *sourcev1.GitRepositoryList +} + +func (a gitRepositoryListAdapter) asClientList() client.ObjectList { + return a.GitRepositoryList +} + +func (a gitRepositoryListAdapter) len() int { + return len(a.GitRepositoryList.Items) +} + +// sourcev1.HelmRepository + +var helmRepositoryType = apiType{ + kind: sourcev1.HelmRepositoryKind, + humanKind: "source helm", +} + +type helmRepositoryAdapter struct { + *sourcev1.HelmRepository +} + +func (a helmRepositoryAdapter) asClientObject() client.Object { + return a.HelmRepository +} + +// sourcev1.HelmRepositoryList + +type helmRepositoryListAdapter struct { + *sourcev1.HelmRepositoryList +} + +func (a helmRepositoryListAdapter) asClientList() client.ObjectList { + return a.HelmRepositoryList +} + +func (a helmRepositoryListAdapter) len() int { + return len(a.HelmRepositoryList.Items) +}