Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement the setting up of the scheme with managed contents #1

Merged
merged 2 commits into from
Jul 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 61 additions & 22 deletions internal/contentmanager/contentmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import (
"context"
"errors"
"fmt"

"github.com/operator-framework/operator-controller/api/v1alpha1"

Check failure on line 8 in internal/contentmanager/contentmanager.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard -s dot -s default -s prefix(github.com/operator-framework) -s prefix(github.com/operator-framework/operator-controller) --custom-order (gci)
oclabels "github.com/operator-framework/operator-controller/internal/labels"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"

Check failure on line 20 in internal/contentmanager/contentmanager.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard -s dot -s default -s prefix(github.com/operator-framework) -s prefix(github.com/operator-framework/operator-controller) --custom-order (gci)
)

type ContentManager interface {
Expand All @@ -28,20 +33,23 @@

type RestConfigMapper func(context.Context, client.Object, *rest.Config) (*rest.Config, error)

type extensionCacheData struct {
Cache cache.Cache
Cancel context.CancelFunc
}

type instance struct {
rcm RestConfigMapper
baseCfg *rest.Config
extensionCaches map[string]cache.Cache
scheme *runtime.Scheme
extensionCaches map[string]extensionCacheData
mapper meta.RESTMapper
}

func New(rcm RestConfigMapper, cfg *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper) ContentManager {
func New(rcm RestConfigMapper, cfg *rest.Config, mapper meta.RESTMapper) ContentManager {
return &instance{
rcm: rcm,
baseCfg: cfg,
extensionCaches: make(map[string]cache.Cache),
scheme: scheme,
extensionCaches: make(map[string]extensionCacheData),
mapper: mapper,
}
}
Expand All @@ -54,29 +62,52 @@

// TODO: add a http.RoundTripper to the config to ensure it is always using an up
// to date authentication token for the ServiceAccount token provided in the ClusterExtension.
// Maybe this should be handled by the RestConfigMapper?
// Maybe this should be handled by the RestConfigMapper

// Assumptions: all objects received by the function will have the Object metadata specfically,
// ApiVersion and Kind set. Failure to which the code will panic when adding the types to the scheme
scheme := runtime.NewScheme()
for _, obj := range objs {
gvk := obj.GetObjectKind().GroupVersionKind()
listKind := obj.GetObjectKind().GroupVersionKind().Kind + "List"

if gvk.Kind == "" || gvk.Version == "" {
return errors.New("object Kind or Version is not defined")
}

if !scheme.Recognizes(gvk) {
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(gvk)
ul := &unstructured.UnstructuredList{}

ul.SetGroupVersionKind(gvk.GroupVersion().WithKind(listKind))

scheme.AddKnownTypeWithName(gvk, u)
scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(listKind), ul)
metav1.AddToGroupVersion(scheme, gvk.GroupVersion())
everettraven marked this conversation as resolved.
Show resolved Hide resolved
}
}

tgtLabels := labels.Set{
oclabels.OwnerKindKey: v1alpha1.ClusterExtensionKind,
oclabels.OwnerNameKey: ce.GetName(),
}

c, err := cache.New(cfg, cache.Options{
// TODO: explore how we can dynamically build this scheme based on the provided
// resources to be managed. Using a top level scheme will not be sufficient as
// that means it will have to know of every type that could be watched on startup
Scheme: i.scheme,
Scheme: scheme,
everettraven marked this conversation as resolved.
Show resolved Hide resolved
DefaultLabelSelector: tgtLabels.AsSelector(),
})
if err != nil {
return fmt.Errorf("creating cache for ClusterExtension %q: %w", ce.Name, err)
}

for _, obj := range objs {
// TODO: Make sure we are sufficiently filtering
// the watches to cache the minimum amount of information necessary.
// This will likely result in some default label selection option being placed
// in the cache configuration.
err = ctrl.Watch(
source.Kind(
c,
obj,
handler.TypedEnqueueRequestForOwner[client.Object](
i.scheme,
scheme,
i.mapper,
ce,
),
Expand All @@ -88,17 +119,25 @@
}
}

if data, ok := i.extensionCaches[ce.GetName()]; ok {
data.Cancel()
}

ctx, cancel := context.WithCancel(ctx)
go c.Start(ctx)

Check failure on line 127 in internal/contentmanager/contentmanager.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Start` is not checked (errcheck)
// TODO: If a cache already exists, we should ensure that we are removing informers
// for any resources that no longer need to be watched. Ideally we would not always create
// a new cache, but it could be acceptable to do so and leave optimization as a follow up item.
// if we continue to create a new cache every time, we should ensure that we are appropriately stopping
// the cache and configured sources _before_ replacing it in the mapping.
i.extensionCaches[ce.Name] = c
i.extensionCaches[ce.Name] = extensionCacheData{
Cache: c,
Cancel: cancel,
}

return nil
}

func (i *instance) RemoveManagedContent(ce *v1alpha1.ClusterExtension) error {
panic("Not implemented!")
if data, ok := i.extensionCaches[ce.GetName()]; ok {
data.Cancel()
delete(i.extensionCaches, ce.GetName())
}

return nil
}
Loading