Skip to content

Commit 65df903

Browse files
committed
Add command to create a new olmv1 catalog
Signed-off-by: Artur Zych <[email protected]>
1 parent c788f7c commit 65df903

File tree

6 files changed

+194
-10
lines changed

6 files changed

+194
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package olmv1
2+
3+
import (
4+
"time"
5+
6+
"github.com/spf13/cobra"
7+
"github.com/spf13/pflag"
8+
9+
"github.com/operator-framework/kubectl-operator/internal/cmd/internal/log"
10+
v1action "github.com/operator-framework/kubectl-operator/internal/pkg/v1/action"
11+
"github.com/operator-framework/kubectl-operator/pkg/action"
12+
)
13+
14+
// NewCatalogCreateCmd allows creating a new catalog
15+
func NewCatalogCreateCmd(cfg *action.Configuration) *cobra.Command {
16+
i := v1action.NewCatalogCreate(cfg)
17+
i.Logf = log.Printf
18+
19+
cmd := &cobra.Command{
20+
Use: "catalog <catalog_name> <image_source_ref>",
21+
Aliases: []string{"catalogs <catalog_name> <image_source_ref>"},
22+
Args: cobra.ExactArgs(2),
23+
Short: "Create a new catalog",
24+
Run: func(cmd *cobra.Command, args []string) {
25+
i.CatalogName = args[0]
26+
i.ImageSourceRef = args[1]
27+
28+
if err := i.Run(cmd.Context()); err != nil {
29+
log.Fatalf("failed to create catalog %q: %v", i.CatalogName, err)
30+
}
31+
log.Printf("catalog %q created", i.CatalogName)
32+
},
33+
}
34+
bindCatalogCreateFlags(cmd.Flags(), i)
35+
36+
return cmd
37+
}
38+
39+
func bindCatalogCreateFlags(fs *pflag.FlagSet, i *v1action.CatalogCreate) {
40+
fs.Int32Var(&i.Priority, "priority", 0, "priority of the catalog")
41+
fs.BoolVar(&i.Available, "available", true, "availability of the catalog")
42+
fs.IntVar(&i.PollIntervalMinutes, "source-poll-interval-minutes", 10, "source poll interval [in minutes]")
43+
fs.StringToStringVar(&i.Labels, "labels", map[string]string{}, "catalog labels")
44+
fs.DurationVar(&i.CleanupTimeout, "cleanup-timeout", time.Minute, "the amount of time to wait before cancelling cleanup")
45+
}

internal/cmd/olmv1.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,26 @@ func newOlmV1Cmd(cfg *action.Configuration) *cobra.Command {
1616

1717
getCmd := &cobra.Command{
1818
Use: "get",
19-
Short: "Display one or many OLMv1-specific resource(s)",
20-
Long: "Display one or many OLMv1-specific resource(s)",
19+
Short: "Display one or many resource(s)",
20+
Long: "Display one or many resource(s)",
2121
}
2222
getCmd.AddCommand(
2323
olmv1.NewOperatorInstalledGetCmd(cfg),
2424
olmv1.NewCatalogInstalledGetCmd(cfg),
2525
)
2626

27+
createCmd := &cobra.Command{
28+
Use: "create",
29+
Short: "Create a resource",
30+
Long: "Create a resource",
31+
}
32+
createCmd.AddCommand(olmv1.NewCatalogCreateCmd(cfg))
33+
2734
cmd.AddCommand(
2835
olmv1.NewOperatorInstallCmd(cfg),
2936
olmv1.NewOperatorUninstallCmd(cfg),
3037
getCmd,
38+
createCmd,
3139
)
3240

3341
return cmd
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package action
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
9+
olmv1catalogd "github.com/operator-framework/catalogd/api/v1"
10+
11+
"github.com/operator-framework/kubectl-operator/pkg/action"
12+
)
13+
14+
type CatalogCreate struct {
15+
config *action.Configuration
16+
CatalogName string
17+
ImageSourceRef string
18+
19+
Priority int32
20+
PollIntervalMinutes int
21+
Labels map[string]string
22+
Available bool
23+
CleanupTimeout time.Duration
24+
25+
Logf func(string, ...interface{})
26+
}
27+
28+
func NewCatalogCreate(cfg *action.Configuration) *CatalogCreate {
29+
return &CatalogCreate{
30+
config: cfg,
31+
Logf: func(string, ...interface{}) {},
32+
}
33+
}
34+
35+
func (i *CatalogCreate) Run(ctx context.Context) error {
36+
catalog := i.buildCatalog()
37+
if err := i.config.Client.Create(ctx, &catalog); err != nil {
38+
return err
39+
}
40+
41+
var err error
42+
if i.Available {
43+
err = waitUntilCatalogStatusCondition(ctx, i.config.Client, &catalog, olmv1catalogd.TypeServing, metav1.ConditionTrue)
44+
} else {
45+
err = waitUntilCatalogStatusCondition(ctx, i.config.Client, &catalog, olmv1catalogd.TypeServing, metav1.ConditionFalse)
46+
}
47+
48+
if err != nil {
49+
if cleanupErr := deleteWithTimeout(i.config.Client, &catalog, i.CleanupTimeout); cleanupErr != nil {
50+
i.Logf("cleaning up failed catalog: %v", cleanupErr)
51+
}
52+
return err
53+
}
54+
55+
return nil
56+
}
57+
58+
func (i *CatalogCreate) buildCatalog() olmv1catalogd.ClusterCatalog {
59+
catalog := olmv1catalogd.ClusterCatalog{
60+
ObjectMeta: metav1.ObjectMeta{
61+
Name: i.CatalogName,
62+
Labels: i.Labels,
63+
},
64+
Spec: olmv1catalogd.ClusterCatalogSpec{
65+
Source: olmv1catalogd.CatalogSource{
66+
Type: olmv1catalogd.SourceTypeImage,
67+
Image: &olmv1catalogd.ImageSource{
68+
Ref: i.ImageSourceRef,
69+
PollIntervalMinutes: &i.PollIntervalMinutes,
70+
},
71+
},
72+
Priority: i.Priority,
73+
AvailabilityMode: olmv1catalogd.AvailabilityModeAvailable,
74+
},
75+
}
76+
if !i.Available {
77+
catalog.Spec.AvailabilityMode = olmv1catalogd.AvailabilityModeUnavailable
78+
}
79+
80+
return catalog
81+
}

