Skip to content

Enable the metrics endpoint on the OCI registry container #8

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

Merged
merged 1 commit into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."

# Build the docker image
docker-build: test
docker-build:
docker build . -t ${IMG}

# Push the docker image
Expand Down
2 changes: 1 addition & 1 deletion config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ spec:
resources:
limits:
cpu: 100m
memory: 30Mi
memory: 100Mi
requests:
cpu: 100m
memory: 20Mi
Expand Down
1 change: 1 addition & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rules:
- apiGroups:
- ""
resources:
- configmaps
- persistentvolumeclaims
- services
verbs:
Expand Down
21 changes: 14 additions & 7 deletions controllers/devfileregistry_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type DevfileRegistryReconciler struct {
// +kubebuilder:rbac:groups=registry.devfile.io,resources=devfileregistries,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=registry.devfile.io,resources=devfileregistries/status;devfileregistries/finalizers,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps;services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
// +kubebuilder:rbac:groups=extensions,resources=ingresses,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -71,22 +71,29 @@ func (r *DevfileRegistryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er
// Generate labels for any subresources generated by the operator
labels := registry.LabelsForDevfileRegistry(devfileRegistry.Name)

log.Info("Deploying registry")

// Check if the service already exists, if not create a new one
result, err := r.ensureService(ctx, devfileRegistry, labels)
result, err := r.ensure(ctx, devfileRegistry, &corev1.Service{}, labels, "")
if result != nil {
return *result, err
}

// If storage is enabled, create a persistent volume claim
if registry.IsStorageEnabled(devfileRegistry) {
// Check if the persistentvolumeclaim already exists, if not create a new one
result, err = r.ensurePVC(ctx, devfileRegistry, labels)
result, err = r.ensure(ctx, devfileRegistry, &corev1.PersistentVolumeClaim{}, labels, "")
if result != nil {
return *result, err
}
}

result, err = r.ensureDeployment(ctx, devfileRegistry, labels)
result, err = r.ensure(ctx, devfileRegistry, &corev1.ConfigMap{}, labels, "")
if result != nil {
return *result, err
}

result, err = r.ensure(ctx, devfileRegistry, &appsv1.Deployment{}, labels, "")
if result != nil {
return *result, err
}
Expand All @@ -102,14 +109,14 @@ func (r *DevfileRegistryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er
hostname := devfileRegistry.Spec.K8s.IngressDomain
if config.ControllerCfg.IsOpenShift() && hostname == "" {
// Check if the route exposing the devfile index exists
result, err = r.ensureRoute(ctx, devfileRegistry, labels)
result, err = r.ensure(ctx, devfileRegistry, &routev1.Route{}, labels, "")
if result != nil {
return *result, err
}

// Get the hostname of the generated devfile route
devfilesRoute := &routev1.Route{}
err = r.Get(ctx, types.NamespacedName{Name: devfileRegistry.Name + "-devfiles", Namespace: devfileRegistry.Namespace}, devfilesRoute)
err = r.Get(ctx, types.NamespacedName{Name: registry.IngressName(devfileRegistry.Name), Namespace: devfileRegistry.Namespace}, devfilesRoute)
if err != nil {
// Log an error, but requeue, as the controller's cached kube client likely hasn't registered the new route yet.
// See https://github.com/operator-framework/operator-sdk/issues/4013#issuecomment-707267616 for an explanation on why we requeue rather than error out here
Expand All @@ -120,7 +127,7 @@ func (r *DevfileRegistryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er
} else {
// Create/update the ingress for the devfile registry
hostname = registry.GetDevfileRegistryIngress(devfileRegistry)
result, err = r.ensureIngress(ctx, devfileRegistry, hostname, labels)
result, err = r.ensure(ctx, devfileRegistry, &v1beta1.Ingress{}, labels, hostname)
if result != nil {
return *result, err
}
Expand Down
158 changes: 54 additions & 104 deletions controllers/ensure.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package controllers

import (
"context"
"reflect"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
"github.com/devfile/registry-operator/pkg/registry"
Expand All @@ -22,133 +23,82 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ensureService ensures that a service for the devfile registry exists on the cluster and is up to date with the custom resource
func (r *DevfileRegistryReconciler) ensureService(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, labels map[string]string) (*reconcile.Result, error) {
// Check if the service already exists, if not create a new one
svc := &corev1.Service{}
err := r.Get(ctx, types.NamespacedName{Name: registry.ServiceName(cr.Name), Namespace: cr.Namespace}, svc)
if err != nil && errors.IsNotFound(err) {
// Define a new service
svc := registry.GenerateService(cr, r.Scheme, labels)
log.Info("Creating a new Service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name)
err = r.Create(ctx, svc)
if err != nil {
log.Error(err, "Failed to create new Service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name)
return &ctrl.Result{}, err
}
return nil, nil
} else if err != nil {
log.Error(err, "Failed to get Service")
return &ctrl.Result{}, err
}

return nil, nil
}

// ensureDeployment ensures that a devfile registry deployment exists on the cluster and is up to date with the custom resource
func (r *DevfileRegistryReconciler) ensureDeployment(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, labels map[string]string) (*reconcile.Result, error) {
dep := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{Name: registry.DeploymentName(cr.Name), Namespace: cr.Namespace}, dep)
if err != nil && errors.IsNotFound(err) {
// Generate a new Deployment template and create it on the cluster
dep = registry.GenerateDeployment(cr, r.Scheme, labels)

log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
err = r.Create(ctx, dep)
if err != nil {
log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
return &ctrl.Result{}, err
}
return nil, nil
} else if err != nil {
log.Error(err, "Failed to get Deployment")
return &ctrl.Result{}, err
}

err = r.updateDeployment(ctx, cr, dep)
if err != nil {
log.Error(err, "Failed to update Deployment")
return &ctrl.Result{}, err
}
return nil, nil
}
func (r *DevfileRegistryReconciler) ensure(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, resource runtime.Object, labels map[string]string, ingressDomain string) (*reconcile.Result, error) {
resourceType := reflect.TypeOf(resource).Elem().Name()
resourceName := getResourceName(resource, cr.Name)

func (r *DevfileRegistryReconciler) ensurePVC(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, labels map[string]string) (*reconcile.Result, error) {
// Check if the persistentvolumeclaim already exists, if not create a new one
pvc := &corev1.PersistentVolumeClaim{}
err := r.Get(ctx, types.NamespacedName{Name: registry.PVCName(cr.Name), Namespace: cr.Namespace}, pvc)
// Check to see if the requested resource exists on the cluster. If it doesn't exist, create it and return.
err := r.Get(ctx, types.NamespacedName{Name: resourceName, Namespace: cr.Namespace}, resource)
if err != nil && errors.IsNotFound(err) {
// Define a new PVC
pvc := registry.GeneratePVC(cr, r.Scheme, labels)
log.Info("Creating a new PersistentVolumeClaim", "PersistentVolumeClaim.Namespace", pvc.Namespace, "PersistentVolumeClaim.Name", pvc.Name)
err = r.Create(ctx, pvc)
generatedResource := r.generateResourceObject(cr, resource, labels, ingressDomain)
log.Info("Creating a new "+resourceType, resourceType+".Namespace", cr.Namespace+".Name", resourceName)
err = r.Create(ctx, generatedResource)
if err != nil {
log.Error(err, "Failed to create new PersistentVolumeClaim", "PersistentVolumeClaim.Namespace", pvc.Namespace, "PersistentVolumeClaim.Name", pvc.Name)
log.Error(err, "Failed to create new "+resourceType, resourceType+".Namespace", cr.Namespace, "Service.Name", cr.Namespace+".Name", resourceName)
return &ctrl.Result{}, err
}
return nil, nil
} else if err != nil {
log.Error(err, "Failed to get PersistentVolumeClaim")
log.Error(err, "Failed to get "+resourceType)
return &ctrl.Result{}, err
}

return nil, nil
}

func (r *DevfileRegistryReconciler) ensureRoute(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, labels map[string]string) (*reconcile.Result, error) {
route := &routev1.Route{}
err := r.Get(ctx, types.NamespacedName{Name: registry.DevfilesRouteName(cr.Name), Namespace: cr.Namespace}, route)
if err != nil && errors.IsNotFound(err) {
// Define a new route exposing the devfile registry index
route := registry.GenerateRoute(cr, r.Scheme, labels)
log.Info("Creating a new Route", "Route.Namespace", route.Namespace, "Route.Name", route.Name)
err = r.Create(ctx, route)
if err != nil {
log.Error(err, "Failed to create new Route", "Route.Namespace", route.Namespace, "Route.Name", route.Name)
return &ctrl.Result{}, err
}
return nil, nil
} else if err != nil {
log.Error(err, "Failed to get Route")
return &ctrl.Result{}, err
// Update the given resource, if needed
// At this moment, only registry deployments, routes and ingresses need to be updated.
switch resource.(type) {
case *appsv1.Deployment:
dep, _ := resource.(*appsv1.Deployment)
err = r.updateDeployment(ctx, cr, dep)
case *routev1.Route:
route, _ := resource.(*routev1.Route)
err = r.updateRoute(ctx, cr, route)
case *v1beta1.Ingress:
ingress, _ := resource.(*v1beta1.Ingress)
err = r.updateIngress(ctx, cr, ingressDomain, ingress)
}

err = r.updateRoute(ctx, cr, route)
if err != nil {
log.Error(err, "Failed to update Route")
log.Error(err, "Failed to update "+resourceType)
return &ctrl.Result{}, err
}

return nil, nil
}

func (r *DevfileRegistryReconciler) ensureIngress(ctx context.Context, cr *registryv1alpha1.DevfileRegistry, hostname string, labels map[string]string) (*reconcile.Result, error) {
ingress := &v1beta1.Ingress{}
err := r.Get(ctx, types.NamespacedName{Name: registry.IngressName(cr.Name), Namespace: cr.Namespace}, ingress)
if err != nil && errors.IsNotFound(err) {
// Define a new ingress exposing the devfile index and oci registry
ingress = registry.GenerateIngress(cr, hostname, r.Scheme, labels)
log.Info("Creating a new Ingress", "Ingress.Namespace", ingress.Namespace, "Ingress.Name", ingress.Name)
err = r.Create(ctx, ingress)
if err != nil {
log.Error(err, "Failed to create new Ingress", "Ingress.Namespace", ingress.Namespace, "Ingress.Name", ingress.Name)
return &ctrl.Result{}, err
}
return nil, nil
} else if err != nil {
log.Error(err, "Failed to get Ingress")
return &ctrl.Result{}, err
func getResourceName(resource runtime.Object, crName string) string {
switch resource.(type) {
case *appsv1.Deployment:
return registry.DeploymentName(crName)
case *corev1.ConfigMap:
return registry.ConfigMapName(crName)
case *corev1.PersistentVolumeClaim:
return registry.PVCName(crName)
case *corev1.Service:
return registry.ServiceName(crName)
case *routev1.Route, *v1beta1.Ingress:
return registry.IngressName(crName)
}
return registry.GenericResourceName(crName)
}

err = r.updateIngress(ctx, cr, hostname, ingress)
if err != nil {
log.Error(err, "Failed to update Ingress")
return &ctrl.Result{}, err
func (r *DevfileRegistryReconciler) generateResourceObject(cr *registryv1alpha1.DevfileRegistry, resource runtime.Object, labels map[string]string, ingressDomain string) runtime.Object {
switch resource.(type) {
case *appsv1.Deployment:
return registry.GenerateDeployment(cr, r.Scheme, labels)
case *corev1.ConfigMap:
return registry.GenerateOCIRegistryConfigMap(cr, r.Scheme, labels)
case *corev1.PersistentVolumeClaim:
return registry.GeneratePVC(cr, r.Scheme, labels)
case *corev1.Service:
return registry.GenerateService(cr, r.Scheme, labels)
case *routev1.Route:
return registry.GenerateRoute(cr, r.Scheme, labels)
case *v1beta1.Ingress:
return registry.GenerateIngress(cr, ingressDomain, r.Scheme, labels)
}
return nil, nil
return nil
}
56 changes: 56 additions & 0 deletions pkg/registry/configmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// Copyright (c) 2020 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation

package registry

import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"

registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
)

// GenerateOCIRegistryConfigMap returns a configmap that is used to configure the OCI registry container
func GenerateOCIRegistryConfigMap(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Scheme, labels map[string]string) *corev1.ConfigMap {
configMapData := make(map[string]string, 0)

registryConfig := `
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
debug:
addr: :5001
prometheus:
enabled: true
path: /metrics`

configMapData["registry-config.yml"] = registryConfig

cm := &corev1.ConfigMap{
ObjectMeta: generateObjectMeta(ConfigMapName(cr.Name), cr.Namespace, labels),
Data: configMapData,
}

// Set DevfileRegistry instance as the owner and controller
ctrl.SetControllerReference(cr, cm, scheme)
return cm
}
6 changes: 4 additions & 2 deletions pkg/registry/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const (
DevfileRegistryTLSEnabled = true

// Defaults/constants for devfile registry services
DevfileIndexPortName = "devfile-registry-metadata"
DevfileIndexPort = 8080
DevfileIndexPortName = "devfile-registry-metadata"
DevfileIndexPort = 8080
DevfileMetricsPortName = "devfile-registry-metrics"
DevfileMetricsPort = 5001
)

func GetOCIRegistryImage(cr *registryv1alpha1.DevfileRegistry) string {
Expand Down
21 changes: 21 additions & 0 deletions pkg/registry/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ func GenerateDeployment(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Sc
Name: DevfileRegistryVolumeName,
MountPath: "/var/lib/registry",
},
{
Name: "config",
MountPath: "/etc/docker/registry",
ReadOnly: true,
},
},
},
},
Expand All @@ -98,6 +103,22 @@ func GenerateDeployment(cr *registryv1alpha1.DevfileRegistry, scheme *runtime.Sc
Name: DevfileRegistryVolumeName,
VolumeSource: GetDevfileRegistryVolumeSource(cr),
},
{
Name: "config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ConfigMapName(cr.Name),
},
Items: []corev1.KeyToPath{
{
Key: "registry-config.yml",
Path: "config.yml",
},
},
},
},
},
},
},
},
Expand Down
Loading