1
0
mirror of synced 2026-02-06 19:05:55 +00:00

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>
This commit is contained in:
Michael Bridgen
2020-12-07 17:30:41 +00:00
parent f316aff2d3
commit 2bb09697ce
14 changed files with 167 additions and 170 deletions

99
cmd/flux/auto.go Normal file
View File

@@ -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)
}

View File

@@ -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
}

View File

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

View File

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

View File

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

View File

@@ -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
}

View File

@@ -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])
}

View File

@@ -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])
}

View File

@@ -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])
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}