Skip to content

Commit

Permalink
Extract the registry deployment into component. Add unit tests for th…
Browse files Browse the repository at this point in the history
…e new component

A new component is introduced in `./pkg/component/registrycaches`. The component deploys registry caches. It implements the well-known `component.DeployWaiter` interface.

This commit also moves the existing controller from `./pkg/controller` to dedicated pkg `./pkg/controller/extension`. The controller name is changed from `registry-cache` to `extension-controller`.
This commit also introduces a new pkg `./pkg/constants`. It exports constants that are used from 2 or more packages.
  • Loading branch information
ialidzhikov committed Aug 30, 2023
1 parent ce4367b commit b42ac2e
Show file tree
Hide file tree
Showing 34 changed files with 3,840 additions and 276 deletions.
4 changes: 2 additions & 2 deletions cmd/gardener-extension-registry-cache-admission/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

admissioncmd "github.com/gardener/gardener-extension-registry-cache/pkg/admission/cmd"
registryinstall "github.com/gardener/gardener-extension-registry-cache/pkg/apis/registry/install"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller"
"github.com/gardener/gardener-extension-registry-cache/pkg/constants"
)

var log = logf.Log.WithName("gardener-extension-registry-cache-admission")
Expand All @@ -56,7 +56,7 @@ func NewAdmissionCommand(ctx context.Context) *cobra.Command {
)

