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

Add ConsoleLink Creation/Remove #462

Merged
merged 2 commits into from
Oct 2, 2020
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
6 changes: 6 additions & 0 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
crmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
consolev1 "github.com/openshift/api/console/v1"
)

// Change below variables to serve metrics on different host or port.
Expand Down Expand Up @@ -118,6 +119,11 @@ func main() {
os.Exit(1)
}

if err := consolev1.AddToScheme(mgr.GetScheme()); err != nil {
log.Error(err, "")
os.Exit(1)
}

// Setup Scheme for OpenShift imagestreams and related
if err := imagev1.AddToScheme(mgr.GetScheme()); err != nil {
log.Error(err, "")
Expand Down
14 changes: 14 additions & 0 deletions deploy/cluster_role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
eguzki marked this conversation as resolved.
Show resolved Hide resolved
metadata:
name: 3scale-operator
rules:
- apiGroups:
- console.openshift.io
resources:
- consolelinks
verbs:
- create
- delete
- get
- update
12 changes: 12 additions & 0 deletions deploy/cluster_role_binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: 3scale-operator
subjects:
- kind: ServiceAccount
name: 3scale-operator
eguzki marked this conversation as resolved.
Show resolved Hide resolved
namespace: <placeholder>
roleRef:
kind: ClusterRole
name: 3scale-operator
apiGroup: rbac.authorization.k8s.io
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,18 @@ spec:
mediatype: image/png
install:
spec:
clusterPermissions:
- rules:
- apiGroups:
- console.openshift.io
resources:
- consolelinks
verbs:
- create
- delete
- get
- update
serviceAccountName: 3scale-operator
deployments:
- name: 3scale-operator
spec:
Expand Down
20 changes: 11 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ go 1.13

require (
github.com/3scale/3scale-porta-go-client v0.0.4
github.com/RHsyseng/operator-utils v0.0.0-20200204194854-c5b0d8533458
github.com/coreos/prometheus-operator v0.34.0
github.com/RHsyseng/operator-utils v0.0.0-20200506183821-e3b4a2ba9c30
github.com/coreos/prometheus-operator v0.35.1
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-bindata/go-bindata v3.1.1+incompatible
github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/go-logr/logr v0.1.0
github.com/go-openapi/spec v0.19.4
github.com/go-openapi/spec v0.19.6
github.com/go-playground/validator/v10 v10.2.0
github.com/google/go-cmp v0.3.1
github.com/integr8ly/grafana-operator/v3 v3.5.0
github.com/luci/go-render v0.0.0-20160219211803-9a04cc21af0f
github.com/mitchellh/go-homedir v1.1.0
github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible
github.com/openshift/client-go v0.0.0-20190923180330-3b6373338c9b
github.com/openshift/client-go v3.9.0+incompatible
github.com/operator-framework/operator-sdk v0.16.0
github.com/prometheus/client_golang v1.2.1
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.4.0
gopkg.in/yaml.v2 v2.2.4
k8s.io/api v0.0.0
k8s.io/apimachinery v0.0.0
gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v12.0.0+incompatible
k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d
sigs.k8s.io/controller-runtime v0.4.0
Expand Down Expand Up @@ -58,6 +58,8 @@ replace (

replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm

replace github.com/openshift/api => github.com/openshift/api v0.0.0-20190924102528-32369d4db2ad // Required until https://github.com/operator-framework/operator-lifecycle-manager/pull/1241 is resolved
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20200527184302-a843dc3262a0 // Required until https://github.com/operator-framework/operator-lifecycle-manager/pull/1241 is resolved

replace github.com/openshift/client-go => github.com/openshift/client-go v0.0.0-20191125132246-f6563a70e19a
eguzki marked this conversation as resolved.
Show resolved Hide resolved

replace github.com/operator-framework/operator-sdk => github.com/operator-framework/operator-sdk v0.15.2
150 changes: 144 additions & 6 deletions go.sum

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions pkg/controller/add_webconsole.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package controller

import (
"github.com/3scale/3scale-operator/pkg/controller/webconsole"
)

func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, webconsole.Add)
}
148 changes: 148 additions & 0 deletions pkg/controller/webconsole/webconsole_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package webconsole

import (
"context"
"strings"

"github.com/3scale/3scale-operator/pkg/common"
"github.com/3scale/3scale-operator/pkg/helper"
"github.com/3scale/3scale-operator/version"

"github.com/3scale/3scale-operator/pkg/reconcilers"
"github.com/go-logr/logr"
consolev1 "github.com/openshift/api/console/v1"
routev1 "github.com/openshift/api/route/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/discovery"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)

var (
// controllerName is the name of this controller
controllerName = "controller_webconsole"
log = logf.Log.WithName(controllerName)
)

// Add creates a new WebConsole Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
reconciler, err := newReconciler(mgr)
if err != nil {
return err
}
return add(mgr, reconciler)
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) (reconcile.Reconciler, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
if err != nil {
return nil, err
}

apiClientReader, err := common.NewAPIClientReader(mgr)
if err != nil {
return nil, err
}

client := mgr.GetClient()
scheme := mgr.GetScheme()
ctx := context.TODO()
recorder := mgr.GetEventRecorderFor(controllerName)
return &ReconcileWebConsole{
BaseReconciler: reconcilers.NewBaseReconciler(client, scheme, apiClientReader, ctx, log, discoveryClient, recorder),
}, nil
}

// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("webconsole-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}

