Skip to content

Commit 076a742

Browse files
committed
fix empty action config client and update catalog creation unit test
Signed-off-by: Ankita Thomas <[email protected]>
1 parent 6be5255 commit 076a742

File tree

7 files changed

+145
-53
lines changed

7 files changed

+145
-53
lines changed

internal/cmd/internal/olmv1/catalog_create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
// NewCatalogCreateCmd allows creating a new catalog
1515
func NewCatalogCreateCmd(cfg *action.Configuration) *cobra.Command {
16-
i := v1action.NewCatalogCreate(cfg.Client)
16+
i := v1action.NewCatalogCreate(cfg)
1717
i.Logf = log.Printf
1818

1919
cmd := &cobra.Command{

internal/cmd/internal/olmv1/printing.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ func printFormattedOperators(extensions ...olmv1.ClusterExtension) {
2121

2222
sortOperators(extensions)
2323
for _, ext := range extensions {
24+
var bundleName, bundleVersion string
25+
if ext.Status.Install != nil {
26+
bundleName = ext.Status.Install.Bundle.Name
27+
bundleVersion = ext.Status.Install.Bundle.Version
28+
}
2429
age := time.Since(ext.CreationTimestamp.Time)
2530
_, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
2631
ext.Name,
27-
ext.Status.Install.Bundle.Name,
28-
ext.Status.Install.Bundle.Version,
32+
bundleName,
33+
bundleVersion,
2934
ext.Spec.Source.SourceType,
3035
status(ext.Status.Conditions, olmv1.TypeInstalled),
3136
status(ext.Status.Conditions, olmv1.TypeProgressing),
@@ -41,13 +46,16 @@ func printFormattedCatalogs(catalogs ...olmv1.ClusterCatalog) {
4146

4247
sortCatalogs(catalogs)
4348
for _, cat := range catalogs {
49+
var lastUnpacked string
50+
if cat.Status.LastUnpacked != nil {
51+
duration.HumanDuration(time.Since(cat.Status.LastUnpacked.Time))
52+
}
4453
age := time.Since(cat.CreationTimestamp.Time)
45-
lastUnpacked := time.Since(cat.Status.LastUnpacked.Time)
4654
_, _ = fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t%s\t%s\n",
4755
cat.Name,
4856
string(cat.Spec.AvailabilityMode),
4957
cat.Spec.Priority,
50-
duration.HumanDuration(lastUnpacked),
58+
lastUnpacked,
5159
status(cat.Status.Conditions, olmv1.TypeServing),
5260
duration.HumanDuration(age),
5361
)
@@ -59,6 +67,9 @@ func printFormattedCatalogs(catalogs ...olmv1.ClusterCatalog) {
5967
// name (asc), version (desc)
6068
func sortOperators(extensions []olmv1.ClusterExtension) {
6169
slices.SortFunc(extensions, func(a, b olmv1.ClusterExtension) int {
70+
if a.Status.Install == nil || b.Status.Install == nil {
71+
return cmp.Compare(a.Name, b.Name)
72+
}
6273
return cmp.Or(
6374
cmp.Compare(a.Name, b.Name),
6475
-semver.MustParse(a.Status.Install.Bundle.Version).Compare(semver.MustParse(b.Status.Install.Bundle.Version)),

internal/cmd/olmv1.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cmd
22

33
import (
4+
"fmt"
5+
46
"github.com/spf13/cobra"
57

68
"github.com/operator-framework/kubectl-operator/internal/cmd/internal/olmv1"
@@ -29,6 +31,7 @@ func newOlmV1Cmd(cfg *action.Configuration) *cobra.Command {
2931
Short: "Create a resource",
3032
Long: "Create a resource",
3133
}
34+
fmt.Println("u wot m8? ", cfg)
3235
createCmd.AddCommand(olmv1.NewCatalogCreateCmd(cfg))
3336

3437
deleteCmd := &cobra.Command{

internal/cmd/root.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"context"
5+
"fmt"
56
"time"
67

78
"github.com/spf13/cobra"
@@ -38,12 +39,14 @@ operators from the installed catalogs.`,
3839
flags.DurationVar(&timeout, "timeout", 1*time.Minute, "The amount of time to wait before giving up on an operation.")
3940

4041
cmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
42+
defer fmt.Println("Initializing now~ uwu", cfg)
4143
var ctx context.Context
4244
ctx, cancel = context.WithTimeout(cmd.Context(), timeout)
4345

4446
cmd.SetContext(ctx)
4547

46-
return cfg.Load()
48+
fmt.Println("welp, might as well try...", cfg.Load())
49+
return nil
4750
}
4851
cmd.PersistentPostRun = func(command *cobra.Command, _ []string) {
4952
cancel()

internal/pkg/v1/action/action_suite_test.go

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,85 @@ import (
77

88
. "github.com/onsi/ginkgo"
99
. "github.com/onsi/gomega"
10+
"github.com/operator-framework/kubectl-operator/pkg/action"
11+
"k8s.io/apimachinery/pkg/types"
12+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
13+
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
1014

1115
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1216
"sigs.k8s.io/controller-runtime/pkg/client"
1317

1418
olmv1 "github.com/operator-framework/operator-controller/api/v1"
1519
)
1620

21+
const (
22+
verbCreate = "create"
23+
)
24+
1725
func TestCommand(t *testing.T) {
1826
RegisterFailHandler(Fail)
1927
RunSpecs(t, "Internal v1 action Suite")
2028
}
2129

22-
type mockCreator struct {
30+
type fakeClient struct {
2331
createErr error
24-
createCalled int
25-
}
26-
27-
func (mc *mockCreator) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
28-
mc.createCalled++
29-
return mc.createErr
30-
}
31-
32-
type mockDeleter struct {
3332
deleteErr error
33+
getErr error
34+
createCalled int
3435
deleteCalled int
36+
getCalled int
37+
transformers []objectTransformer
38+
client.Client
3539
}
3640

37-
func (md *mockDeleter) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
38-
md.deleteCalled++
39-
return md.deleteErr
41+
// meant to apply a function to anything matching the objectKey
42+
// when the fakeClient an action corresponding to verb.
43+
// transformer will not run if an error is already specified
44+
// for the verb in the fakeClient.
45+
// only implemented in Create for now
46+
type objectTransformer struct {
47+
verb string
48+
objectKey client.ObjectKey
49+
transformFunc func(obj *client.Object)
4050
}
4151

42-
type mockGetter struct {
43-
getErr error
44-
getCalled int
45-
}
46-
47-
func (mg *mockGetter) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
48-
mg.getCalled++
49-
return mg.getErr
52+
func (c *fakeClient) Initialize() error {
53+
scheme, err := action.NewScheme()
54+
if err != nil {
55+
return err
56+
}
57+
clientBuilder := fake.NewClientBuilder().WithInterceptorFuncs(interceptor.Funcs{
58+
Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
59+
c.createCalled++
60+
if c.createErr != nil {
61+
return c.createErr
62+
}
63+
objKey := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}
64+
for _, t := range c.transformers {
65+
if t.verb == verbCreate && objKey == t.objectKey && t.transformFunc != nil {
66+
t.transformFunc(&obj)
67+
}
68+
}
69+
// make sure to plumb request through to underlying client
70+
return client.Create(ctx, obj, opts...)
71+
},
72+
Delete: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.DeleteOption) error {
73+
c.deleteCalled++
74+
if c.deleteErr != nil {
75+
return c.deleteErr
76+
}
77+
return client.Delete(ctx, obj, opts...)
78+
},
79+
Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
80+
c.getCalled++
81+
if c.getErr != nil {
82+
return c.getErr
83+
}
84+
return client.Get(ctx, key, obj, opts...)
85+
},
86+
}).WithScheme(scheme)
87+
c.Client = clientBuilder.Build()
88+
return nil
5089
}
5190

5291
func setupTestCatalogs(n int) []client.Object {

internal/pkg/v1/action/catalog_create.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"time"
66

7+
"github.com/operator-framework/kubectl-operator/pkg/action"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89

910
olmv1 "github.com/operator-framework/operator-controller/api/v1"
@@ -29,9 +30,9 @@ type CatalogCreate struct {
2930
Logf func(string, ...interface{})
3031
}
3132

32-
func NewCatalogCreate(client createClient) *CatalogCreate {
33+
func NewCatalogCreate(cfg *action.Configuration) *CatalogCreate {
3334
return &CatalogCreate{
34-
client: client,
35+
client: cfg.Client,
3536
Logf: func(string, ...interface{}) {},
3637
}
3738
}

internal/pkg/v1/action/catalog_create_test.go

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,22 @@ import (
66

77
. "github.com/onsi/ginkgo"
88
. "github.com/onsi/gomega"
9-
9+
"github.com/operator-framework/kubectl-operator/pkg/action"
1010
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/types"
1112
"sigs.k8s.io/controller-runtime/pkg/client"
1213

1314
olmv1 "github.com/operator-framework/operator-controller/api/v1"
1415

1516
internalaction "github.com/operator-framework/kubectl-operator/internal/pkg/v1/action"
1617
)
1718

18-
type mockCreateClient struct {
19-
*mockCreator
20-
*mockGetter
21-
*mockDeleter
22-
createCatalog *olmv1.ClusterCatalog
23-
}
24-
25-
func (mcc *mockCreateClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
26-
mcc.createCatalog = obj.(*olmv1.ClusterCatalog)
27-
return mcc.mockCreator.Create(ctx, obj, opts...)
28-
}
29-
3019
var _ = Describe("CatalogCreate", func() {
20+
catalogName := "testcatalog"
3121
pollInterval := 20
3222
expectedCatalog := olmv1.ClusterCatalog{
3323
ObjectMeta: metav1.ObjectMeta{
34-
Name: "testcatalog",
24+
Name: catalogName,
3525
Labels: map[string]string{"a": "b"},
3626
},
3727
Spec: olmv1.ClusterCatalogSpec{
@@ -49,9 +39,10 @@ var _ = Describe("CatalogCreate", func() {
4939

5040
It("fails creating catalog", func() {
5141
expectedErr := errors.New("create failed")
52-
mockClient := &mockCreateClient{&mockCreator{createErr: expectedErr}, nil, nil, &expectedCatalog}
42+
mockClient := fakeClient{createErr: expectedErr}
43+
mockClient.Initialize()
5344

54-
creator := internalaction.NewCatalogCreate(mockClient)
45+
creator := internalaction.NewCatalogCreate(&action.Configuration{Client: mockClient})
5546
creator.Available = true
5647
creator.CatalogName = expectedCatalog.Name
5748
creator.ImageSourceRef = expectedCatalog.Spec.Source.Image.Ref
@@ -64,17 +55,22 @@ var _ = Describe("CatalogCreate", func() {
6455
Expect(err).To(MatchError(expectedErr))
6556
Expect(mockClient.createCalled).To(Equal(1))
6657

67-
// there is no way of testing a happy path in unit tests because we have no way to
68-
// set/mock the catalog status condition we're waiting for in waitUntilCatalogStatusCondition
69-
// but we can still at least verify that CR would have been created with expected attribute values
70-
validateCreateCatalog(mockClient.createCatalog, &expectedCatalog)
58+
//// there is no way of testing a happy path in unit tests because we have no way to
59+
//// set/mock the catalog status condition we're waiting for in waitUntilCatalogStatusCondition
60+
//// but we can still at least verify that CR would have been created with expected attribute values
61+
//actualCatalog := &olmv1catalogd.ClusterCatalog{}
62+
//fmt.Printf("%+v, %+v", actualCatalog, expectedCatalog)
63+
//Expect(mockClient.Get(context.TODO(), types.NamespacedName{Name: catalogName}, actualCatalog)).To(Succeed())
64+
//Expect(actualCatalog).To(Equal(expectedCatalog))
7165
})
7266

7367
It("fails waiting for created catalog status, successfully cleans up", func() {
7468
expectedErr := errors.New("get failed")
75-
mockClient := &mockCreateClient{&mockCreator{}, &mockGetter{getErr: expectedErr}, &mockDeleter{}, nil}
69+
mockClient := fakeClient{getErr: expectedErr}
70+
Expect(mockClient.Initialize()).To(Succeed())
7671

77-
creator := internalaction.NewCatalogCreate(mockClient)
72+
creator := internalaction.NewCatalogCreate(&action.Configuration{Client: mockClient})
73+
creator.CatalogName = expectedCatalog.Name
7874
err := creator.Run(context.TODO())
7975

8076
Expect(err).NotTo(BeNil())
@@ -87,9 +83,12 @@ var _ = Describe("CatalogCreate", func() {
8783
It("fails waiting for created catalog status, fails clean up", func() {
8884
getErr := errors.New("get failed")
8985
deleteErr := errors.New("delete failed")
90-
mockClient := &mockCreateClient{&mockCreator{}, &mockGetter{getErr: getErr}, &mockDeleter{deleteErr: deleteErr}, nil}
86+
mockClient := fakeClient{deleteErr: deleteErr, getErr: getErr}
87+
Expect(mockClient.Initialize()).To(Succeed())
9188

92-
creator := internalaction.NewCatalogCreate(mockClient)
89+
creator := internalaction.NewCatalogCreate(&action.Configuration{Client: mockClient})
90+
// fakeClient requires at least the catalogName to be set to run
91+
creator.CatalogName = expectedCatalog.Name
9392
err := creator.Run(context.TODO())
9493

9594
Expect(err).NotTo(BeNil())
@@ -98,6 +97,42 @@ var _ = Describe("CatalogCreate", func() {
9897
Expect(mockClient.getCalled).To(Equal(1))
9998
Expect(mockClient.deleteCalled).To(Equal(1))
10099
})
100+
It("succeeds creating catalog", func() {
101+
mockClient := fakeClient{
102+
transformers: []objectTransformer{
103+
{
104+
verb: verbCreate,
105+
objectKey: types.NamespacedName{Name: catalogName},
106+
transformFunc: func(obj *client.Object) {
107+
if obj == nil {
108+
return
109+
}
110+
catalogObj, ok := (*obj).(*olmv1.ClusterCatalog)
111+
if !ok {
112+
return
113+
}
114+
catalogObj.Status.Conditions = []metav1.Condition{{Type: olmv1.TypeServing, Status: metav1.ConditionTrue}}
115+
},
116+
},
117+
},
118+
}
119+
Expect(mockClient.Initialize()).To(Succeed())
120+
121+
creator := internalaction.NewCatalogCreate(&action.Configuration{Client: mockClient})
122+
creator.Available = true
123+
creator.CatalogName = expectedCatalog.Name
124+
creator.ImageSourceRef = expectedCatalog.Spec.Source.Image.Ref
125+
creator.Priority = expectedCatalog.Spec.Priority
126+
creator.Labels = expectedCatalog.Labels
127+
creator.PollIntervalMinutes = *expectedCatalog.Spec.Source.Image.PollIntervalMinutes
128+
Expect(creator.Run(context.TODO())).To(Succeed())
129+
130+
Expect(mockClient.createCalled).To(Equal(1))
131+
132+
actualCatalog := &olmv1.ClusterCatalog{TypeMeta: metav1.TypeMeta{Kind: "ClusterCatalog", APIVersion: "olm.operatorframework.io/v1"}}
133+
mockClient.Client.Get(context.TODO(), types.NamespacedName{Name: catalogName}, actualCatalog)
134+
validateCreateCatalog(actualCatalog, &expectedCatalog)
135+
})
101136
})
102137

103138
func validateCreateCatalog(actual, expected *olmv1.ClusterCatalog) {

0 commit comments

Comments
 (0)