internal/pkg/v1/action/helpers.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package action
2+
3+
import (
4+
"context"
5+
"slices"
6+
"time"
7+
8+
apierrors "k8s.io/apimachinery/pkg/api/errors"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/types"
11+
"k8s.io/apimachinery/pkg/util/wait"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
14+
olmv1catalogd "github.com/operator-framework/catalogd/api/v1"
15+
)
16+
17+
const pollInterval = 250 * time.Millisecond
18+
19+
func objectKeyForObject(obj client.Object) types.NamespacedName {
20+
return types.NamespacedName{
21+
Namespace: obj.GetNamespace(),
22+
Name: obj.GetName(),
23+
}
24+
}
25+
26+
func waitUntilCatalogStatusCondition(
27+
ctx context.Context,
28+
cl client.Client,
29+
catalog *olmv1catalogd.ClusterCatalog,
30+
conditionType string,
31+
conditionStatus metav1.ConditionStatus,
32+
) error {
33+
opKey := objectKeyForObject(catalog)
34+
return wait.PollUntilContextCancel(ctx, pollInterval, true, func(conditionCtx context.Context) (bool, error) {
35+
if err := cl.Get(conditionCtx, opKey, catalog); err != nil {
36+
return false, err
37+
}
38+
39+
if slices.ContainsFunc(catalog.Status.Conditions, func(cond metav1.Condition) bool {
40+
return cond.Type == conditionType && cond.Status == conditionStatus
41+
}) {
42+
return true, nil
43+
}
44+
return false, nil
45+
})
46+
}
47+
48+
func deleteWithTimeout(cl client.Client, obj client.Object, timeout time.Duration) error {
49+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
50+
defer cancel()
51+
52+
if err := cl.Delete(ctx, obj); err != nil && !apierrors.IsNotFound(err) {
53+
return err
54+
}
55+
56+
return nil
57+
}

internal/pkg/v1/action/operator_install.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (i *OperatorInstall) Run(ctx context.Context) (*olmv1.ClusterExtension, err
5858
// All Types will exist, so Ready may have a false Status. So, wait until
5959
// Type=Ready,Status=True happens
6060

61-
if err := wait.PollUntilContextCancel(ctx, pollTimeout, true, func(conditionCtx context.Context) (bool, error) {
61+
if err := wait.PollUntilContextCancel(ctx, pollInterval, true, func(conditionCtx context.Context) (bool, error) {
6262
if err := i.config.Client.Get(conditionCtx, opKey, op); err != nil {
6363
return false, err
6464
}

internal/pkg/v1/action/operator_uninstall.go

-7
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
4343
return waitForDeletion(ctx, u.config.Client, op)
4444
}
4545

46-
func objectKeyForObject(obj client.Object) types.NamespacedName {
47-
return types.NamespacedName{
48-
Namespace: obj.GetNamespace(),
49-
Name: obj.GetName(),
50-
}
51-
}
52-
5346
func waitForDeletion(ctx context.Context, cl client.Client, objs ...client.Object) error {
5447
for _, obj := range objs {
5548
obj := obj

0 commit comments

Comments
 (0)