Centralise adapter types

Since the generic commands tend to share a few of the methods they
need -- at least AsClientObject -- it's worth having just one wrapper
struct for each API type, and adding methods to it where necessary.

For the automation types, I put these in auto.go.

While doing this I also did some tidying:

 - I changed the name of the wrappers to `<type>Adapter`, and the
   generic adapter to `universalAdapter` (it's only needed for delete,
   so far).

 - I de-exported and renamed some interface methods e.g.,
   `exportItem`. They aren't needed outside the package.

Signed-off-by: Michael Bridgen <michael@weave.works>
pull/538/head
Michael Bridgen 4 years ago
parent f316aff2d3
commit 2bb09697ce

@ -0,0 +1,99 @@
/*
Copyright 2020 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 (
"k8s.io/apimachinery/pkg/runtime"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
)
// 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.
// imagev1.ImageRepository
type imageRepositoryAdapter struct {
*imagev1.ImageRepository
}
func (a imageRepositoryAdapter) asRuntimeObject() runtime.Object {
return a.ImageRepository
}
// imagev1.ImageRepositoryList
type imageRepositoryListAdapter struct {
*imagev1.ImageRepositoryList
}
func (a imageRepositoryListAdapter) asRuntimeObject() runtime.Object {
return a.ImageRepositoryList
}
func (a imageRepositoryListAdapter) len() int {
return len(a.ImageRepositoryList.Items)
}
// imagev1.ImagePolicy
type imagePolicyAdapter struct {
*imagev1.ImagePolicy
}
func (a imagePolicyAdapter) asRuntimeObject() runtime.Object {
return a.ImagePolicy
}
// imagev1.ImagePolicyList
type imagePolicyListAdapter struct {
*imagev1.ImagePolicyList
}
func (a imagePolicyListAdapter) asRuntimeObject() runtime.Object {
return a.ImagePolicyList
}
func (a imagePolicyListAdapter) len() int {
return len(a.ImagePolicyList.Items)
}
// autov1.ImageUpdateAutomation
type imageUpdateAutomationAdapter struct {
*autov1.ImageUpdateAutomation
}
func (a imageUpdateAutomationAdapter) asRuntimeObject() runtime.Object {
return a.ImageUpdateAutomation
}
// autov1.ImageUpdateAutomationList
type imageUpdateAutomationListAdapter struct {
*autov1.ImageUpdateAutomationList
}
func (a imageUpdateAutomationListAdapter) asRuntimeObject() runtime.Object {
return a.ImageUpdateAutomationList
}
func (a imageUpdateAutomationListAdapter) len() int {
return len(a.ImageUpdateAutomationList.Items)
}

@ -45,8 +45,8 @@ func init() {
} }
type deleteCommand struct { type deleteCommand struct {
humanKind string // the kind being deleted, lowercase and spaced e.g., "image policy" humanKind string // the kind being deleted, lowercase and spaced e.g., "image policy"
container objectContainer // for getting the value, and later deleting it adapter adapter // for getting the value, and later deleting it
} }
func (del deleteCommand) run(cmd *cobra.Command, args []string) error { func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
@ -68,7 +68,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, del.container.AsClientObject()) err = kubeClient.Get(ctx, namespacedName, del.adapter.asRuntimeObject())
if err != nil { if err != nil {
return err return err
} }
@ -84,7 +84,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
} }
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, namespace) logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, namespace)
err = kubeClient.Delete(ctx, del.container.AsClientObject()) err = kubeClient.Delete(ctx, del.adapter.asRuntimeObject())
if err != nil { if err != nil {
return err return err
} }

@ -31,7 +31,7 @@ var deleteImagePolicyCmd = &cobra.Command{
`, `,
RunE: deleteCommand{ RunE: deleteCommand{
humanKind: "image policy", humanKind: "image policy",
container: genericContainer{&imagev1.ImagePolicy{}}, adapter: universalAdapter{&imagev1.ImagePolicy{}},
}.run, }.run,
} }

@ -31,7 +31,7 @@ var deleteImageRepositoryCmd = &cobra.Command{
`, `,
RunE: deleteCommand{ RunE: deleteCommand{
humanKind: "image repository", humanKind: "image repository",
container: genericContainer{&imagev1.ImageRepository{}}, adapter: universalAdapter{&imagev1.ImageRepository{}},
}.run, }.run,
} }

@ -31,7 +31,7 @@ var deleteImageUpdateCmd = &cobra.Command{
`, `,
RunE: deleteCommand{ RunE: deleteCommand{
humanKind: "image update automation", humanKind: "image update automation",
container: genericContainer{&autov1.ImageUpdateAutomation{}}, adapter: universalAdapter{&autov1.ImageUpdateAutomation{}},
}.run, }.run,
} }

@ -48,21 +48,21 @@ func init() {
// exportable represents a type that you can fetch from the Kubernetes // exportable represents a type that you can fetch from the Kubernetes
// API, then tidy up for serialising. // API, then tidy up for serialising.
type exportable interface { type exportable interface {
objectContainer adapter
Export() interface{} export() interface{}
} }
// exportableAt represents a type that has a list of values, each of // exportableList represents a type that has a list of values, each of
// which is exportable. // which is exportable.
type exportableAt interface { type exportableList interface {
objectContainer adapter
Len() int len() int
ExportAt(i int) interface{} exportItem(i int) interface{}
} }
type exportCommand struct { type exportCommand struct {
object exportable object exportable
list exportableAt list exportableList
} }
func (export exportCommand) run(cmd *cobra.Command, args []string) error { func (export exportCommand) run(cmd *cobra.Command, args []string) error {
@ -79,18 +79,18 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
} }
if exportAll { if exportAll {
err = kubeClient.List(ctx, export.list.AsClientObject(), client.InNamespace(namespace)) err = kubeClient.List(ctx, export.list.asRuntimeObject(), client.InNamespace(namespace))
if err != nil { if err != nil {
return err return err
} }
if export.list.Len() == 0 { if export.list.len() == 0 {
logger.Failuref("no objects found in %s namespace", namespace) logger.Failuref("no objects found in %s namespace", namespace)
return nil return nil
} }
for i := 0; i < export.list.Len(); i++ { for i := 0; i < export.list.len(); i++ {
if err = printExport(export.list.ExportAt(i)); err != nil { if err = printExport(export.list.exportItem(i)); err != nil {
return err return err
} }
} }
@ -100,11 +100,11 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, export.object.AsClientObject()) err = kubeClient.Get(ctx, namespacedName, export.object.asRuntimeObject())
if err != nil { if err != nil {
return err return err
} }
return printExport(export.object.Export()) return printExport(export.object.export())
} }
return nil return nil
} }

@ -19,7 +19,6 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
) )
@ -35,8 +34,8 @@ var exportImagePolicyCmd = &cobra.Command{
flux export auto image-policy alpine1x > alpine1x.yaml flux export auto image-policy alpine1x > alpine1x.yaml
`, `,
RunE: exportCommand{ RunE: exportCommand{
object: exportableImagePolicy{&imagev1.ImagePolicy{}}, object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
list: exportableImagePolicyList{&imagev1.ImagePolicyList{}}, list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run, }.run,
} }
@ -64,30 +63,10 @@ func exportImagePolicy(item *imagev1.ImagePolicy) interface{} {
return export return export
} }
type exportableImagePolicy struct { func (ex imagePolicyAdapter) export() interface{} {
policy *imagev1.ImagePolicy return exportImagePolicy(ex.ImagePolicy)
} }
func (ex exportableImagePolicy) AsClientObject() runtime.Object { func (ex imagePolicyListAdapter) exportItem(i int) interface{} {
return ex.policy return exportImagePolicy(&ex.ImagePolicyList.Items[i])
}
func (ex exportableImagePolicy) Export() interface{} {
return exportImagePolicy(ex.policy)
}
type exportableImagePolicyList struct {
list *imagev1.ImagePolicyList
}
func (ex exportableImagePolicyList) AsClientObject() runtime.Object {
return ex.list
}
func (ex exportableImagePolicyList) Len() int {
return len(ex.list.Items)
}
func (ex exportableImagePolicyList) ExportAt(i int) interface{} {
return exportImagePolicy(&ex.list.Items[i])
} }

@ -19,7 +19,6 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
) )
@ -35,8 +34,8 @@ var exportImageRepositoryCmd = &cobra.Command{
flux export auto image-repository alpine > alpine.yaml flux export auto image-repository alpine > alpine.yaml
`, `,
RunE: exportCommand{ RunE: exportCommand{
object: exportableImageRepository{&imagev1.ImageRepository{}}, object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
list: exportableImageRepositoryList{&imagev1.ImageRepositoryList{}}, list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run, }.run,
} }
@ -62,30 +61,10 @@ func exportImageRepository(repo *imagev1.ImageRepository) interface{} {
return export return export
} }
type exportableImageRepository struct { func (ex imageRepositoryAdapter) export() interface{} {
repo *imagev1.ImageRepository return exportImageRepository(ex.ImageRepository)
} }
func (ex exportableImageRepository) AsClientObject() runtime.Object { func (ex imageRepositoryListAdapter) exportItem(i int) interface{} {
return ex.repo return exportImageRepository(&ex.ImageRepositoryList.Items[i])
}
func (ex exportableImageRepository) Export() interface{} {
return exportImageRepository(ex.repo)
}
type exportableImageRepositoryList struct {
list *imagev1.ImageRepositoryList
}
func (ex exportableImageRepositoryList) AsClientObject() runtime.Object {
return ex.list
}
func (ex exportableImageRepositoryList) Len() int {
return len(ex.list.Items)
}
func (ex exportableImageRepositoryList) ExportAt(i int) interface{} {
return exportImageRepository(&ex.list.Items[i])
} }

@ -19,7 +19,6 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
) )
@ -35,8 +34,8 @@ var exportImageUpdateCmd = &cobra.Command{
flux export auto image-update latest-images > latest.yaml flux export auto image-update latest-images > latest.yaml
`, `,
RunE: exportCommand{ RunE: exportCommand{
object: exportableImageUpdate{&autov1.ImageUpdateAutomation{}}, object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
list: exportableImageUpdateList{&autov1.ImageUpdateAutomationList{}}, list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run, }.run,
} }
@ -64,30 +63,10 @@ func exportImageUpdate(item *autov1.ImageUpdateAutomation) interface{} {
return export return export
} }
type exportableImageUpdate struct { func (ex imageUpdateAutomationAdapter) export() interface{} {
update *autov1.ImageUpdateAutomation return exportImageUpdate(ex.ImageUpdateAutomation)
} }
func (ex exportableImageUpdate) AsClientObject() runtime.Object { func (ex imageUpdateAutomationListAdapter) exportItem(i int) interface{} {
return ex.update return exportImageUpdate(&ex.ImageUpdateAutomationList.Items[i])
}
func (ex exportableImageUpdate) Export() interface{} {
return exportImageUpdate(ex.update)
}
type exportableImageUpdateList struct {
list *autov1.ImageUpdateAutomationList
}
func (ex exportableImageUpdateList) AsClientObject() runtime.Object {
return ex.list
}
func (ex exportableImageUpdateList) Len() int {
return len(ex.list.Items)
}
func (ex exportableImageUpdateList) ExportAt(i int) interface{} {
return exportImageUpdate(&ex.list.Items[i])
} }

@ -45,10 +45,10 @@ func init() {
} }
type summarisable interface { type summarisable interface {
objectContainer adapter
Len() int len() int
SummariseAt(i int, includeNamespace bool) []string summariseItem(i int, includeNamespace bool) []string
Headers(includeNamespace bool) []string headers(includeNamespace bool) []string
} }
// --- these help with implementations of summarisable // --- these help with implementations of summarisable
@ -92,20 +92,20 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
if !allNamespaces { if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace)) listOpts = append(listOpts, client.InNamespace(namespace))
} }
err = kubeClient.List(ctx, get.list.AsClientObject(), listOpts...) err = kubeClient.List(ctx, get.list.asRuntimeObject(), listOpts...)
if err != nil { if err != nil {
return err return err
} }
if get.list.Len() == 0 { if get.list.len() == 0 {
logger.Failuref("no imagerepository objects found in %s namespace", namespace) logger.Failuref("no imagerepository objects found in %s namespace", namespace)
return nil return nil
} }
header := get.list.Headers(allNamespaces) header := get.list.headers(allNamespaces)
var rows [][]string var rows [][]string
for i := 0; i < get.list.Len(); i++ { for i := 0; i < get.list.len(); i++ {
row := get.list.SummariseAt(i, allNamespaces) row := get.list.summariseItem(i, allNamespaces)
rows = append(rows, row) rows = append(rows, row)
} }
utils.PrintTable(os.Stdout, header, rows) utils.PrintTable(os.Stdout, header, rows)

@ -18,7 +18,6 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
) )
@ -34,7 +33,7 @@ var getImagePolicyCmd = &cobra.Command{
flux get auto image-policy --all-namespaces flux get auto image-policy --all-namespaces
`, `,
RunE: getCommand{ RunE: getCommand{
list: &imagePolicySummary{&imagev1.ImagePolicyList{}}, list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run, }.run,
} }
@ -42,28 +41,16 @@ func init() {
getAutoCmd.AddCommand(getImagePolicyCmd) getAutoCmd.AddCommand(getImagePolicyCmd)
} }
type imagePolicySummary struct { func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool) []string {
*imagev1.ImagePolicyList
}
func (s imagePolicySummary) Len() int {
return len(s.Items)
}
func (s imagePolicySummary) SummariseAt(i int, includeNamespace bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), status, msg, item.Status.LatestImage) return append(nameColumns(&item, includeNamespace), status, msg, item.Status.LatestImage)
} }
func (s imagePolicySummary) Headers(includeNamespace bool) []string { func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Latest image"} headers := []string{"Name", "Ready", "Message", "Latest image"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }
return headers return headers
} }
func (s imagePolicySummary) AsClientObject() runtime.Object {
return s.ImagePolicyList
}

@ -22,7 +22,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
) )
@ -38,7 +37,7 @@ var getImageRepositoryCmd = &cobra.Command{
flux get auto image-repository --all-namespaces flux get auto image-repository --all-namespaces
`, `,
RunE: getCommand{ RunE: getCommand{
list: imageRepositorySummary{&imagev1.ImageRepositoryList{}}, list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run, }.run,
} }
@ -46,15 +45,7 @@ func init() {
getAutoCmd.AddCommand(getImageRepositoryCmd) getAutoCmd.AddCommand(getImageRepositoryCmd)
} }
type imageRepositorySummary struct { func (s imageRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string {
*imagev1.ImageRepositoryList
}
func (s imageRepositorySummary) Len() int {
return len(s.Items)
}
func (s imageRepositorySummary) SummariseAt(i int, includeNamespace bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
var lastScan string var lastScan string
@ -65,14 +56,10 @@ func (s imageRepositorySummary) SummariseAt(i int, includeNamespace bool) []stri
status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s imageRepositorySummary) Headers(includeNamespace bool) []string { func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Last scan", "Suspended"} headers := []string{"Name", "Ready", "Message", "Last scan", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }
return headers return headers
} }
func (s imageRepositorySummary) AsClientObject() runtime.Object {
return s.ImageRepositoryList
}

@ -22,7 +22,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
) )
@ -38,7 +37,7 @@ var getImageUpdateCmd = &cobra.Command{
flux get auto image-update --all-namespaces flux get auto image-update --all-namespaces
`, `,
RunE: getCommand{ RunE: getCommand{
list: &imageUpdateSummary{&autov1.ImageUpdateAutomationList{}}, list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run, }.run,
} }
@ -46,15 +45,7 @@ func init() {
getAutoCmd.AddCommand(getImageUpdateCmd) getAutoCmd.AddCommand(getImageUpdateCmd)
} }
type imageUpdateSummary struct { func (s imageUpdateAutomationListAdapter) summariseItem(i int, includeNamespace bool) []string {
*autov1.ImageUpdateAutomationList
}
func (s imageUpdateSummary) Len() int {
return len(s.Items)
}
func (s imageUpdateSummary) SummariseAt(i int, includeNamespace bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
var lastRun string var lastRun string
@ -64,14 +55,10 @@ func (s imageUpdateSummary) SummariseAt(i int, includeNamespace bool) []string {
return append(nameColumns(&item, includeNamespace), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend))) return append(nameColumns(&item, includeNamespace), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s imageUpdateSummary) Headers(includeNamespace bool) []string { func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Last run", "Suspended"} headers := []string{"Name", "Ready", "Message", "Last run", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }
return headers return headers
} }
func (s imageUpdateSummary) AsClientObject() runtime.Object {
return s.ImageUpdateAutomationList
}

@ -20,21 +20,21 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
) )
// objectContainer is an interface for a wrapper or alias from which we // adapter is an interface for a wrapper or alias from which we can
// can get a controller-runtime deserialisable value. This is used so // get a controller-runtime deserialisable value. This is used so that
// that you can wrap an API type to give it other useful methods, but // you can wrap an API type to give it other useful methods, but still
// still use values of the wrapper with `client.Client`, which only // use values of the wrapper with `client.Client`, which only deals
// deals with types that have been added to the schema. // with types that have been added to the schema.
type objectContainer interface { type adapter interface {
AsClientObject() runtime.Object asRuntimeObject() runtime.Object
} }
// genericContainer is an objectContainer for any runtime.Object. Use // universalAdapter is an adapter for any runtime.Object. Use this if
// this if there are no other methods needed. // there are no other methods needed.
type genericContainer struct { type universalAdapter struct {
obj runtime.Object obj runtime.Object
} }
func (c genericContainer) AsClientObject() runtime.Object { func (c universalAdapter) asRuntimeObject() runtime.Object {
return c.obj return c.obj
} }

Loading…
Cancel
Save