// Watch for Route resources
err = c.Watch(&source.Kind{Type: &routev1.Route{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}

return nil
}

// blank assignment to verify that ReconcileWebConsole implements reconcile.Reconciler
var _ reconcile.Reconciler = &ReconcileWebConsole{}

// ReconcileWebConsole reconciles a WebConsole object
type ReconcileWebConsole struct {
*reconcilers.BaseReconciler
}

//Reconcile reads the state of the Routes and makes changes to the corresponding Consolelinks
func (r *ReconcileWebConsole) Reconcile(request reconcile.Request) (reconcile.Result, error) {
logger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
logger.Info("Reconciling ReconcileWebConsole", "Operator version", version.Version)

kindExists, err := r.HasConsoleLink()
if err != nil {
return reconcile.Result{}, err
}
if !kindExists {
logger.Info("Console link not supported in the cluster")
return reconcile.Result{}, nil
}

result, err := r.reconcileMasterLink(request, logger)
if err != nil {
return result, err
}
if result.Requeue {
logger.Info("Master link reconciled. Needs Requeueing.")
return result, nil
}

logger.V(1).Info("END")
return reconcile.Result{}, nil
}

func (r *ReconcileWebConsole) reconcileMasterLink(request reconcile.Request, logger logr.Logger) (reconcile.Result, error) {
if !strings.Contains(request.Name, "zync-3scale-master") {
// Nothing to do
return reconcile.Result{}, nil
}

route := &routev1.Route{}
err := r.Client().Get(r.Context(), request.NamespacedName, route)
if err != nil && !errors.IsNotFound(err) {
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}

if errors.IsNotFound(err) {
logger.V(1).Info("Master route not found", "name", request.Name)
// cluster-scoped resource must not have a namespace-scoped owner
// So consolelinks cannot have owners like apimanager or route object
// delete consolelink if exists
desired := &consolev1.ConsoleLink{
ObjectMeta: metav1.ObjectMeta{
Name: helper.GetMasterConsoleLinkName(request.Namespace),
},
}
common.TagObjectToDelete(desired)
err := r.ReconcileResource(&consolev1.ConsoleLink{}, desired, reconcilers.CreateOnlyMutator)
return reconcile.Result{}, err
}

logger.V(1).Info("Master route found", "name", request.Name)

err = r.ReconcileResource(&consolev1.ConsoleLink{}, helper.GetMasterConsoleLink(route), helper.GenericConsoleLinkMutator)
logger.V(1).Info("Reconcile master consolelink", "err", err)
return reconcile.Result{}, err
}
69 changes: 69 additions & 0 deletions pkg/helper/webconsole.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package helper

import (
"fmt"

"github.com/3scale/3scale-operator/pkg/common"
consolev1 "github.com/openshift/api/console/v1"
routev1 "github.com/openshift/api/route/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ConsoleLinkText is the text of the consoleLink shown on the webconsole
const ConsoleLinkText = "APIManager - 3scale"

// ConsoleLinkMasterNamePrefix is the prefix applied to Console link for system master
const ConsoleLinkMasterNamePrefix = "system-master-link"

//GenericConsoleLinkMutator performs the reconciliation for consolelink objects
func GenericConsoleLinkMutator(existingObj, desiredObj common.KubernetesObject) (bool, error) {
existing, ok := existingObj.(*consolev1.ConsoleLink)
if !ok {
return false, fmt.Errorf("%T is not a *consolev1.ConsoleLink", existingObj)
}
desired, ok := desiredObj.(*consolev1.ConsoleLink)
if !ok {
return false, fmt.Errorf("%T is not a *consolev1.ConsoleLink", desiredObj)
}

update := false

if existing.Spec.Href != desired.Spec.Href {
existing.Spec.Href = desired.Spec.Href
update = true
}

if existing.Spec.Text != desired.Spec.Text {
existing.Spec.Text = desired.Spec.Text
update = true
}

return update, nil
}

//GetMasterConsoleLink creates the consolelink obj for a target
func GetMasterConsoleLink(route *routev1.Route) *consolev1.ConsoleLink {
return &consolev1.ConsoleLink{
ObjectMeta: metav1.ObjectMeta{
Name: GetMasterConsoleLinkName(route.Namespace),
Labels: map[string]string{
"3scale.net/route-name": route.Name,
},
},
Spec: consolev1.ConsoleLinkSpec{
Link: consolev1.Link{
Text: ConsoleLinkText,
Href: "https://" + route.Spec.Host,
},
Location: consolev1.NamespaceDashboard,
NamespaceDashboard: &consolev1.NamespaceDashboardSpec{
Namespaces: []string{route.Namespace},
},
},
}
}

//GetMasterConsoleLinkName returns the consolelink name
func GetMasterConsoleLinkName(namespace string) string {
return fmt.Sprintf("%s-%s", ConsoleLinkMasterNamePrefix, namespace)
}
8 changes: 8 additions & 0 deletions pkg/reconcilers/base_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/3scale/3scale-operator/pkg/common"

"github.com/go-logr/logr"
consolev1 "github.com/openshift/api/console/v1"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -164,3 +166,9 @@ func (b *BaseReconciler) UpdateResourceStatus(obj common.KubernetesObject) error
b.Logger().Info(fmt.Sprintf("Updated status of object '%s/%s'", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), obj.GetName()))
return b.Client().Status().Update(context.TODO(), obj)
}

//HasConsoleLink checks if the ConsoleLink is supported in current cluster
func (b *BaseReconciler) HasConsoleLink() (bool, error) {
return k8sutil.ResourceExists(b.DiscoveryClient(),
consolev1.GroupVersion.String(), "ConsoleLink")
}