Skip to content

Commit e5a91c9

Browse files
authored
Merge pull request #4006 from uselagoon/feature-envvar-updates
Feature - Environment Variable "Updated" field and pending update endpoint
2 parents 6be446a + c32d4d2 commit e5a91c9

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @param { import("knex").Knex } knex
3+
* @returns { Promise<void> }
4+
*/
5+
exports.up = function(knex) {
6+
return knex.schema
7+
.alterTable('env_vars', (table) => {
8+
table.datetime('updated').notNullable().defaultTo(knex.fn.now());
9+
})
10+
.raw("UPDATE env_vars SET updated='1970-01-01 00:00:00'");
11+
// Note, we do the above update so that all _existing_ env vars are
12+
// not picked up as needing to be deployed (since we're using 'updated' to track new/updated vars)
13+
// but any newly created items will get the _current_ date/time
14+
};
15+
16+
/**
17+
* @param { import("knex").Knex } knex
18+
* @returns { Promise<void> }
19+
*/
20+
exports.down = function(knex) {
21+
return knex.schema
22+
.alterTable('env_vars', (table) => {
23+
table.dropColumn('updated');
24+
})
25+
};

services/api/src/resolvers.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ const {
129129
deleteEnvironmentService,
130130
} = require('./resources/environment/resolvers');
131131

132+
const {
133+
getPendingChangesByEnvironmentId,
134+
} = require('./resources/environment/environment_redeploy')
135+
132136
const {
133137
getDeployTargetConfigById,
134138
getDeployTargetConfigsByProjectId,
@@ -522,6 +526,7 @@ async function getResolvers() {
522526
facts: getFactsByEnvironmentId,
523527
openshift: getOpenshiftByEnvironmentId,
524528
kubernetes: getOpenshiftByEnvironmentId,
529+
pendingChanges: getPendingChangesByEnvironmentId,
525530
},
526531
Organization: {
527532
groups: getGroupsByOrganizationId,

services/api/src/resources/env-variables/resolvers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ export const addOrUpdateEnvVariableByName: ResolverFn = async (
417417
}
418418
}
419419

420+
// Let's set the updated value for the env var
421+
updateData['updated'] = knex.fn.now();
422+
420423
const createOrUpdateSql = knex('env_vars')
421424
.insert({
422425
...updateData,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// This file contains the logic to determine whether an environment requires a redeploy
2+
3+
import * as R from 'ramda';
4+
import { sendToLagoonLogs } from '@lagoon/commons/dist/logs/lagoon-logger';
5+
import { createRemoveTask, seedNamespace } from '@lagoon/commons/dist/tasks';
6+
import { ResolverFn } from '..';
7+
import { logger } from '../../loggers/logger';
8+
import { isPatchEmpty, query, knex } from '../../util/db';
9+
import { convertDateToMYSQLDateFormat } from '../../util/convertDateToMYSQLDateTimeFormat';
10+
import { Helpers } from './helpers';
11+
import { Sql } from './sql';
12+
import { Sql as projectSql } from '../project/sql';
13+
import { Helpers as projectHelpers } from '../project/helpers';
14+
import { Helpers as openshiftHelpers } from '../openshift/helpers';
15+
import { Helpers as organizationHelpers } from '../organization/helpers';
16+
import { getFactFilteredEnvironmentIds } from '../fact/resolvers';
17+
import { getUserProjectIdsFromRoleProjectIds } from '../../util/auth';
18+
import { RemoveData, DeployType, AuditType } from '@lagoon/commons/dist/types';
19+
import { AuditLog } from '../audit/types';
20+
21+
22+
export const environmentPendingChangeTypes = {
23+
ENVVAR: "ENVVAR",
24+
};
25+
26+
export const getPendingChangesByEnvironmentId: ResolverFn = async(
27+
{
28+
id
29+
},
30+
_,
31+
{ sqlClientPool, hasPermission },
32+
) => {
33+
// Note: as it stands, the only pending changes we have now have to do
34+
// with env vars, but anything can be added in the form
35+
// {type:"string", details:"string", date: "string"}
36+
let pendingChanges = await getPendingEnvVarChanges(sqlClientPool, id);
37+
return pendingChanges;
38+
}
39+
40+
const getPendingEnvVarChanges = async(sqlClientPool, envId) => {
41+
const sql = `
42+
WITH last_completed AS (
43+
SELECT COALESCE(MAX(d.created), TIMESTAMP('1970-01-01 00:00:00')) AS ts
44+
FROM deployment d
45+
WHERE d.environment = ? AND d.status = 'complete'
46+
)
47+
SELECT *
48+
FROM (
49+
-- Environment-scoped
50+
SELECT
51+
e.id AS env_id,
52+
e.name AS env_name,
53+
ev.name AS envvar_name,
54+
ev.updated AS envvar_updated,
55+
'Environment' AS envvar_source
56+
FROM environment e
57+
JOIN env_vars ev ON ev.environment = e.id
58+
CROSS JOIN last_completed lc
59+
WHERE e.id = ?
60+
AND ev.name IS NOT NULL
61+
AND ev.updated > lc.ts
62+
63+
UNION ALL
64+
65+
-- Project-scoped
66+
SELECT
67+
e.id, e.name,
68+
ev.name, ev.updated,
69+
'Project' AS envvar_source
70+
FROM environment e
71+
JOIN project p ON p.id = e.project
72+
JOIN env_vars ev ON ev.project = p.id
73+
CROSS JOIN last_completed lc
74+
WHERE e.id = ?
75+
AND ev.name IS NOT NULL
76+
AND ev.updated > lc.ts
77+
78+
UNION ALL
79+
80+
-- Organization-scoped
81+
SELECT
82+
e.id, e.name,
83+
ev.name, ev.updated,
84+
'Organization' AS envvar_source
85+
FROM environment e
86+
JOIN project p ON p.id = e.project
87+
JOIN organization o ON o.id = p.organization
88+
JOIN env_vars ev ON ev.organization = o.id
89+
CROSS JOIN last_completed lc
90+
WHERE e.id = ?
91+
AND ev.name IS NOT NULL
92+
AND ev.updated > lc.ts
93+
) AS allenvs
94+
ORDER BY allenvs.envvar_updated DESC;
95+
`;
96+
97+
const results = await query(sqlClientPool, sql, [envId, envId, envId, envId]);
98+
99+
const pendingChanges = results.map(row => {
100+
return {type:environmentPendingChangeTypes.ENVVAR, details: `Variable name: ${row.envvarName} (source: ${row.envvarSource} )`, date: row.envvarUpdated};
101+
});
102+
103+
return pendingChanges;
104+
}

services/api/src/typeDefs.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,20 @@ const typeDefs = gql`
897897
openshiftProjectPattern: String @deprecated(reason: "No longer in use")
898898
kubernetes: Kubernetes
899899
kubernetesNamespacePattern: String @deprecated(reason: "No longer in use")
900+
"""
901+
Pending changes tell us if we need to redeploy an environment
902+
"""
903+
pendingChanges: [EnvironmentPendingChanges]
904+
}
905+
906+
type EnvironmentPendingChanges {
907+
type: EnvironmentPendingChangeType
908+
details: String
909+
date: String
910+
}
911+
912+
enum EnvironmentPendingChangeType {
913+
ENVVAR
900914
}
901915
902916
type EnvironmentHitsMonth {
@@ -1005,6 +1019,7 @@ const typeDefs = gql`
10051019
scope: String
10061020
name: String
10071021
value: String
1022+
updated: String
10081023
}
10091024
10101025
type Task {

0 commit comments

Comments
 (0)