cmd := &cobra.Command{
Use: fmt.Sprintf("gardener-extension-%s-admission", controller.Type),
Use: fmt.Sprintf("gardener-extension-%s-admission", constants.ExtensionType),

RunE: func(cmd *cobra.Command, args []string) error {
verflag.PrintAndExitIfRequested()
Expand Down
8 changes: 4 additions & 4 deletions cmd/gardener-extension-registry-cache/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"

registryinstall "github.com/gardener/gardener-extension-registry-cache/pkg/apis/registry/install"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller/extension"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller/healthcheck"
)

Expand Down Expand Up @@ -88,10 +88,10 @@ func (o *Options) run(ctx context.Context) error {

ctrlConfig := o.registryOptions.Completed()
ctrlConfig.ApplyHealthCheckConfig(&healthcheck.DefaultAddOptions.HealthCheckConfig)
ctrlConfig.Apply(&controller.DefaultAddOptions.Config)
o.controllerOptions.Completed().Apply(&controller.DefaultAddOptions.ControllerOptions)
ctrlConfig.Apply(&extension.DefaultAddOptions.Config)
o.controllerOptions.Completed().Apply(&extension.DefaultAddOptions.ControllerOptions)
o.healthOptions.Completed().Apply(&healthcheck.DefaultAddOptions.Controller)
o.reconcileOptions.Completed().Apply(&controller.DefaultAddOptions.IgnoreOperationAnnotation)
o.reconcileOptions.Completed().Apply(&extension.DefaultAddOptions.IgnoreOperationAnnotation)
o.heartbeatOptions.Completed().Apply(&heartbeat.DefaultAddOptions)

if err := o.controllerSwitches.Completed().AddToManager(ctx, mgr); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/admission/validator/shoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

api "github.com/gardener/gardener-extension-registry-cache/pkg/apis/registry"
"github.com/gardener/gardener-extension-registry-cache/pkg/apis/registry/validation"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller"
"github.com/gardener/gardener-extension-registry-cache/pkg/constants"
)

// shoot validates shoots
Expand All @@ -51,7 +51,7 @@ func (s *shoot) Validate(_ context.Context, new, _ client.Object) error {
var ext *core.Extension
var fldPath *field.Path
for i, ex := range shoot.Spec.Extensions {
if ex.Type == controller.Type {
if ex.Type == constants.ExtensionType {
ext = ex.DeepCopy()
fldPath = field.NewPath("spec", "extensions").Index(i)
break
Expand Down
4 changes: 2 additions & 2 deletions pkg/admission/validator/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"

"github.com/gardener/gardener-extension-registry-cache/pkg/controller"
"github.com/gardener/gardener-extension-registry-cache/pkg/constants"
)

const (
Expand All @@ -38,7 +38,7 @@ func New(mgr manager.Manager) (*extensionswebhook.Webhook, error) {
logger.Info("Setting up webhook", "name", Name)

return extensionswebhook.New(mgr, extensionswebhook.Args{
Provider: controller.Type,
Provider: constants.ExtensionType,
Name: Name,
Path: "/webhooks/validate",
Validators: map[extensionswebhook.Validator][]extensionswebhook.Type{
Expand Down
3 changes: 0 additions & 3 deletions pkg/apis/registry/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// RegistryResourceName is the name for registry resources in the shoot.
const RegistryResourceName = "extension-registry-cache"

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// RegistryConfig contains information about registry caches to deploy.
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
configapi "github.com/gardener/gardener-extension-registry-cache/pkg/apis/config"
"github.com/gardener/gardener-extension-registry-cache/pkg/apis/config/v1alpha1"
"github.com/gardener/gardener-extension-registry-cache/pkg/apis/config/validation"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller"
"github.com/gardener/gardener-extension-registry-cache/pkg/controller/extension"
healthcheckcontroller "github.com/gardener/gardener-extension-registry-cache/pkg/controller/healthcheck"
oscwebhook "github.com/gardener/gardener-extension-registry-cache/pkg/webhook/operatingsystemconfig"
)
Expand Down Expand Up @@ -105,7 +105,7 @@ func (c *RegistryServiceConfig) Apply(config *configapi.Configuration) {
// ControllerSwitches are the cmd.SwitchOptions for the provider controllers.
func ControllerSwitches() *cmd.SwitchOptions {
return cmd.NewSwitchOptions(
cmd.Switch(controller.ControllerName, controller.AddToManager),
cmd.Switch(extension.ControllerName, extension.AddToManager),
cmd.Switch(extensionshealthcheckcontroller.ControllerName, healthcheckcontroller.AddToManager),
cmd.Switch(extensionsheartbeatcontroller.ControllerName, extensionsheartbeatcontroller.AddToManager),
)
Expand Down
257 changes: 257 additions & 0 deletions pkg/component/registrycaches/registrycaches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
// Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// 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 registrycaches

import (
"context"
"fmt"
"strconv"
"strings"
"time"

"github.com/gardener/gardener/pkg/client/kubernetes"
"github.com/gardener/gardener/pkg/component"
"github.com/gardener/gardener/pkg/utils/managedresources"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/gardener/gardener-extension-registry-cache/pkg/apis/registry/v1alpha1"
"github.com/gardener/gardener-extension-registry-cache/pkg/constants"
registryutils "github.com/gardener/gardener-extension-registry-cache/pkg/utils/registry"
)

const (
// ManagedResourceName is the ManagedResource name for the registry cache resources in the shoot.
ManagedResourceName = "extension-registry-cache"
)

// Values is a set of configuration values for the registry caches.
type Values struct {
// Image is the container image used for the registry cache.
Image string
// Caches are the registry caches to deploy.
Caches []v1alpha1.RegistryCache
}

// New creates a new instance of DeployWaiter for registry caches.
func New(
client client.Client,
namespace string,
values Values,
) component.DeployWaiter {
return &registryCaches{
client: client,
namespace: namespace,
values: values,
}
}

type registryCaches struct {
client client.Client
namespace string
values Values
}

// Deploy implements component.DeployWaiter.
func (r *registryCaches) Deploy(ctx context.Context) error {
data, err := r.computeResourcesData()
if err != nil {
return err
}

var (
origin = "registry-cache"
keepObjects = false

secretName, secret = managedresources.NewSecret(r.client, r.namespace, ManagedResourceName, data, false)
managedResource = managedresources.NewForShoot(r.client, r.namespace, ManagedResourceName, origin, keepObjects).
WithSecretRef(secretName).
DeletePersistentVolumeClaims(true)
)

if err := secret.Reconcile(ctx); err != nil {
return fmt.Errorf("failed to create or update secret of managed resources: %w", err)
}

if err := managedResource.Reconcile(ctx); err != nil {
return fmt.Errorf("failed to not create or update managed resource: %w", err)
}

return nil
}

// Destroy implements component.DeployWaiter.
func (r *registryCaches) Destroy(ctx context.Context) error {
return managedresources.Delete(ctx, r.client, r.namespace, ManagedResourceName, false)
}

// TimeoutWaitForManagedResource is the timeout used while waiting for the ManagedResources to become healthy
// or deleted.
var TimeoutWaitForManagedResource = 2 * time.Minute

// Wait implements component.DeployWaiter.
func (r *registryCaches) Wait(ctx context.Context) error {
timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource)
defer cancel()

return managedresources.WaitUntilHealthy(timeoutCtx, r.client, r.namespace, ManagedResourceName)
}

// WaitCleanup implements component.DeployWaiter.
func (r *registryCaches) WaitCleanup(ctx context.Context) error {
timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource)
defer cancel()

return managedresources.WaitUntilDeleted(timeoutCtx, r.client, r.namespace, ManagedResourceName)
}

func (r *registryCaches) computeResourcesData() (map[string][]byte, error) {
objects := []client.Object{
&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: constants.NamespaceRegistryCache,
},
},
}

for _, cache := range r.values.Caches {
cacheObjects, err := computeResourcesDataForRegistryCache(&cache, r.values.Image)
if err != nil {
return nil, fmt.Errorf("failed to compute resources for upstream %s: %w", cache.Upstream, err)
}

objects = append(objects, cacheObjects...)
}

registry := managedresources.NewRegistry(kubernetes.ShootScheme, kubernetes.ShootCodec, kubernetes.ShootSerializer)

return registry.AddAllAndSerialize(objects...)
}

func computeResourcesDataForRegistryCache(cache *v1alpha1.RegistryCache, image string) ([]client.Object, error) {
if cache.Size == nil {
return nil, fmt.Errorf("registry cache size is required")
}
if cache.GarbageCollectionEnabled == nil {
return nil, fmt.Errorf("registry cache garbageCollectionEnabled is required")
}

const (
registryCacheVolumeName = "cache-volume"
)

var (
name = strings.Replace(fmt.Sprintf("registry-%s", strings.Split(cache.Upstream, ":")[0]), ".", "-", -1)
labels = map[string]string{
"app": name,
constants.UpstreamHostLabel: cache.Upstream,
}

service = &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: constants.NamespaceRegistryCache,
Labels: labels,
},
Spec: corev1.ServiceSpec{
Selector: labels,
Ports: []corev1.ServicePort{{
Name: "registry-cache",
Port: constants.RegistryCachePort,
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromString("registry-cache"),
}},
Type: corev1.ServiceTypeClusterIP,
},
}

statefulSet = &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: constants.NamespaceRegistryCache,
Labels: labels,
},
Spec: appsv1.StatefulSetSpec{
ServiceName: service.Name,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Replicas: pointer.Int32(1),
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "registry-cache",
Image: image,
ImagePullPolicy: corev1.PullIfNotPresent,
Ports: []corev1.ContainerPort{
{
ContainerPort: constants.RegistryCachePort,
Name: "registry-cache",
},
},
Env: []corev1.EnvVar{
{
Name: "REGISTRY_PROXY_REMOTEURL",
Value: registryutils.GetUpstreamURL(cache.Upstream),
},
{
Name: "REGISTRY_STORAGE_DELETE_ENABLED",
Value: strconv.FormatBool(*cache.GarbageCollectionEnabled),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: registryCacheVolumeName,
ReadOnly: false,
MountPath: "/var/lib/registry",
},
},
},
},
},
},
VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: registryCacheVolumeName,
Labels: labels,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: *cache.Size,
},
},
},
},
},
},
}
)

return []client.Object{
service,
statefulSet,
}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package controller_test
package registrycaches_test

import (
"testing"
Expand All @@ -21,7 +21,7 @@ import (
. "github.com/onsi/gomega"
)

func TestController(t *testing.T) {
func TestRegistryCaches(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
RunSpecs(t, "Component RegistryCaches Suite")
}
Loading

0 comments on commit b42ac2e

Please sign in to comment.