Skip to content

Commit

Permalink
Merge pull request #148 from uselagoon/support-activestandby
Browse files Browse the repository at this point in the history
  • Loading branch information
shreddedbacon authored Jan 31, 2023
2 parents d3c777e + aebd521 commit a9c5416
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 89 deletions.
84 changes: 0 additions & 84 deletions controllers/v1beta1/build_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,90 +303,6 @@ func (r *LagoonBuildReconciler) getOrCreateConfigMap(ctx context.Context, cmName
return nil
}

// getOrCreatePromoteSARoleBinding will create the rolebinding for openshift promotions to be used by the lagoon-deployer service account.
// @TODO: this role binding can be used as a basis for active/standby tasks allowing one ns to work in another ns
func (r *LagoonBuildReconciler) getOrCreatePromoteSARoleBinding(ctx context.Context, sourcens string, ns string) error {
viewRoleBinding := &rbacv1.RoleBinding{}
viewRoleBinding.ObjectMeta = metav1.ObjectMeta{
Name: fmt.Sprintf("%s-lagoon-deployer-view", ns),
Namespace: sourcens,
}
viewRoleBinding.RoleRef = rbacv1.RoleRef{
Name: "view",
Kind: "ClusterRole",
APIGroup: "rbac.authorization.k8s.io",
}
viewRoleBinding.Subjects = []rbacv1.Subject{
{
Name: "lagoon-deployer",
Kind: "ServiceAccount",
Namespace: ns,
},
}
err := r.Get(ctx, types.NamespacedName{
Namespace: sourcens,
Name: fmt.Sprintf("%s-lagoon-deployer-view", ns),
}, viewRoleBinding)
if err != nil {
if err := r.Create(ctx, viewRoleBinding); err != nil {
return fmt.Errorf("There was an error creating the deployer view role binding. Error was: %v", err)
}
}
imagePullRoleBinding := &rbacv1.RoleBinding{}
imagePullRoleBinding.ObjectMeta = metav1.ObjectMeta{
Name: fmt.Sprintf("%s-lagoon-deployer-image-puller", ns),
Namespace: sourcens,
}
imagePullRoleBinding.RoleRef = rbacv1.RoleRef{
Name: "system:image-puller",
Kind: "ClusterRole",
APIGroup: "rbac.authorization.k8s.io",
}
imagePullRoleBinding.Subjects = []rbacv1.Subject{
{
Name: "lagoon-deployer",
Kind: "ServiceAccount",
Namespace: ns,
},
}
err = r.Get(ctx, types.NamespacedName{
Namespace: sourcens,
Name: fmt.Sprintf("%s-lagoon-deployer-image-puller", ns),
}, imagePullRoleBinding)
if err != nil {
if err := r.Create(ctx, imagePullRoleBinding); err != nil {
return fmt.Errorf("There was an error creating the image puller role binding. Error was: %v", err)
}
}
defaultImagePullRoleBinding := &rbacv1.RoleBinding{}
defaultImagePullRoleBinding.ObjectMeta = metav1.ObjectMeta{
Name: fmt.Sprintf("%s-lagoon-deployer-default-image-puller", ns),
Namespace: sourcens,
}
defaultImagePullRoleBinding.RoleRef = rbacv1.RoleRef{
Name: "system:image-puller",
Kind: "ClusterRole",
APIGroup: "rbac.authorization.k8s.io",
}
defaultImagePullRoleBinding.Subjects = []rbacv1.Subject{
{
Name: "lagoon-deployer",
Kind: "ServiceAccount",
Namespace: ns,
},
}
err = r.Get(ctx, types.NamespacedName{
Namespace: sourcens,
Name: fmt.Sprintf("%s-lagoon-deployer-default-image-puller", ns),
}, defaultImagePullRoleBinding)
if err != nil {
if err := r.Create(ctx, defaultImagePullRoleBinding); err != nil {
return fmt.Errorf("There was an error creating the image puller role binding. Error was: %v", err)
}
}
return nil
}

