Skip to content

Commit

Permalink
chore: Refactor registry to be more reusable
Browse files Browse the repository at this point in the history
We need to move things around to avoid import cycles as we add IAM
support.
  • Loading branch information
justinsb committed Jun 5, 2024
1 parent 040a9a2 commit cbbf85b
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"reflect"

"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/iam/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
kcciamclient "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/iamclient"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/deepcopy"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/krmtotf"
Expand Down Expand Up @@ -49,8 +49,8 @@ func New(tfProvider *schema.Provider, smloader *servicemappingloader.ServiceMapp

func (i *iamClient) SupportsIAM(unstructured *unstructured.Unstructured) (bool, error) {
groundKind := unstructured.GroupVersionKind().GroupKind()
if direct.IsDirect(groundKind) {
return direct.SupportsIAM(groundKind)
if registry.IsDirectByGK(groundKind) {
return registry.SupportsIAM(groundKind)
}

rc, err := i.smLoader.GetResourceConfig(unstructured)
Expand Down
7 changes: 4 additions & 3 deletions pkg/controller/direct/alloydb/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ import (
krm "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/alloydb/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
)

func init() {
directbase.ControllerBuilder.RegisterModel(krm.AlloyDBClusterGVK, NewModel)
registry.RegisterModel(krm.AlloyDBClusterGVK, NewModel)
}

func NewModel(config *controller.Config) directbase.Model {
return &clusterModel{config: config}
func NewModel(ctx context.Context, config *controller.Config) (directbase.Model, error) {
return &clusterModel{config: config}, nil
}

type clusterModel struct {
Expand Down
7 changes: 4 additions & 3 deletions pkg/controller/direct/apikeys/apikeyskey_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@ import (
krm "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/apikeys/v1alpha1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"

. "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/mappings" //nolint:revive
)

func init() {
directbase.ControllerBuilder.RegisterModel(krm.APIKeysKeyGVK, newAPIKeysModel)
registry.RegisterModel(krm.APIKeysKeyGVK, newAPIKeysModel)
}

func newAPIKeysModel(config *controller.Config) directbase.Model {
return &model{config: *config}
func newAPIKeysModel(ctx context.Context, config *controller.Config) (directbase.Model, error) {
return &model{config: *config}, nil
}

type model struct {
Expand Down
55 changes: 4 additions & 51 deletions pkg/controller/direct/directbase/directbase_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (

"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/apis/core/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/kccstate"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
kcciamclient "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/iamclient"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/jitter"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/lifecyclehandler"
Expand All @@ -37,7 +36,6 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util"

"golang.org/x/sync/semaphore"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -55,66 +53,21 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"
)

var ControllerBuilder directControllerBuilder

func init() {
ControllerBuilder = directControllerBuilder{}
}

type directControllerBuilder struct {
modelMapper map[schema.GroupVersionKind]func(*controller.Config) Model
}

func (c *directControllerBuilder) RegisterModel(gvk schema.GroupVersionKind, modelFn func(*controller.Config) Model) {
if c.modelMapper == nil {
c.modelMapper = map[schema.GroupVersionKind]func(*controller.Config) Model{}
}
c.modelMapper[gvk] = modelFn
}

func (c *directControllerBuilder) AddController(mgr manager.Manager, config *controller.Config, crd *apiextensions.CustomResourceDefinition, deps Deps) error {
func AddController(mgr manager.Manager, gvk schema.GroupVersionKind, model Model, deps Deps) error {
immediateReconcileRequests := make(chan event.GenericEvent, k8s.ImmediateReconcileRequestsBufferSize)
resourceWatcherRoutines := semaphore.NewWeighted(k8s.MaxNumResourceWatcherRoutines)

reconciler, err := c.NewReconciler(mgr, config, immediateReconcileRequests, resourceWatcherRoutines, crd, deps.JitterGenerator)
reconciler, err := NewReconciler(mgr, immediateReconcileRequests, resourceWatcherRoutines, gvk, model, deps.JitterGenerator)
if err != nil {
return err
}
return add(mgr, reconciler)
}

func (c *directControllerBuilder) IsDirectByGK(gk schema.GroupKind) bool {
for gvk, _ := range c.modelMapper {
if gvk.Group == gk.Group && gvk.Kind == gk.Kind {
return true
}
}
return false
}

func (c *directControllerBuilder) gvkByCrd(crd *apiextensions.CustomResourceDefinition) schema.GroupVersionKind {
for gvk, _ := range c.modelMapper {
if crd.Spec.Group == gvk.Group && crd.Spec.Names.Kind == gvk.Kind {
return gvk
}
}
return schema.GroupVersionKind{}
}

// NewReconciler returns a new reconcile.Reconciler.
func (c *directControllerBuilder) NewReconciler(mgr manager.Manager, config *controller.Config, immediateReconcileRequests chan event.GenericEvent, resourceWatcherRoutines *semaphore.Weighted,
crd *apiextensions.CustomResourceDefinition, jg jitter.Generator) (*DirectReconciler, error) {
gvk := c.gvkByCrd(crd)
if gvk.Empty() {
return nil, fmt.Errorf("CRD %s is not registered on direct controllers", crd.Name)
}
func NewReconciler(mgr manager.Manager, immediateReconcileRequests chan event.GenericEvent, resourceWatcherRoutines *semaphore.Weighted,
gvk schema.GroupVersionKind, model Model, jg jitter.Generator) (*DirectReconciler, error) {
controllerName := strings.ToLower(gvk.Kind) + "-controller"
modelFn, ok := c.modelMapper[gvk]
if !ok {
return nil, fmt.Errorf("no direct controller is registered for GroupVersionKind %s", gvk)
}
model := modelFn(config)

if jg == nil {
return nil, fmt.Errorf("jitter generator is not initialized")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,31 @@ import (
"strings"

"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/logging"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// IsDirect returns true if this resource uses the direct-reconciliation model.
func IsDirect(groupKind schema.GroupKind) bool {
switch groupKind {
case schema.GroupKind{Group: "logging.cnrm.cloud.google.com", Kind: "LoggingLogMetric"}:
return true
}
return false
}

// SupportsIAM returns true if this resource supports IAM (not all GCP resources do).
// An error will be returned if IsDirect(groupKind) is not true.
func SupportsIAM(groupKind schema.GroupKind) (bool, error) {
switch groupKind {
case schema.GroupKind{Group: "logging.cnrm.cloud.google.com", Kind: "LoggingLogMetric"}:
return false, nil
}
return false, fmt.Errorf("groupKind %v is not recognized as a direct kind", groupKind)
}

// Export attempts to export the resource specified by url.
// The url format should match the Cloud-Asset-Inventory format: https://cloud.google.com/asset-inventory/docs/resource-name-format
// If url is not recognized or not implemented by a direct controller, this returns (nil, nil)
func Export(ctx context.Context, url string, config *controller.Config) (*unstructured.Unstructured, error) {
if strings.HasPrefix(url, "//logging.googleapis.com/") {
tokens := strings.Split(strings.TrimPrefix(url, "//logging.googleapis.com/"), "/")
if len(tokens) == 4 && tokens[0] == "projects" && tokens[2] == "metrics" {
m := logging.NewLogMetricModel(config)
model, err := registry.GetModel(schema.GroupKind{Group: "logging.cnrm.cloud.google.com", Kind: "LoggingLogMetric"})
if err != nil {
return nil, err
}
in := &unstructured.Unstructured{}
in.SetName(tokens[3])
if err := unstructured.SetNestedField(in.Object, tokens[1], "spec", "projectRef", "external"); err != nil {
return nil, err
}

var reader client.Reader // TODO: Create erroring reader?
a, err := m.AdapterForObject(ctx, reader, in)
a, err := model.AdapterForObject(ctx, reader, in)
if err != nil {
return nil, err
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/controller/direct/gkehub/featuremembership_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/references"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
)

const ctrlName = "gkehubfeaturemembership-controller"

func init() {
directbase.ControllerBuilder.RegisterModel(krm.GKEHubFeatureMembershipGVK, GetModel)
registry.RegisterModel(krm.GKEHubFeatureMembershipGVK, getGkeHubModel)
}

func GetModel(config *controller.Config) directbase.Model {
return &gkeHubModel{config: config}
func getGkeHubModel(ctx context.Context, config *controller.Config) (directbase.Model, error) {
return &gkeHubModel{config: config}, nil
}

type gkeHubModel struct {
Expand Down
7 changes: 4 additions & 3 deletions pkg/controller/direct/logging/logmetric_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/references"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
)

const ctrlName = "logmetric-controller"

func init() {
directbase.ControllerBuilder.RegisterModel(krm.LoggingLogMetricGVK, NewLogMetricModel)
registry.RegisterModel(krm.LoggingLogMetricGVK, NewLogMetricModel)
}

func NewLogMetricModel(config *controller.Config) directbase.Model {
return &logMetricModel{config: config}
func NewLogMetricModel(ctx context.Context, config *controller.Config) (directbase.Model, error) {
return &logMetricModel{config: config}, nil
}

type logMetricModel struct {
Expand Down
107 changes: 107 additions & 0 deletions pkg/controller/direct/registry/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2024 Google LLC
//
// 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 registry

import (
"context"
"fmt"

"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var singleton = registry{}

type registry struct {
registrations map[schema.GroupKind]*registration
}

type registration struct {
gvk schema.GroupVersionKind
factory ModelFactoryFunc
model directbase.Model
}

type ModelFactoryFunc func(ctx context.Context, config *controller.Config) (directbase.Model, error)

func GetModel(gk schema.GroupKind) (directbase.Model, error) {
registration := singleton.registrations[gk]
if registration == nil {
return nil, fmt.Errorf("no model registered for %s", gk)
}
return registration.model, nil
}

func PreferredGVK(gk schema.GroupKind) (schema.GroupVersionKind, bool) {
registration := singleton.registrations[gk]
if registration == nil {
return schema.GroupVersionKind{}, false
}
return registration.gvk, true
}

func Init(ctx context.Context, config *controller.Config) error {
for _, registration := range singleton.registrations {
model, err := registration.factory(ctx, config)
if err != nil {
return err
}

registration.model = model
}
return nil
}

func RegisterModel(gvk schema.GroupVersionKind, modelFn ModelFactoryFunc) {
if singleton.registrations == nil {
singleton.registrations = make(map[schema.GroupKind]*registration)
}
singleton.registrations[gvk.GroupKind()] = &registration{
gvk: gvk,
factory: modelFn,
}
}

func IsDirectByGK(gk schema.GroupKind) bool {
registration := singleton.registrations[gk]
return registration != nil
}

// IsIAMDirect returns true if this resource uses the direct-reconciliation model for IAM.
func IsIAMDirect(groupKind schema.GroupKind) bool {
registration := singleton.registrations[groupKind]
if registration == nil {
return false
}

// TODO: Move to registration somehow?
switch groupKind {
case schema.GroupKind{Group: "privateca.cnrm.cloud.google.com", Kind: "PrivateCACAPool"}:
return true
}
return false
}

// SupportsIAM returns true if this resource supports IAM (not all GCP resources do).
// An error will be returned if IsDirect(groupKind) is not true.
func SupportsIAM(groupKind schema.GroupKind) (bool, error) {
// TODO: Move to registration somehow?
switch groupKind {
case schema.GroupKind{Group: "logging.cnrm.cloud.google.com", Kind: "LoggingLogMetric"}:
return false, nil
}
return false, fmt.Errorf("groupKind %v is not recognized as a direct kind", groupKind)
}
7 changes: 4 additions & 3 deletions pkg/controller/direct/resourcemanager/tagkey_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ import (
krm "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/tags/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry"
)

func init() {
directbase.ControllerBuilder.RegisterModel(krm.TagsTagKeyGVK, newTagKeyModel)
registry.RegisterModel(krm.TagsTagKeyGVK, newTagKeyModel)
}

func newTagKeyModel(config *controller.Config) directbase.Model {
return &tagKeyModel{config: config}
func newTagKeyModel(ctx context.Context, config *controller.Config) (directbase.Model, error) {
return &tagKeyModel{config: config}, nil
}

type tagKeyModel struct {
Expand Down
Loading

0 comments on commit cbbf85b

Please sign in to comment.