Skip to content

Commit

Permalink
Service Binding From Add Flow
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewballantyne committed Jan 14, 2020
1 parent 3b6c21b commit 2647ae3
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { setActiveApplication } from '@console/internal/actions/ui';
import { RootState } from '@console/internal/redux';
import { getActiveApplication } from '@console/internal/reducers/ui';
import { QUERY_PROPERTIES } from '../const';

type StateProps = {
application: string;
};
type DispatchProps = {
onSetApp: (application: string) => void;
};
type OwnProps = {
children: (desiredApplication?: string) => React.ReactNode;
keepOnUnmount?: boolean;
searchParams: URLSearchParams;
};

type QueryFocusApplicationProps = StateProps & DispatchProps & OwnProps;

const QueryFocusApplication: React.FC<QueryFocusApplicationProps> = ({
children,
application,
keepOnUnmount,
searchParams,
onSetApp,
}) => {
const [originalApp] = React.useState(application);
const desiredApplication = searchParams.get(QUERY_PROPERTIES.APPLICATION);

React.useEffect(() => {
if (desiredApplication && desiredApplication !== originalApp) {
onSetApp(desiredApplication);
}

return () => {
if (!keepOnUnmount && application !== originalApp) {
onSetApp(originalApp);
}
};
}, [desiredApplication, keepOnUnmount, onSetApp, originalApp, application]);

return <>{children(desiredApplication)}</>;
};

export default connect<StateProps, DispatchProps, OwnProps>(
(state: RootState): StateProps => ({
application: getActiveApplication(state),
}),
{
onSetApp: setActiveApplication,
},
)(QueryFocusApplication);
30 changes: 26 additions & 4 deletions frontend/packages/dev-console/src/components/import/ImportForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import { RootState } from '@console/internal/redux';
import { connect } from 'react-redux';
import { ALL_APPLICATIONS_KEY } from '@console/internal/const';
import { NormalizedBuilderImages, normalizeBuilderImages } from '../../utils/imagestream-utils';
import { doContextualBinding } from '../../utils/application-utils';
import { ALLOW_SERVICE_BINDING } from '../../const';
import { GitImportFormData, FirehoseList, ImportData, Resources } from './import-types';
import { createOrUpdateResources } from './import-submit-utils';
import { validationSchema } from './import-validation-utils';