// processBuild will actually process the build.
func (r *LagoonBuildReconciler) processBuild(ctx context.Context, opLog logr.Logger, lagoonBuild lagoonv1beta1.LagoonBuild) error {
// we run these steps again just to be sure that it gets updated/created if it hasn't already
Expand Down
15 changes: 15 additions & 0 deletions controllers/v1beta1/podmonitor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"io"
"strconv"

"github.com/go-logr/logr"
lagoonv1beta1 "github.com/uselagoon/remote-controller/apis/lagoon/v1beta1"
Expand Down Expand Up @@ -78,6 +79,20 @@ func (r *LagoonMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Reques
// pod is not being deleted
return ctrl.Result{}, r.handleTaskMonitor(ctx, opLog, req, jobPod)
}
// pod deletion request came through, check if this is an activestandby task, if it is, delete the activestandby role
if value, ok := jobPod.ObjectMeta.Labels["lagoon.sh/activeStandby"]; ok {
isActiveStandby, _ := strconv.ParseBool(value)
if isActiveStandby {
var destinationNamespace string
if value, ok := jobPod.ObjectMeta.Labels["lagoon.sh/activeStandbyDestinationNamespace"]; ok {
destinationNamespace = value
}
err := r.deleteActiveStandbyRole(ctx, destinationNamespace)
if err != nil {
return ctrl.Result{}, err
}
}
}
}
// if this is a lagoon build, then run the handle build monitoring process
if jobPod.ObjectMeta.Labels["lagoon.sh/jobType"] == "build" {
Expand Down
29 changes: 27 additions & 2 deletions controllers/v1beta1/task_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"regexp"
"strconv"

"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -88,7 +89,7 @@ func (r *LagoonTaskReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// The object is being deleted
if helpers.ContainsString(lagoonTask.ObjectMeta.Finalizers, taskFinalizer) {
// our finalizer is present, so lets handle any external dependency
if err := r.deleteExternalResources(&lagoonTask, req.NamespacedName.Namespace); err != nil {
if err := r.deleteExternalResources(ctx, &lagoonTask, req.NamespacedName.Namespace); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return ctrl.Result{}, err
Expand Down Expand Up @@ -120,7 +121,7 @@ func (r *LagoonTaskReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *LagoonTaskReconciler) deleteExternalResources(lagoonTask *lagoonv1beta1.LagoonTask, namespace string) error {
func (r *LagoonTaskReconciler) deleteExternalResources(ctx context.Context, lagoonTask *lagoonv1beta1.LagoonTask, namespace string) error {
// delete any external resources if required
return nil
}
Expand Down Expand Up @@ -335,6 +336,30 @@ func (r *LagoonTaskReconciler) createStandardTask(ctx context.Context, lagoonTas
// createAdvancedTask allows running of more advanced tasks than the standard lagoon tasks
// see notes in the docs for infomration about advanced tasks
func (r *LagoonTaskReconciler) createAdvancedTask(ctx context.Context, lagoonTask *lagoonv1beta1.LagoonTask, opLog logr.Logger) error {
additionalLabels := map[string]string{}

// check if this is an activestandby task, if it is, create the activestandby role
if value, ok := lagoonTask.ObjectMeta.Labels["lagoon.sh/activeStandby"]; ok {
isActiveStandby, _ := strconv.ParseBool(value)
if isActiveStandby {
var sourceNamespace, destinationNamespace string
if value, ok := lagoonTask.ObjectMeta.Labels["lagoon.sh/activeStandbySourceNamespace"]; ok {
sourceNamespace = value
}
if value, ok := lagoonTask.ObjectMeta.Labels["lagoon.sh/activeStandbyDestinationNamespace"]; ok {
destinationNamespace = value
}
// create the role + binding to allow the service account to interact with both namespaces
err := r.createActiveStandbyRole(ctx, sourceNamespace, destinationNamespace)
if err != nil {
return err
}
additionalLabels["lagoon.sh/activeStandby"] = "true"
additionalLabels["lagoon.sh/activeStandbySourceNamespace"] = sourceNamespace
additionalLabels["lagoon.sh/activeStandbyDestinationNamespace"] = destinationNamespace
}
}

// handle the volumes for sshkey
sshKeyVolume := corev1.Volume{
Name: "lagoon-sshkey",
Expand Down
59 changes: 59 additions & 0 deletions controllers/v1beta1/task_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package v1beta1

import (
"context"
"fmt"

"github.com/uselagoon/remote-controller/internal/helpers"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

// createActiveStandbyRole will create the rolebinding for allowing lagoon-deployer to talk between namespaces for active/standby functionality
func (r *LagoonTaskReconciler) createActiveStandbyRole(ctx context.Context, sourceNamespace, destinationNamespace string) error {
activeStandbyRoleBinding := &rbacv1.RoleBinding{}
activeStandbyRoleBinding.ObjectMeta = metav1.ObjectMeta{
Name: "lagoon-deployer-activestandby",
Namespace: destinationNamespace,
}
activeStandbyRoleBinding.RoleRef = rbacv1.RoleRef{
Name: "admin",
Kind: "ClusterRole",
APIGroup: "rbac.authorization.k8s.io",
}
activeStandbyRoleBinding.Subjects = []rbacv1.Subject{
{
Name: "lagoon-deployer",
Kind: "ServiceAccount",
Namespace: sourceNamespace,
},
}
err := r.Get(ctx, types.NamespacedName{
Namespace: destinationNamespace,
Name: "lagoon-deployer-activestandby",
}, activeStandbyRoleBinding)
if err != nil {
if err := r.Create(ctx, activeStandbyRoleBinding); err != nil {
return fmt.Errorf("There was an error creating the lagoon-deployer-activestandby role binding. Error was: %v", err)
}
}
return nil
}

// deleteActiveStandbyRole
func (r *LagoonMonitorReconciler) deleteActiveStandbyRole(ctx context.Context, destinationNamespace string) error {
activeStandbyRoleBinding := &rbacv1.RoleBinding{}
err := r.Get(ctx, types.NamespacedName{
Namespace: destinationNamespace,
Name: "lagoon-deployer-activestandby",
}, activeStandbyRoleBinding)
if err != nil {
helpers.IgnoreNotFound(err)
}
err = r.Delete(ctx, activeStandbyRoleBinding)
if err != nil {
return fmt.Errorf("Unable to delete lagoon-deployer-activestandby role binding")
}
return nil
}
13 changes: 13 additions & 0 deletions internal/messenger/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,19 @@ func (m *Messenger) Consumer(targetName string) { //error {
message.Ack(false) // ack to remove from queue
return
}
case "deploytarget:task:activestandby":
opLog.Info(
fmt.Sprintf(
"Received activestandy switch for project %s",
jobSpec.Project.Name,
),
)
err := m.ActiveStandbySwitch(namespace, jobSpec)
if err != nil {
//@TODO: send msg back to lagoon and update task to failed?
message.Ack(false) // ack to remove from queue
return
}
default:
// if we get something that we don't know about, spit out the entire message
opLog.Info(
Expand Down
37 changes: 34 additions & 3 deletions internal/messenger/tasks_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
)

type ActiveStandbyPayload struct {
SourceNamespace string `json:"sourceNamespace"`
DestinationNamespace string `json:"destinationNamespace"`
}

// CancelBuild handles cancelling builds or handling if a build no longer exists.
func (m *Messenger) CancelBuild(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec) error {
opLog := ctrl.Log.WithName("handlers").WithName("LagoonTasks")
Expand Down Expand Up @@ -193,7 +198,24 @@ func (m *Messenger) IngressRouteMigration(namespace string, jobSpec *lagoonv1bet
// always set these to true for ingress migration tasks
jobSpec.AdvancedTask.DeployerToken = true
jobSpec.AdvancedTask.SSHKey = true
return createAdvancedTask(namespace, jobSpec, m)
return m.createAdvancedTask(namespace, jobSpec, nil)
}

// ActiveStandbySwitch handles running the active standby switch setup advanced task.
func (m *Messenger) ActiveStandbySwitch(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec) error {
// always set these to true for ingress migration tasks
jobSpec.AdvancedTask.DeployerToken = true
jobSpec.AdvancedTask.SSHKey = true
asPayload := &ActiveStandbyPayload{}
err := json.Unmarshal([]byte(jobSpec.AdvancedTask.JSONPayload), asPayload)
if err != nil {
return fmt.Errorf("Unable to unmarshal json payload: %v", err)
}
return m.createAdvancedTask(namespace, jobSpec, map[string]string{
"lagoon.sh/activeStandby": "true",
"lagoon.sh/activeStandbyDestinationNamespace": asPayload.DestinationNamespace,
"lagoon.sh/activeStandbySourceNamespace": asPayload.SourceNamespace,
})
}

// AdvancedTask handles running the ingress migrations.
Expand All @@ -204,11 +226,16 @@ func (m *Messenger) AdvancedTask(namespace string, jobSpec *lagoonv1beta1.Lagoon
if m.AdvancedTaskDeployTokenInjection {
jobSpec.AdvancedTask.DeployerToken = true
}
return createAdvancedTask(namespace, jobSpec, m)
return m.createAdvancedTask(namespace, jobSpec, nil)
}

// CreateAdvancedTask takes care of creating actual advanced tasks
func (m *Messenger) createAdvancedTask(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec, additionalLabels map[string]string) error {
return createAdvancedTask(namespace, jobSpec, m, additionalLabels)
}

// CreateAdvancedTask takes care of creating actual advanced tasks
func createAdvancedTask(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec, m *Messenger) error {
func createAdvancedTask(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec, m *Messenger, additionalLabels map[string]string) error {
opLog := ctrl.Log.WithName("handlers").WithName("LagoonTasks")
// create the advanced task
task := lagoonv1beta1.LagoonTask{
Expand All @@ -223,6 +250,10 @@ func createAdvancedTask(namespace string, jobSpec *lagoonv1beta1.LagoonTaskSpec,
},
Spec: *jobSpec,
}
// add additional labels if required
for key, value := range additionalLabels {
task.ObjectMeta.Labels[key] = value
}
if err := m.Client.Create(context.Background(), &task); err != nil {
opLog.Error(err,
fmt.Sprintf(
Expand Down

0 comments on commit a9c5416

Please sign in to comment.