Skip to content

Commit cbb94cc

Browse files
authored
Avoid errors removing parent ref from deleted resources (#405)
1 parent 3a59b21 commit cbb94cc

File tree

1 file changed

+63
-33
lines changed

1 file changed

+63
-33
lines changed

lib/CompositeController.js

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const yaml = require('js-yaml');
2626

2727
const BaseController = require('./BaseController');
2828

29+
const moduleName = 'razeedeploy-core.lib.CompositeController';
2930

3031
module.exports = class CompositeController extends BaseController {
3132
constructor(params) {
@@ -34,19 +35,22 @@ module.exports = class CompositeController extends BaseController {
3435
}
3536

3637
async finalizerCleanup() {
38+
const methodName = `${moduleName}.finalizerCleanup()`;
39+
this.log.info(`${methodName} entry`);
40+
3741
// if cleanup fails, do not return successful response => Promise.reject(err) or throw Error(err)
3842
let children = objectPath.get(this.data, ['object', 'status', 'children'], {});
3943
let res = await Promise.all(Object.entries(children).map(async ([selfLink, child]) => {
4044
try {
4145
let reconcile = objectPath.get(child, ['deploy.razee.io/Reconcile'], this.reconcileDefault);
4246
if (reconcile.toLowerCase() == 'true') {
4347
// If child is to be Reconciled, delete it
44-
this.log.info(`finalizer: ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. removing from cluster`);
48+
this.log.info(`${methodName} finalizer: ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. removing from cluster`);
4549
await this._deleteChild(selfLink);
4650
}
4751
else {
4852
// If child is NOT to be Reconciled, remove the parent reference
49-
this.log.info(`finalizer: ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. leaving on cluster`);
53+
this.log.info(`${methodName} finalizer: ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. leaving on cluster`);
5054
await this._patchChild(selfLink);
5155
}
5256
let res = await this.patchSelf({
@@ -84,13 +88,15 @@ module.exports = class CompositeController extends BaseController {
8488
}
8589

8690
async applyChild(child) {
91+
const methodName = `${moduleName}.applyChild(child)`;
92+
8793
const childApiVersion = objectPath.get(child, 'apiVersion');
8894
const childKind = objectPath.get(child, 'kind');
8995
const childName = objectPath.get(child, 'metadata.name');
9096
let childNamespace = objectPath.get(child, 'metadata.namespace');
9197
let childUri = `${childApiVersion}/${childKind}/${childNamespace ? `namespace/${childNamespace}/` : ''}${childName}`;
9298

93-
this.log.info(`applyChild entry: ${childUri}`);
99+
this.log.info(`${methodName} entry, childUri: ${childUri}`);
94100

95101
if (!childApiVersion || !childKind) {
96102
return {
@@ -189,33 +195,35 @@ module.exports = class CompositeController extends BaseController {
189195
res = await this.apply(krm, child);
190196
}
191197
if (res.body == 'Multiple Parents') {
192-
this.log.warn(`Child already managed by another parent. Skipping addChildren for ${childUri}`);
198+
this.log.warn(`${methodName} Child already managed by another parent. Skipping addChildren for ${childUri}`);
193199
} else {
194-
this.log.info(`applyChild successful, adding to children: ${modeUsed} ${res.statusCode} ${childUri}`);
200+
this.log.info(`${methodName} patch successful, adding to children: ${modeUsed} ${res.statusCode} ${childUri}`);
195201
await this.addChildren({ uid: childUid, selfLink: childUri, 'deploy.razee.io/Reconcile': reconcile, 'Impersonate-User': impersonateUser });
196202
}
197-
this.log.info(`applyChild complete: ${childUri} -- ${res.statusCode}`);
203+
this.log.info(`${methodName} complete: ${childUri} -- ${res.statusCode}`);
198204
} catch (e) {
199-
this.log.warn(`applyChild error: ${childUri} -- ${e.message || e}`);
205+
this.log.warn(`${methodName} error: ${childUri} -- ${e.message || e}`);
200206
res = e;
201207
}
202208
return res;
203209
}
204210

205211
async reconcileChildren() {
212+
const methodName = `${moduleName}.reconcileChildren()`;
213+
206214
let newChildren = this.children; // children that were computed this cycle
207215
let oldChildren = objectPath.get(this.data, ['object', 'status', 'children'], {}); // children that existed at the start of the cycle
208216

209217
if (Object.entries(newChildren).length < Object.entries(oldChildren).length) {
210-
this.log.info(`Less children found this cycle then previously (${Object.entries(newChildren).length} < ${Object.entries(oldChildren).length}).. ReconcileChildren called by ${objectPath.get(this.data, ['object', 'metadata', 'selfLink'])}`);
218+
this.log.info(`${methodName} Less children found this cycle then previously (${Object.entries(newChildren).length} < ${Object.entries(oldChildren).length}).. ReconcileChildren called by ${objectPath.get(this.data, ['object', 'metadata', 'selfLink'])}`);
211219
}
212220

213221
let res = await Promise.all(Object.entries(oldChildren).map(async ([selfLink, child]) => {
214222
const newChild = clone(child);
215223
let reconcile = objectPath.get(child, ['deploy.razee.io/Reconcile'], this.reconcileDefault);
216224
let exists = objectPath.has(newChildren, [selfLink]);
217225
if (!exists && reconcile.toLowerCase() == 'true') {
218-
this.log.info(`${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. removing from cluster`);
226+
this.log.info(`${methodName} ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. removing from cluster`);
219227
try {
220228
await this._deleteChild(selfLink);
221229
let res = await this.patchSelf({
@@ -236,7 +244,7 @@ module.exports = class CompositeController extends BaseController {
236244
await this.addChildren(newChild);
237245
}
238246
} else if (!exists) {
239-
this.log.info(`${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. leaving on cluster`);
247+
this.log.info(`${methodName} ${selfLink} no longer applied.. Reconcile ${reconcile.toLowerCase()}.. leaving on cluster`);
240248
await this._patchChild(selfLink);
241249
let res = await this.patchSelf({
242250
status: {
@@ -252,43 +260,65 @@ module.exports = class CompositeController extends BaseController {
252260

253261
}
254262

255-
async _deleteChild(child) {
256-
this.log.info(`Delete ${child}`);
257-
let opt = { uri: child, simple: false, resolveWithFullResponse: true, method: 'DELETE' };
263+
// Delete the child resource
264+
// Calling code does not check a return value
265+
async _deleteChild(childURI) {
266+
const methodName = `${moduleName}._deleteChild('${childURI}')`;
267+
this.log.info(`${methodName} entry`);
258268

269+
let opt = { uri: childURI, simple: false, resolveWithFullResponse: true, method: 'DELETE' };
259270
let res = await this.kubeResourceMeta.request(opt);
260-
if (res.statusCode === 404) {
261-
this.log.debug(`Delete ${res.statusCode} ${opt.uri || opt.url}`);
262-
return { statusCode: res.statusCode, body: res.body };
263-
} else if (res.statusCode !== 200) {
264-
this.log.debug(`Delete ${res.statusCode} ${opt.uri || opt.url}`);
271+
if (res.statusCode === 404 || res.statusCode === 200) {
272+
this.log.info(`${methodName} child deleted (RC: ${res.statusCode})`);
273+
}
274+
else {
275+
this.log.warn(`${methodName} child could not be deleted (RC: ${res.statusCode}): ${res.body}`);
265276
return Promise.reject({ statusCode: res.statusCode, body: res.body });
266277
}
267-
this.log.debug(`Delete ${res.statusCode} ${opt.uri || opt.url}`);
268-
return { statusCode: res.statusCode, body: res.body };
269278
}
270279

271-
async _patchChild(child) {
272-
this.log.info(`Patch ${child}`);
273-
const opt = { uri: child, simple: false, resolveWithFullResponse: true, method: 'GET' };
274-
const get = await this.kubeResourceMeta.request(opt);
275-
const file = yaml.loadAll(get.body)[0];
276-
const childApiVersion = objectPath.get(file, 'apiVersion');
277-
const childKind = objectPath.get(file, 'kind');
278-
const namespace = objectPath.get(file, ['metadata', 'namespace']);
279-
const name = objectPath.get(file, ['metadata', 'name']);
280-
const krm = await this.kubeClass.getKubeResourceMeta(childApiVersion, childKind, 'update');
280+
// Patch the child resource to remove the `deploy.razee.io.parent` annotation
281+
// Calling code does not check a return value
282+
async _patchChild(childURI) {
283+
const methodName = `${moduleName}._patchChild('${childURI}')`;
284+
this.log.info(`${methodName} entry`);
285+
286+
// Retrieve the child resource to get version/kind/namespace/name details
287+
const opt = { uri: childURI, simple: false, resolveWithFullResponse: true, method: 'GET' };
288+
const getChildResponse = await this.kubeResourceMeta.request(opt);
289+
if( getChildResponse.statusCode === 404 ) {
290+
this.log.info(`${methodName} child no longer exists (RC: ${getChildResponse.statusCode})`);
291+
return;
292+
}
293+
294+
const childResource = yaml.loadAll(getChildResponse.body)[0];
295+
let childApiVersion = objectPath.get(childResource, 'apiVersion');
296+
let childKind = objectPath.get(childResource, 'kind');
297+
let childNamespace = objectPath.get(childResource, ['metadata', 'namespace']);
298+
let childName = objectPath.get(childResource, ['metadata', 'name']);
281299

300+
// Get the Kube api krm for the child resource
301+
let krm = await this.kubeClass.getKubeResourceMeta(childApiVersion, childKind, 'update');
302+
if( !krm ) {
303+
this.log.warn(`${methodName} unable to get 'update' api for child. Child GET response (RC: ${getChildResponse.statusCode}): ${getChildResponse.body}`);
304+
return;
305+
}
306+
307+
// Remove the parent ref from the child
282308
const patchObj = {
283309
metadata: {
284310
annotations: {
285311
'deploy.razee.io.parent': null
286312
}
287313
}
288314
};
289-
let res = await krm.mergePatch(name, namespace, patchObj, {simple: false, resolveWithFullResponse: true});
290-
291-
return { statusCode: res.statusCode, body: res.body };
315+
let patchRes = await krm.mergePatch(childName, childNamespace, patchObj, {simple: false, resolveWithFullResponse: true});
292316

317+
if( patchRes.statusCode >= 200 && patchRes.statusCode < 300 ) {
318+
this.log.info(`${methodName} child patched (RC: ${patchRes.statusCode})`);
319+
}
320+
else {
321+
this.log.warn(`${methodName} child patch failure (RC: ${patchRes.statusCode}): ${patchRes.body}`);
322+
}
293323
}
294324
};

0 commit comments

Comments
 (0)