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

MigCluster : Use generateName to create unique secrets #759

Merged
merged 10 commits into from
Mar 23, 2020
61 changes: 45 additions & 16 deletions src/app/cluster/duck/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createTokenSecret,
createMigCluster,
updateTokenSecret,
getTokenSecretLabelSelector,
} from '../../../client/resources/conversions';

import { ClusterActions, ClusterActionTypes } from './actions';
Expand Down Expand Up @@ -87,9 +88,15 @@ function* removeClusterSaga(action) {
migMeta.configNamespace
);
const migClusterResource = new MigResource(MigResourceKind.MigCluster, migMeta.namespace);

const secretResourceList = yield client.list(secretResource,
getTokenSecretLabelSelector(MigResourceKind.MigCluster, name));

const secretResourceName = (secretResourceList.data.items && secretResourceList.data.items.length > 0) ?
secretResourceList.data.items[0].metadata.name : '';

yield Promise.all([
client.delete(secretResource, name),
client.delete(secretResource, secretResourceName),
client.delete(migClusterResource, name),
]);

Expand All @@ -110,8 +117,11 @@ function* addClusterRequest(action) {
const tokenSecret = createTokenSecret(
clusterValues.name,
migMeta.configNamespace,
clusterValues.token
clusterValues.token,
MigResourceKind.MigCluster,
clusterValues.name
);

const migCluster = createMigCluster(
clusterValues.name,
migMeta.namespace,
Expand All @@ -132,13 +142,20 @@ function* addClusterRequest(action) {
// Ensure that none of objects that make up a cluster already exist
try {
const getResults = yield Q.allSettled([
client.get(secretResource, tokenSecret.metadata.name),
client.list(secretResource,
getTokenSecretLabelSelector(MigResourceKind.MigCluster, migCluster.metadata.name)),
client.get(migClusterResource, migCluster.metadata.name),
]);

const alreadyExists = getResults.reduce((exists, res) => {
return res.value && res.value.status === 200 ?
[...exists, { kind: res.value.data.kind, name: res.value.data.metadata.name }] :
return (res && res.status === 200) ?
[...exists,
{
kind: res.value.data.kind,
name: (res.value.data.items && res.value.data.items.length > 0) ?
res.value.data.items[0].metadata.name : res.value.data.metadata.name
}
] :
exists;
}, []);

Expand All @@ -162,18 +179,30 @@ function* addClusterRequest(action) {
// If any of the objects actually fail creation, we need to rollback the others
// so the clusters are created, or fail atomically
try {
const clusterAddResults = yield Q.allSettled([
client.create(secretResource, tokenSecret),
client.create(migClusterResource, migCluster),
]);
const clusterAddResults = [];
const tokenSecretAddResult = yield client.create(secretResource, tokenSecret);

if (tokenSecretAddResult.status === 201) {
clusterAddResults.push(tokenSecretAddResult);

Object.assign(migCluster.spec.serviceAccountSecretRef, {
name: tokenSecretAddResult.data.metadata.name,
namespace: tokenSecretAddResult.data.metadata.namespace,
});

const clusterAddResult = yield client.create(migClusterResource, migCluster);
if (clusterAddResult.status === 201) {
clusterAddResults.push(clusterAddResult);
}
}

// If any of the attempted object creation promises have failed, we need to
// rollback those that succeeded so we don't have a halfway created "Cluster"
// A rollback is only required if some objects have actually *succeeded*,
// as well as failed.
const isRollbackRequired =
clusterAddResults.find(res => res.state === 'rejected') &&
clusterAddResults.find(res => res.state === 'fulfilled');
const isRollbackRequired =
clusterAddResults.find(res => res.status === 201) &&
clusterAddResults.find(res => res.status !== 201)

if (isRollbackRequired) {
const kindToResourceMap = {
Expand All @@ -183,8 +212,8 @@ function* addClusterRequest(action) {

// The objects that need to be rolled back are those that were fulfilled
const rollbackObjs = clusterAddResults.reduce((rollbackAccum, res) => {
return res.state === 'fulfilled' ?
[...rollbackAccum, { kind: res.value.data.kind, name: res.value.data.metadata.name }] :
return res.status === 201 ?
[...rollbackAccum, { kind: res.data.kind, name: res.data.metadata.name }] :
rollbackAccum;
}, []);

Expand All @@ -209,7 +238,7 @@ function* addClusterRequest(action) {
} // End rollback handling

const cluster = clusterAddResults.reduce((accum, res) => {
const data = res.value.data;
const data = res.data;
accum[data.kind] = data;
return accum;
}, {});
Expand Down Expand Up @@ -304,7 +333,7 @@ function* updateClusterRequest(action) {

// Pushing a request fn to delay the call until its yielded in a batch at same time
updatePromises.push(() => client.patch(
secretResource, clusterValues.name, newTokenSecret));
secretResource, currentCluster.spec.serviceAccountSecretRef.name, newTokenSecret));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I am assuming that Cluster name is not changeable from UI

}

try {
Expand Down
23 changes: 14 additions & 9 deletions src/app/common/duck/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ function* poll(action) {
const migMeta = state.migMeta;
const oauthMetaUrl = `${migMeta.clusterApi}/.well-known/oauth-authorization-server`;

const alertModalObj = {
name: params.pollName,
errorMessage: 'error'
};
yield put(AlertActions.alertErrorModal(alertModalObj));
yield put(AuthActions.certErrorOccurred(oauthMetaUrl));
yield put(PlanActions.stopPlanPolling());
yield put(ClusterActions.stopClusterPolling());
yield put(StorageActions.stopStoragePolling());
// Handle cert refresh error & network connectivity error
if (response.e.response.data.message === 'Network Error') {
const alertModalObj = {
name: params.pollName,
errorMessage: 'error'
};
yield put(AlertActions.alertErrorModal(alertModalObj));
yield put(AuthActions.certErrorOccurred(oauthMetaUrl));
yield put(PlanActions.stopPlanPolling());
yield put(ClusterActions.stopClusterPolling());
yield put(StorageActions.stopStoragePolling());

}

}
yield delay(params.delay);
}
Expand Down
14 changes: 12 additions & 2 deletions src/client/resources/conversions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { pvStorageClassAssignmentKey } from '../../app/plan/components/Wizard/StorageClassTable';
import { pvCopyMethodAssignmentKey } from '../../app/plan/components/Wizard/StorageClassTable';

export function createTokenSecret(name: string, namespace: string, rawToken: string) {
export function createTokenSecret(name: string, namespace: string, rawToken: string, createdForResourceType: string, createdForResource: string) {
// btoa => to base64, atob => from base64
const encodedToken = btoa(rawToken);
return {
Expand All @@ -11,13 +11,23 @@ export function createTokenSecret(name: string, namespace: string, rawToken: str
},
kind: 'Secret',
metadata: {
name,
generateName: `${name}-`,
namespace,
labels: {
createdForResourceType,
createdForResource,
}
},
type: 'Opaque',
};
}

export function getTokenSecretLabelSelector(createdForResourceType: string, createdForResource: string) {
return {
labelSelector: `createdForResourceType=${createdForResourceType},createdForResource=${createdForResource}`
}
}

export function updateTokenSecret(rawToken: string) {
// btoa => to base64, atob => from base64
const encodedToken = btoa(rawToken);
Expand Down