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

[release-4.17] OCPBUGS-51277: Fix JSON annotation parsing issue and harden related code #14795

Open
wants to merge 1 commit into
base: release-4.17
Choose a base branch
from
Open
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
Fix JSON annotation parsing issue and harden related code
  • Loading branch information
TheRealJon authored and openshift-cherrypick-robot committed Feb 25, 2025
commit d7d439366c91d2dce7c213cc5dfe7ee9b6d54d10
32 changes: 22 additions & 10 deletions frontend/packages/console-shared/src/utils/annotations.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { ObjectMetadata } from '@console/internal/module/k8s';

export const parseJSONAnnotation = (
export const parseJSONAnnotation = <T = any>(
annotations: ObjectMetadata['annotations'],
annotationKey: string,
onError?: (err: Error) => void,
defaultReturn?: any,
): any => {
key: string,
options: ParseJSONAnnotationOptions,
): T => {
const { validate, onError } = options ?? {};
const annotation = annotations?.[key];
if (!annotation) {
return null;
}
try {
return annotations?.[annotationKey] ? JSON.parse(annotations?.[annotationKey]) : defaultReturn;
const parsed: T = JSON.parse(annotation);
const valid = validate?.(parsed) ?? true;
if (!valid) {
throw new Error(`Invalid value: "${annotation}"`);
}
return parsed;
} catch (e) {
onError?.(e);
// eslint-disable-next-line no-console
console.warn(`Could not parse annotation ${annotationKey} as JSON: `, e);
return defaultReturn;
onError?.(e.message);
return null;
}
};

type ParseJSONAnnotationOptions = {
validate?: (value: any) => boolean;
onError?: (error: any) => void;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as _ from 'lodash';
import { OperatorHubCSVAnnotationKey } from '@console/operator-lifecycle-manager/src/components/operator-hub';
import { normalizedInfrastructureFeatures } from '@console/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils';
import { OLMAnnotation } from '@console/operator-lifecycle-manager/src/components/operator-hub';
import { normalizeInfrastructureFeature } from '@console/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils';
import {
CommaSeparatedList,
ExtensionCatalogItemMetadata,
Expand Down Expand Up @@ -69,7 +69,7 @@ const aggregateLegacyInfrastructureFeatures = (acc, key, value) => {
if (!infrastructureFeatures) return acc;
return {
...acc,
infrastructureFeatures: infrastructureFeatures.map((i) => normalizedInfrastructureFeatures[i]),
infrastructureFeatures: infrastructureFeatures.map((i) => normalizeInfrastructureFeature[i]),
};
};

Expand Down Expand Up @@ -97,19 +97,19 @@ const aggregateAnnotations = (
);
case CSVMetadataKey.categories:
return aggregateCommaSeparatedList(acc, key, value);
case OperatorHubCSVAnnotationKey.disconnected:
case OperatorHubCSVAnnotationKey.fipsCompliant:
case OperatorHubCSVAnnotationKey.proxyAware:
case OperatorHubCSVAnnotationKey.cnf:
case OperatorHubCSVAnnotationKey.cni:
case OperatorHubCSVAnnotationKey.csi:
case OperatorHubCSVAnnotationKey.tlsProfiles:
case OperatorHubCSVAnnotationKey.tokenAuthAWS:
case OperatorHubCSVAnnotationKey.tokenAuthAzure:
case OperatorHubCSVAnnotationKey.tokenAuthGCP:
case OLMAnnotation.Disconnected:
case OLMAnnotation.FIPSCompliant:
case OLMAnnotation.ProxyAware:
case OLMAnnotation.CNF:
case OLMAnnotation.CNI:
case OLMAnnotation.CSI:
case OLMAnnotation.TLSProfiles:
case OLMAnnotation.TokenAuthAWS:
case OLMAnnotation.TokenAuthAzure:
case OLMAnnotation.TokenAuthGCP:
return value === 'true'
? aggregateArray(acc, NormalizedCSVMetadataKey.infrastructureFeatures, [
normalizedInfrastructureFeatures[key],
normalizeInfrastructureFeature[key],
])
: acc;
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InfraFeatures } from '@console/operator-lifecycle-manager/src/components/operator-hub';
import { InfrastructureFeature } from '@console/operator-lifecycle-manager/src/components/operator-hub';

export enum FileBasedCatalogSchema {
package = 'olm.package',
Expand Down Expand Up @@ -84,7 +84,7 @@ export type ExtensionCatalogItemMetadata = {
categories?: string[];
description?: string;
displayName?: string;
infrastructureFeatures?: InfraFeatures[];
infrastructureFeatures?: InfrastructureFeature[];
keywords?: string[];
longDescription?: string;
provider?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,7 @@ import {
CSVConditionReason,
SubscriptionKind,
} from '../types';
import {
getClusterServiceVersionPlugins,
isCatalogSourceTrusted,
upgradeRequiresApproval,
} from '../utils';
import { isCatalogSourceTrusted, upgradeRequiresApproval } from '../utils';
import { isCopiedCSV, isStandaloneCSV } from '../utils/clusterserviceversions';
import { useClusterServiceVersion } from '../utils/useClusterServiceVersion';
import { useClusterServiceVersionPath } from '../utils/useClusterServiceVersionPath';
Expand All @@ -115,6 +111,7 @@ import {
import { createUninstallOperatorModal } from './modals/uninstall-operator-modal';
import { ProvidedAPIsPage, ProvidedAPIPage, ProvidedAPIPageProps } from './operand';
import { operatorGroupFor, operatorNamespaceFor, targetNamespacesFor } from './operator-group';
import { getClusterServiceVersionPlugins } from './operator-hub/operator-hub-utils';
import { CreateInitializationResourceButton } from './operator-install-page';
import {
SourceMissingStatus,
Expand Down Expand Up @@ -785,7 +782,7 @@ export const ClusterServiceVersionsPage: React.FC<ClusterServiceVersionsPageProp
_.isNil(_.get(sub, 'status.installedCSV')),
),
].filter(
(obj, i, all) =>
(obj, _i, all) =>
isCSV(obj) ||
_.isUndefined(
all.find(({ metadata }) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
StepResource,
SubscriptionKind,
} from '../types';
import { getInternalObjects } from '../utils';
import { getInternalObjects } from './operator-hub/operator-hub-utils';

export const visibilityLabel = 'olm-visibility';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@ import { CONSOLE_OPERATOR_CONFIG_NAME } from '@console/shared/src/constants';
import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler';
import { useOperands } from '@console/shared/src/hooks/useOperands';
import { getPatchForRemovingPlugins, isPluginEnabled } from '@console/shared/src/utils';
import {
DEFAULT_GLOBAL_OPERATOR_INSTALLATION_NAMESPACE,
OPERATOR_UNINSTALL_MESSAGE_ANNOTATION,
} from '../../const';
import { DEFAULT_GLOBAL_OPERATOR_INSTALLATION_NAMESPACE } from '../../const';
import { ClusterServiceVersionModel, SubscriptionModel } from '../../models';
import { ClusterServiceVersionKind, SubscriptionKind } from '../../types';
import { getClusterServiceVersionPlugins } from '../../utils';
import { OperandLink } from '../operand/operand-link';
import { OLMAnnotation } from '../operator-hub';
import { getClusterServiceVersionPlugins } from '../operator-hub/operator-hub-utils';
import Timeout = NodeJS.Timeout;

const deleteOptions = {
Expand Down Expand Up @@ -292,7 +290,7 @@ export const UninstallOperatorModal: React.FC<UninstallOperatorModalProps> = ({
subscription.metadata.namespace === DEFAULT_GLOBAL_OPERATOR_INSTALLATION_NAMESPACE
? 'all-namespaces'
: subscription.metadata.namespace;
const uninstallMessage = csv?.metadata?.annotations?.[OPERATOR_UNINSTALL_MESSAGE_ANNOTATION];
const uninstallMessage = csv?.metadata?.annotations?.[OLMAnnotation.UninstallMessage];
const showOperandsContent = !operandsLoaded || operands.length > 0;

const instructions = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ export enum CapabilityLevel {
DeepInsights = 'Deep Insights',
}

export enum InfraFeatures {
disconnected = 'Disconnected',
proxyAware = 'Proxy-aware',
fipsMode = 'Designed for FIPS',
tlsProfiles = 'Configurable TLS ciphers',
cnf = 'Cloud-Native Network Function',
cni = 'Container Network Interface',
csi = 'Container Storage Interface',
sno = 'Single Node Clusters',
tokenAuth = 'Short-lived token authentication',
tokenAuthGCP = 'Auth Token GCP',
export enum InfrastructureFeature {
Disconnected = 'Disconnected',
ProxyAware = 'Proxy-aware',
FIPSMode = 'Designed for FIPS',
TLSProfiles = 'Configurable TLS ciphers',
CNF = 'Cloud-Native Network Function',
CNI = 'Container Network Interface',
CSI = 'Container Storage Interface',
SNO = 'Single Node Clusters',
TokenAuth = 'Short-lived token authentication',
TokenAuthGCP = 'Auth Token GCP',
}

export enum ValidSubscriptionValue {
Expand All @@ -47,7 +47,7 @@ export type OperatorHubItem = {
cloudCredentials: CloudCredentialKind;
createdAt?: string;
description: string;
infraFeatures: InfraFeatures[];
infraFeatures: InfrastructureFeature[];
infrastructure: InfrastructureKind;
installed: boolean;
installState?: InstalledState;
Expand All @@ -64,61 +64,67 @@ export type OperatorHubItem = {
[key: string]: any;
};

export enum OperatorHubCSVAnnotationKey {
certifiedLevel = 'certifiedLevel',
healthIndex = 'healthIndex',
repository = 'repository',
containerImage = 'containerImage',
createdAt = 'createdAt',
support = 'support',
description = 'description',
categories = 'categories',
capabilities = 'capabilities',
actionText = 'marketplace.openshift.io/action-text',
remoteWorkflow = 'marketplace.openshift.io/remote-workflow',
supportWorkflow = 'marketplace.openshift.io/support-workflow',
infrastructureFeatures = 'operators.openshift.io/infrastructure-features',
validSubscription = 'operators.openshift.io/valid-subscription',
tags = 'tags',
disconnected = 'features.operators.openshift.io/disconnected',
fipsCompliant = 'features.operators.openshift.io/fips-compliant',
proxyAware = 'features.operators.openshift.io/proxy-aware',
cnf = 'features.operators.openshift.io/cnf',
cni = 'features.operators.openshift.io/cni',
csi = 'features.operators.openshift.io/csi',
tlsProfiles = 'features.operators.openshift.io/tls-profiles',
tokenAuthAWS = 'features.operators.openshift.io/token-auth-aws',
tokenAuthAzure = 'features.operators.openshift.io/token-auth-azure',
tokenAuthGCP = 'features.operators.openshift.io/token-auth-gcp',
export enum OLMAnnotation {
CertifiedLevel = 'certifiedLevel',
HealthIndex = 'healthIndex',
Repository = 'repository',
ContainerImage = 'containerImage',
CreatedAt = 'createdAt',
Support = 'support',
Description = 'description',
Categories = 'categories',
Capabilitiecs = 'capabilities',
ActionText = 'marketplace.openshift.io/action-text',
RemoteWorkflow = 'marketplace.openshift.io/remote-workflow',
SupportWorkflow = 'marketplace.openshift.io/support-workflow',
InfrastructureFeatures = 'operators.openshift.io/infrastructure-features',
ValidSubscription = 'operators.openshift.io/valid-subscription',
Tags = 'tags',
Disconnected = 'features.operators.openshift.io/disconnected',
FIPSCompliant = 'features.operators.openshift.io/fips-compliant',
ProxyAware = 'features.operators.openshift.io/proxy-aware',
CNF = 'features.operators.openshift.io/cnf',
CNI = 'features.operators.openshift.io/cni',
CSI = 'features.operators.openshift.io/csi',
TLSProfiles = 'features.operators.openshift.io/tls-profiles',
TokenAuthAWS = 'features.operators.openshift.io/token-auth-aws',
TokenAuthAzure = 'features.operators.openshift.io/token-auth-azure',
TokenAuthGCP = 'features.operators.openshift.io/token-auth-gcp',
SuggestedNamespaceTemplate = 'operatorframework.io/suggested-namespace-template',
InitializationResource = 'operatorframework.io/initialization-resource',
InternalObjects = 'operators.operatorframework.io/internal-objects',
OperatorPlugins = 'console.openshift.io/plugins',
OperatorType = 'operators.operatorframework.io/operator-type',
UninstallMessage = 'operator.openshift.io/uninstall-message',
}

export type OperatorHubCSVAnnotations = {
[OperatorHubCSVAnnotationKey.certifiedLevel]?: string;
[OperatorHubCSVAnnotationKey.healthIndex]?: string;
[OperatorHubCSVAnnotationKey.repository]?: string;
[OperatorHubCSVAnnotationKey.containerImage]?: string;
[OperatorHubCSVAnnotationKey.createdAt]?: string;
[OperatorHubCSVAnnotationKey.support]?: string;
[OperatorHubCSVAnnotationKey.description]?: string;
[OperatorHubCSVAnnotationKey.categories]?: string;
[OperatorHubCSVAnnotationKey.capabilities]?: CapabilityLevel;
[OperatorHubCSVAnnotationKey.actionText]?: string;
[OperatorHubCSVAnnotationKey.remoteWorkflow]?: string;
[OperatorHubCSVAnnotationKey.supportWorkflow]?: string;
[OperatorHubCSVAnnotationKey.infrastructureFeatures]?: string;
[OperatorHubCSVAnnotationKey.validSubscription]?: string;
[OperatorHubCSVAnnotationKey.tags]?: string[];
[OperatorHubCSVAnnotationKey.disconnected]?: string;
[OperatorHubCSVAnnotationKey.fipsCompliant]?: string;
[OperatorHubCSVAnnotationKey.proxyAware]?: string;
[OperatorHubCSVAnnotationKey.tlsProfiles]?: string;
[OperatorHubCSVAnnotationKey.cnf]?: string;
[OperatorHubCSVAnnotationKey.cni]?: string;
[OperatorHubCSVAnnotationKey.csi]?: string;
[OperatorHubCSVAnnotationKey.tlsProfiles]?: string;
[OperatorHubCSVAnnotationKey.tokenAuthAWS]?: string;
[OperatorHubCSVAnnotationKey.tokenAuthAzure]?: string;
[OperatorHubCSVAnnotationKey.tokenAuthGCP]?: string;
export type CSVAnnotations = {
[OLMAnnotation.CertifiedLevel]?: string;
[OLMAnnotation.HealthIndex]?: string;
[OLMAnnotation.Repository]?: string;
[OLMAnnotation.ContainerImage]?: string;
[OLMAnnotation.CreatedAt]?: string;
[OLMAnnotation.Support]?: string;
[OLMAnnotation.Description]?: string;
[OLMAnnotation.Categories]?: string;
[OLMAnnotation.Capabilitiecs]?: CapabilityLevel;
[OLMAnnotation.ActionText]?: string;
[OLMAnnotation.RemoteWorkflow]?: string;
[OLMAnnotation.SupportWorkflow]?: string;
[OLMAnnotation.InfrastructureFeatures]?: string;
[OLMAnnotation.ValidSubscription]?: string;
[OLMAnnotation.Tags]?: string[];
[OLMAnnotation.Disconnected]?: string;
[OLMAnnotation.FIPSCompliant]?: string;
[OLMAnnotation.ProxyAware]?: string;
[OLMAnnotation.TLSProfiles]?: string;
[OLMAnnotation.CNF]?: string;
[OLMAnnotation.CNI]?: string;
[OLMAnnotation.CSI]?: string;
[OLMAnnotation.TLSProfiles]?: string;
[OLMAnnotation.TokenAuthAWS]?: string;
[OLMAnnotation.TokenAuthAzure]?: string;
[OLMAnnotation.TokenAuthGCP]?: string;
} & ObjectMetadata['annotations'];

type OperatorHubSpec = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { defaultChannelNameFor } from '../index';
import { OperatorChannelSelect, OperatorVersionSelect } from './operator-channel-version-select';
import { CloudServiceTokenWarningAlert } from './operator-hub-subscribe';
import { isAWSSTSCluster, isAzureWIFCluster, isGCPWIFCluster } from './operator-hub-utils';
import { InfraFeatures, OperatorHubItem } from './index';
import { InfrastructureFeature, OperatorHubItem } from './index';

// t('olm~Basic Install'),
// t('olm~Seamless Upgrades'),
Expand Down Expand Up @@ -377,7 +377,7 @@ export const OperatorHubItemDetails: React.FC<OperatorHubItemDetailsProps> = ({
<div className="co-catalog-page__overlay-description">
{isAWSSTSCluster(cloudCredentials, infrastructure, authentication) &&
showCSTokenWarn &&
infraFeatures?.find((i) => i === InfraFeatures.tokenAuth) && (
infraFeatures?.find((i) => i === InfrastructureFeature.TokenAuth) && (
<CloudServiceTokenWarningAlert
title={t('olm~Cluster in STS Mode')}
message={t(
Expand All @@ -388,7 +388,7 @@ export const OperatorHubItemDetails: React.FC<OperatorHubItemDetailsProps> = ({
)}
{isAzureWIFCluster(cloudCredentials, infrastructure, authentication) &&
showCSTokenWarn &&
infraFeatures?.find((i) => i === InfraFeatures.tokenAuth) && (
infraFeatures?.find((i) => i === InfrastructureFeature.TokenAuth) && (
<CloudServiceTokenWarningAlert
title={t('olm~Cluster in Azure Workload Identity / Federated Identity Mode')}
message={t(
Expand All @@ -399,7 +399,7 @@ export const OperatorHubItemDetails: React.FC<OperatorHubItemDetailsProps> = ({
)}
{isGCPWIFCluster(cloudCredentials, infrastructure, authentication) &&
showCSTokenWarn &&
infraFeatures?.find((i) => i === InfraFeatures.tokenAuthGCP) && (
infraFeatures?.find((i) => i === InfrastructureFeature.TokenAuthGCP) && (
<CloudServiceTokenWarningAlert
title={t('olm~Cluster in GCP Workload Identity / Federated Identity Mode')}
message={t(
Expand Down
Loading