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

feat: support idling and unidling from api with idled status #3628

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
10 changes: 8 additions & 2 deletions node-packages/commons/src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1196,9 +1196,7 @@ export const createMiscTask = async function(taskData: any) {
data: { project }
} = taskData;

// handle any controller based misc tasks
let updatedKey = `deploytarget:${key}`;
let taskId = 'misc-kubernetes';
// determine the deploy target (openshift/kubernetes) for the task to go to
// we get this from the environment
const result = await getOpenShiftInfoForEnvironment(taskData.data.environment.id);
Expand Down Expand Up @@ -1373,6 +1371,14 @@ export const createMiscTask = async function(taskData: any) {
// build cancellation is just a standard unmodified message
miscTaskData.misc = taskData.data.build
break;
case 'deploytarget:environment:idling':
// environment idling is used to handle idling or unidling of an an environment
miscTaskData.idling = taskData.data.idling
break;
case 'deploytarget:environment:service':
// environment service is used to handle stop, start, or restarting of a service in an environment
miscTaskData.lagoonService = taskData.data.lagoonService
break;
default:
miscTaskData.misc = taskData.data.build
break;
Expand Down
2 changes: 1 addition & 1 deletion services/actions-handler/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22

require (
github.com/cheshir/go-mq/v2 v2.0.1
github.com/uselagoon/machinery v0.0.29
github.com/uselagoon/machinery v0.0.32-0.20241127011940-ca6f27c94cef
gopkg.in/matryer/try.v1 v1.0.0-20150601225556-312d2599e12e
)

Expand Down
4 changes: 2 additions & 2 deletions services/actions-handler/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,8 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/uselagoon/machinery v0.0.29 h1:invFIPv1Z1xCt8/1ilbiNDuAEPrb+AUO21BnNG+CX8c=
github.com/uselagoon/machinery v0.0.29/go.mod h1:X0qguIO9skumMhhT0ap5CKHulKgYzy3TiIn+xlwiFQc=
github.com/uselagoon/machinery v0.0.32-0.20241127011940-ca6f27c94cef h1:Oz4+8nG3qdOavCijt/3ZXGOpQj+6Nf6KGwZExWWRXvw=
github.com/uselagoon/machinery v0.0.32-0.20241127011940-ca6f27c94cef/go.mod h1:RsHzIMOam3hiA4CKR12yANgzdTGy6tz4D19umjMzZyw=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
Expand Down
94 changes: 94 additions & 0 deletions services/actions-handler/handler/controller_idling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package handler

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"time"

mq "github.com/cheshir/go-mq/v2"
"github.com/uselagoon/machinery/api/lagoon"
lclient "github.com/uselagoon/machinery/api/lagoon/client"
"github.com/uselagoon/machinery/api/schema"
"github.com/uselagoon/machinery/utils/jwt"
)

type Idled struct {
Idled bool `json:"idled"`
}

