parse auth metadata in a single pass

validate the resulting metadata object instead of the config directly.

Signed-off-by: Jamie Klassen <jklassen@vmware.com>
This commit is contained in:
Jamie Klassen
2023-09-05 18:03:01 -04:00
parent 7a914fcff1
commit 9327871142
2 changed files with 56 additions and 28 deletions
@@ -315,4 +315,19 @@ describe('ConfigClusterLocator', () => {
},
]);
});
it('errors when authProvider is oidc but oidcTokenProvider is missing', async () => {
const cluster = {
name: 'oidc-cluster',
url: 'https://aks.test',
authProvider: 'oidc',
};
expect(() =>
ConfigClusterLocator.fromConfig(
new ConfigReader({ clusters: [cluster] }),
),
).toThrow(
`Cluster 'oidc-cluster' missing required config value for 'oidcTokenProvider'`,
);
});
});
@@ -30,12 +30,9 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
}
static fromConfig(config: Config): ConfigClusterLocator {
// TODO: Add validation that authProvider is required and serviceAccountToken
// is required if authProvider is serviceAccount
return new ConfigClusterLocator(
config.getConfigArray('clusters').map(c => {
const authProvider = c.getString('authProvider');
const serviceAccountToken = c.getOptionalString('serviceAccountToken');
const clusterDetails: ClusterDetails = {
name: c.getString('name'),
url: c.getString('url'),
@@ -44,7 +41,7 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
caData: c.getOptionalString('caData'),
caFile: c.getOptionalString('caFile'),
authProvider: authProvider,
...(serviceAccountToken && { authMetadata: { serviceAccountToken } }),
...ConfigClusterLocator.parseAuthMetadata(c),
};
const customResources = c.getOptionalConfigArray('customResources');
@@ -75,35 +72,22 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
return clusterDetails;
}
case 'aws': {
const assumeRole = c.getOptionalString('assumeRole');
const externalId = c.getOptionalString('externalId');
return {
authMetadata: {
...(assumeRole && {
[ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE]: assumeRole,
}),
...(externalId && {
[ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID]: externalId,
}),
...clusterDetails.authMetadata,
},
...clusterDetails,
};
return clusterDetails;
}
case 'azure': {
return clusterDetails;
}
case 'oidc': {
const oidcTokenProvider = c.getString('oidcTokenProvider');
return {
authMetadata: {
[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]: oidcTokenProvider,
...clusterDetails.authMetadata,
},
...clusterDetails,
};
if (
!clusterDetails.authMetadata?.[
ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER
]
) {
throw new Error(
`Cluster '${clusterDetails.name}' missing required config value for 'oidcTokenProvider'`,
);
}
return clusterDetails;
}
case 'serviceAccount': {
return clusterDetails;
@@ -124,6 +108,35 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
);
}
private static parseAuthMetadata(
clusterConfig: Config,
): { authMetadata: Record<string, string> } | undefined {
const serviceAccountToken = clusterConfig.getOptionalString(
'serviceAccountToken',
);
const assumeRole = clusterConfig.getOptionalString('assumeRole');
const externalId = clusterConfig.getOptionalString('externalId');
const oidcTokenProvider =
clusterConfig.getOptionalString('oidcTokenProvider');
return serviceAccountToken || assumeRole || externalId
? {
authMetadata: {
...(serviceAccountToken && { serviceAccountToken }),
...(assumeRole && {
[ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE]: assumeRole,
}),
...(externalId && {
[ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID]: externalId,
}),
...(oidcTokenProvider && {
[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]: oidcTokenProvider,
}),
},
}
: undefined;
}
async getClusters(): Promise<ClusterDetails[]> {
return this.clusterDetails;
}