export interface ImportFormProps {
namespace: string;
importData: ImportData;
contextualSource?: string;
imageStreams?: FirehoseList;
projects?: {
loaded: boolean;
Expand All @@ -25,15 +28,18 @@ export interface ImportFormProps {
export interface StateProps {
perspective: string;
activeApplication: string;
serviceBindingAvailable: boolean;
}

const ImportForm: React.FC<ImportFormProps & StateProps> = ({
namespace,
imageStreams,
importData,
contextualSource,
perspective,
activeApplication,
projects,
serviceBindingAvailable,
}) => {
const initialValues: GitImportFormData = {
name: '',
Expand Down Expand Up @@ -153,8 +159,22 @@ const ImportForm: React.FC<ImportFormProps & StateProps> = ({
project: { name: projectName },
} = values;

createOrUpdateResources(values, imageStream, createNewProject, true)
.then(() => createOrUpdateResources(values, imageStream))
const resourceActions = createOrUpdateResources(
values,
imageStream,
createNewProject,
true,
).then(() => createOrUpdateResources(values, imageStream));

if (contextualSource) {
resourceActions
.then((resources) =>
doContextualBinding(resources, contextualSource, serviceBindingAvailable),
)
.catch(() => {});
}

resourceActions
.then(() => {
actions.setSubmitting(false);
handleRedirect(projectName);
Expand Down Expand Up @@ -187,12 +207,14 @@ const ImportForm: React.FC<ImportFormProps & StateProps> = ({
);
};

const mapStateToProps = (state: RootState): StateProps => {
type OwnProps = ImportFormProps & { forApplication?: string };
const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
const perspective = getActivePerspective(state);
const activeApplication = getActiveApplication(state);
const activeApplication = ownProps.forApplication || getActiveApplication(state);
return {
perspective,
activeApplication: activeApplication !== ALL_APPLICATIONS_KEY ? activeApplication : '',
serviceBindingAvailable: state.FLAGS.get(ALLOW_SERVICE_BINDING),
};
};

Expand Down
33 changes: 22 additions & 11 deletions frontend/packages/dev-console/src/components/import/ImportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { RouteComponentProps } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { PageHeading, Firehose, FirehoseResource } from '@console/internal/components/utils';
import { ImageStreamModel } from '@console/internal/models';
import { QUERY_PROPERTIES } from '../../const';
import NamespacedPage, { NamespacedPageVariants } from '../NamespacedPage';
import QueryFocusApplication from '../QueryFocusApplication';
import ImportForm from './ImportForm';
import { ImportTypes, ImportData } from './import-types';

Expand Down Expand Up @@ -88,17 +90,26 @@ const ImportPage: React.FunctionComponent<ImportPageProps> = ({ match, location
}

return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<Helmet>
<title>{importData.title}</title>
</Helmet>
<PageHeading title={importData.title} />
<div className="co-m-pane__body">
<Firehose resources={resources}>
<ImportForm namespace={namespace || preselectedNamespace} importData={importData} />
</Firehose>
</div>
</NamespacedPage>
<QueryFocusApplication searchParams={searchParams}>
{(application) => (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<Helmet>
<title>{importData.title}</title>
</Helmet>
<PageHeading title={importData.title} />
<div className="co-m-pane__body">
<Firehose resources={resources}>
<ImportForm
forApplication={application}
contextualSource={searchParams.get(QUERY_PROPERTIES.CONTEXT_SOURCE)}
namespace={namespace || preselectedNamespace}
importData={importData}
/>
</Firehose>
</div>
</NamespacedPage>
)}
</QueryFocusApplication>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
createServiceBinding,
removeServiceBinding,
edgesFromServiceBinding,
getOperatorBackedServiceKindMap,
} from '../../utils/application-utils';
import { TopologyFilters } from './filters/filter-utils';
import {
Expand Down Expand Up @@ -321,18 +322,8 @@ export const transformTopologyData = (
filters?: TopologyFilters,
): TopologyDataModel => {
const installedOperators = _.get(resources, 'clusterServiceVersions.data');
let operatorBackedServiceKindMap: OperatorBackedServiceKindMap;
const operatorBackedServiceKindMap = getOperatorBackedServiceKindMap(installedOperators);
const serviceBindingRequests = _.get(resources, 'serviceBindingRequests.data');
if (installedOperators) {
operatorBackedServiceKindMap = installedOperators.reduce((kindMap, csv) => {
_.get(csv, 'spec.customresourcedefinitions.owned', []).forEach((crd) => {
if (!(crd.kind in kindMap)) {
kindMap[crd.kind] = csv;
}
});
return kindMap;
}, {});
}

let topologyGraphAndNodeData: TopologyDataModel = {
graph: { nodes: [], edges: [], groups: [] },
Expand Down
8 changes: 8 additions & 0 deletions frontend/packages/dev-console/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export const ALLOW_SERVICE_BINDING = 'ALLOW_SERVICE_BINDING';
export const FLAG_OPENSHIFT_PIPELINE = 'OPENSHIFT_PIPELINE';
export const CLUSTER_PIPELINE_NS = 'openshift';

/** URL query params that adjust scope / purpose of the page */
export enum QUERY_PROPERTIES {
/** For defining a contextual application group (ie, add new workload into this application group) */
APPLICATION = 'application',
/** For defining a contextual source of the redirect (ie, connect a new workload from this contextual source) */
CONTEXT_SOURCE = 'contextSource',
}
68 changes: 67 additions & 1 deletion frontend/packages/dev-console/src/utils/application-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import {
K8sKind,
k8sGet,
k8sList,
k8sPatch,
k8sKill,
Expand All @@ -9,6 +10,7 @@ import {
k8sCreate,
LabelSelector,
referenceFor,
referenceForModel,
} from '@console/internal/module/k8s';
import {
ImageStreamModel,
Expand All @@ -21,6 +23,10 @@ import {
DaemonSetModel,
StatefulSetModel,
} from '@console/internal/models';
import {
ClusterServiceVersionKind,
ClusterServiceVersionModel,
} from '@console/operator-lifecycle-manager/src';
import {
ServiceModel as KnativeServiceModel,
RouteModel as KnativeRouteModel,
Expand All @@ -31,7 +37,10 @@ import {
EventSourceKafkaModel,
} from '@console/knative-plugin';
import { checkAccess } from '@console/internal/components/utils';
import { TopologyDataObject } from '../components/topology/topology-types';
import {
OperatorBackedServiceKindMap,
TopologyDataObject,
} from '../components/topology/topology-types';
import { detectGitType } from '../components/import/import-validation-utils';
import { GitTypes } from '../components/import/import-types';
import { ServiceBindingRequestModel } from '../models';
Expand Down Expand Up @@ -437,3 +446,60 @@ export const cleanUpWorkload = (
});
return Promise.all(reqs);
};

export const getOperatorBackedServiceKindMap = (
installedOperators: ClusterServiceVersionKind[],
): OperatorBackedServiceKindMap => {
let operatorBackedServiceKindMap: OperatorBackedServiceKindMap = {};
if (installedOperators) {
operatorBackedServiceKindMap = installedOperators.reduce((kindMap, csv) => {
_.get(csv, 'spec.customresourcedefinitions.owned', []).forEach((crd) => {
if (!(crd.kind in kindMap)) {
kindMap[crd.kind] = csv;
}
});
return kindMap;
}, {});
}

return operatorBackedServiceKindMap;
};

export const doContextualBinding = async (
resources: K8sResourceKind[],
contextualSource: string,
serviceBindingAvailable: boolean = false,
): Promise<K8sResourceKind[]> => {
const linkingModelRefs = [
referenceForModel(DeploymentConfigModel),
referenceForModel(DeploymentModel),
];
const newResource: K8sResourceKind = resources.find((resource) =>
linkingModelRefs.includes(referenceFor(resource)),
);
const {
metadata: { namespace },
} = newResource;
const [groupVersionKind, resourceName] = contextualSource.split('/');
const contextualResource: K8sResourceKind = await k8sGet(
modelFor(groupVersionKind),
resourceName,
namespace,
);

if (serviceBindingAvailable) {
const operatorBackedServiceKindMap = getOperatorBackedServiceKindMap(
await k8sList(ClusterServiceVersionModel, { ns: namespace }),
);
const ownerResourceKind = _.get(newResource, 'metadata.ownerReferences[0].kind');
const isOperatorBacked = ownerResourceKind in operatorBackedServiceKindMap;

if (isOperatorBacked) {
await createServiceBinding(contextualResource, newResource);
}
}

await createResourceConnection(contextualResource, newResource);

return resources;
};

0 comments on commit 2647ae3

Please sign in to comment.