func (m *Messenger) handleIdling(ctx context.Context, messageQueue *mq.MessageQueue, message *schema.LagoonMessage, messageID string) error {
prefix := fmt.Sprintf("(messageid:%s) %s: ", messageID, message.Namespace)
log.Println(fmt.Sprintf("%sreceived idling environment status update", prefix))
// generate a lagoon token with a expiry of 60 seconds from now
token, err := jwt.GenerateAdminToken(m.LagoonAPI.TokenSigningKey, m.LagoonAPI.JWTAudience, m.LagoonAPI.JWTSubject, m.LagoonAPI.JWTIssuer, time.Now().Unix(), 60)
if err != nil {
// the token wasn't generated
if m.EnableDebug {
log.Println(fmt.Sprintf("ERROR: unable to generate token: %v", err))
}
return nil
}
// set up a lagoon client for use in the following process
l := lclient.New(m.LagoonAPI.Endpoint, "actions-handler", m.LagoonAPI.Version, &token, false)
var environmentID uint
// determine the environment id from the message
if message.Meta.ProjectID == nil && message.Meta.EnvironmentID == nil {
project, err := lagoon.GetMinimalProjectByName(ctx, message.Meta.Project, l)
if err != nil {
// send the log to the lagoon-logs exchange to be processed
m.toLagoonLogs(messageQueue, map[string]interface{}{
"severity": "error",
"event": fmt.Sprintf("actions-handler:%s:failed", "updateEnvironment"),
"meta": project,
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to get project - %v", prefix, err))
}
return err
}
environment, err := lagoon.GetEnvironmentByName(ctx, message.Meta.Environment, project.ID, l)
if err != nil {
// send the log to the lagoon-logs exchange to be processed
m.toLagoonLogs(messageQueue, map[string]interface{}{
"severity": "error",
"event": fmt.Sprintf("actions-handler:%s:failed", "updateEnvironment"),
"meta": project,
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to get environment - %v", prefix, err))
}
return err
}
environmentID = environment.ID
} else {
// pull the id from the message
environmentID = *message.Meta.EnvironmentID
}
decodeData, _ := base64.StdEncoding.DecodeString(message.Meta.AdvancedData)
idled := &Idled{}
json.Unmarshal(decodeData, idled)
updateEnvironmentPatch := schema.UpdateEnvironmentPatchInput{
Idled: &idled.Idled,
}
updateEnvironment, err := lagoon.UpdateEnvironment(ctx, environmentID, updateEnvironmentPatch, l)
if err != nil {
// send the log to the lagoon-logs exchange to be processed
m.toLagoonLogs(messageQueue, map[string]interface{}{
"severity": "error",
"event": fmt.Sprintf("actions-handler:%s:failed", "updateDeployment"),
"meta": updateEnvironment,
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to update environment - %v", prefix, err))
}
return err
}
log.Println(fmt.Sprintf("%supdated environment", prefix))
return nil
}
16 changes: 8 additions & 8 deletions services/actions-handler/handler/controller_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ import (

func (m *Messenger) handleTask(ctx context.Context, messageQueue *mq.MessageQueue, message *schema.LagoonMessage, messageID string) error {
prefix := fmt.Sprintf("(messageid:%s) %s/%s: ", messageID, message.Namespace, message.Meta.Task.Name)
log.Println(fmt.Sprintf("%sreceived task status update: %s", prefix, message.Meta.JobStatus))
log.Printf("%sreceived task status update: %s", prefix, message.Meta.JobStatus)
// generate a lagoon token with a expiry of 60 seconds from now
token, err := jwt.GenerateAdminToken(m.LagoonAPI.TokenSigningKey, m.LagoonAPI.JWTAudience, m.LagoonAPI.JWTSubject, m.LagoonAPI.JWTIssuer, time.Now().Unix(), 60)
if err != nil {
// the token wasn't generated
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to generate token: %v", prefix, err))
log.Printf("%sERROR: unable to generate token: %v", prefix, err)
}
return nil
}
Expand All @@ -48,7 +48,7 @@ func (m *Messenger) handleTask(ctx context.Context, messageQueue *mq.MessageQueu
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to project information: %v", prefix, err))
log.Printf("%sERROR: unable to project information: %v", prefix, err)
}
return err
}
Expand All @@ -70,7 +70,7 @@ func (m *Messenger) handleTask(ctx context.Context, messageQueue *mq.MessageQueu
updateProject.StandbyProductionEnvironment = &advTask.ProductionEnvironment
}
// update the project in the api
updatedProject, err := lagoon.UpdateProject(ctx, int(project.ID), updateProject, l)
updatedProject, err := lagoon.UpdateProject(ctx, project.ID, updateProject, l)
if err != nil {
// send the log to the lagoon-logs exchange to be processed
m.toLagoonLogs(messageQueue, map[string]interface{}{
Expand All @@ -80,11 +80,11 @@ func (m *Messenger) handleTask(ctx context.Context, messageQueue *mq.MessageQueu
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to update project with active/standby result: %v", prefix, err))
log.Printf("%sERROR: unable to update project with active/standby result: %v", prefix, err)
}
return err
}
log.Println(fmt.Sprintf("%supdated project %s with active/standby result: %v", prefix, message.Meta.Project, "success"))
log.Printf("%supdated project %s with active/standby result: %v", prefix, message.Meta.Project, "success")
}
}
// continue on to updating the task as normal
Expand Down Expand Up @@ -113,10 +113,10 @@ func (m *Messenger) handleTask(ctx context.Context, messageQueue *mq.MessageQueu
"message": err.Error(),
})
if m.EnableDebug {
log.Println(fmt.Sprintf("%sERROR: unable to update task: %v", prefix, err))
log.Printf("%sERROR: unable to update task: %v", prefix, err)
}
return err
}
log.Println(fmt.Sprintf("%supdated task: %s", prefix, message.Meta.JobStatus))
log.Printf("%supdated task: %s", prefix, message.Meta.JobStatus)
return nil
}
2 changes: 2 additions & 0 deletions services/actions-handler/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ func (m *Messenger) Consumer() {
err = m.handleRemoval(ctx, messageQueue, logMsg, messageID)
case "task":
err = m.handleTask(ctx, messageQueue, logMsg, messageID)
case "idling":
err = m.handleIdling(ctx, messageQueue, logMsg, messageID)
}
// if there aren't any errors, then ack the message, an error indicates that there may have been an issue with the api handling the request
// skipping this means the message will remain in the queue
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = async function(knex) {
return knex.schema
.alterTable('environment', (table) => {
table.boolean('idled').notNullable().defaultTo(0);
})
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = async function(knex) {
return knex.schema
.alterTable('environment', (table) => {
table.dropColumn('idled');
})
};
4 changes: 3 additions & 1 deletion services/api/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const {
getEnvironmentByServiceId,
getServiceContainersByServiceId,
deleteEnvironmentService,
environmentIdling,
} = require('./resources/environment/resolvers');

const {
Expand Down Expand Up @@ -705,7 +706,8 @@ const resolvers = {
removeUserFromOrganizationGroups,
bulkImportProjectsAndGroupsToOrganization,
addOrUpdateEnvironmentService,
deleteEnvironmentService
deleteEnvironmentService,
environmentIdling,
},
Subscription: {
backupChanged: backupSubscriber,
Expand Down
Loading