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 {
humanKind string // the kind being deleted, lowercase and spaced e.g., "image policy"
container objectContainer // for getting the value, and later deleting it
humanKind string // the kind being deleted, lowercase and spaced e.g., "image policy"
adapter adapter // for getting the value, and later deleting it
}
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,
}
err = kubeClient.Get(ctx, namespacedName, del.container.AsClientObject())
err = kubeClient.Get(ctx, namespacedName, del.adapter.asRuntimeObject())
if err != nil {
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)
err = kubeClient.Delete(ctx, del.container.AsClientObject())
err = kubeClient.Delete(ctx, del.adapter.asRuntimeObject())
if err != nil {
return err
}

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

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

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

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

@ -19,7 +19,6 @@ package main
import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
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
`,
RunE: exportCommand{
object: exportableImagePolicy{&imagev1.ImagePolicy{}},
list: exportableImagePolicyList{&imagev1.ImagePolicyList{}},
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run,
}
@ -64,30 +63,10 @@ func exportImagePolicy(item *imagev1.ImagePolicy) interface{} {
return export
}
type exportableImagePolicy struct {
policy *imagev1.ImagePolicy
func (ex imagePolicyAdapter) export() interface{} {
return exportImagePolicy(ex.ImagePolicy)
}
func (ex exportableImagePolicy) AsClientObject() runtime.Object {
return ex.policy
}
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])
func (ex imagePolicyListAdapter) exportItem(i int) interface{} {
return exportImagePolicy(&ex.ImagePolicyList.Items[i])
}

@ -19,7 +19,6 @@ package main
import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
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
`,
RunE: exportCommand{
object: exportableImageRepository{&imagev1.ImageRepository{}},
list: exportableImageRepositoryList{&imagev1.ImageRepositoryList{}},
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run,
}
@ -62,30 +61,10 @@ func exportImageRepository(repo *imagev1.ImageRepository) interface{} {
return export
}
type exportableImageRepository struct {
repo *imagev1.ImageRepository
func (ex imageRepositoryAdapter) export() interface{} {
return exportImageRepository(ex.ImageRepository)
}
func (ex exportableImageRepository) AsClientObject() runtime.Object {
return ex.repo
}
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])
func (ex imageRepositoryListAdapter) exportItem(i int) interface{} {
return exportImageRepository(&ex.ImageRepositoryList.Items[i])
}

@ -19,7 +19,6 @@ package main
import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
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
`,
RunE: exportCommand{
object: exportableImageUpdate{&autov1.ImageUpdateAutomation{}},
list: exportableImageUpdateList{&autov1.ImageUpdateAutomationList{}},
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run,
}
@ -64,30 +63,10 @@ func exportImageUpdate(item *autov1.ImageUpdateAutomation) interface{} {
return export
}
type exportableImageUpdate struct {
update *autov1.ImageUpdateAutomation
func (ex imageUpdateAutomationAdapter) export() interface{} {
return exportImageUpdate(ex.ImageUpdateAutomation)
}
func (ex exportableImageUpdate) AsClientObject() runtime.Object {
return ex.update
}
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])
func (ex imageUpdateAutomationListAdapter) exportItem(i int) interface{} {
return exportImageUpdate(&ex.ImageUpdateAutomationList.Items[i])
}

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

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

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

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

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

Loading…
Cancel
Save