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

Remote cluster - Istio Configs Editable #6240

Merged
merged 3 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions business/istio_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1405,11 +1405,6 @@ func getPermissionsApi(ctx context.Context, k8s kubernetes.ClientInterface, clus
log.Debug("View only mode configured, skipping RBAC checks")
return canCreate, canPatch, canDelete
}
// Disable writes for remote clusters
if cluster != conf.KubernetesConfig.ClusterName {
log.Debug("Writes disabled for remote clusters")
return canCreate, canPatch, canDelete
}

/*
Kiali only uses create,patch,delete as WRITE permissions
Expand Down
2 changes: 1 addition & 1 deletion business/istio_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func TestCheckMulticlusterPermissions(t *testing.T) {

istioConfigDetailsRemote, err := configService.GetIstioConfigDetails(context.TODO(), "east", "test", "gateways", "gw-1")
assert.Equal("gw-1", istioConfigDetailsRemote.Gateway.Name)
assert.False(istioConfigDetailsRemote.Permissions.Update)
assert.True(istioConfigDetailsRemote.Permissions.Update)
assert.False(istioConfigDetailsRemote.Permissions.Delete)
assert.Nil(err)
}
Expand Down
82 changes: 70 additions & 12 deletions frontend/src/components/IstioWizards/ServiceWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,19 @@ class ServiceWizard extends React.Component<ServiceWizardProps, ServiceWizardSta
const pa = this.state.previews!.pa;
// Gateway is only created when user has explicit selected this option
if (gw) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'gateways', JSON.stringify(gw)));
promises.push(
API.createIstioConfigDetail(this.props.namespace, 'gateways', JSON.stringify(gw), this.props.cluster)
);
}
if (k8sgateway) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'k8sgateways', JSON.stringify(k8sgateway)));
promises.push(
API.createIstioConfigDetail(
this.props.namespace,
'k8sgateways',
JSON.stringify(k8sgateway),
this.props.cluster
)
);
}

if (this.props.update) {
Expand All @@ -348,13 +357,20 @@ class ServiceWizard extends React.Component<ServiceWizardProps, ServiceWizardSta
this.props.namespace,
'destinationrules',
dr.metadata.name,
JSON.stringify(dr)
JSON.stringify(dr),
this.props.cluster
)
);
}
if (vs) {
promises.push(
API.updateIstioConfigDetail(this.props.namespace, 'virtualservices', vs.metadata.name, JSON.stringify(vs))
API.updateIstioConfigDetail(
this.props.namespace,
'virtualservices',
vs.metadata.name,
JSON.stringify(vs),
this.props.cluster
)
);
}
if (k8shttproute) {
Expand All @@ -363,7 +379,8 @@ class ServiceWizard extends React.Component<ServiceWizardProps, ServiceWizardSta
this.props.namespace,
'k8shttproutes',
k8shttproute.metadata.name,
JSON.stringify(k8shttproute)
JSON.stringify(k8shttproute),
this.props.cluster
)
);
}
Expand All @@ -374,19 +391,45 @@ class ServiceWizard extends React.Component<ServiceWizardProps, ServiceWizardSta
// Note that Gateways are not updated from the Wizard, only the VS hosts/gateways sections are updated
} else {
if (dr) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'destinationrules', JSON.stringify(dr)));
promises.push(
API.createIstioConfigDetail(
this.props.namespace,
'destinationrules',
JSON.stringify(dr),
this.props.cluster
)
);
}
if (vs) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'virtualservices', JSON.stringify(vs)));
promises.push(
API.createIstioConfigDetail(
this.props.namespace,
'virtualservices',
JSON.stringify(vs),
this.props.cluster
)
);
}
if (k8shttproute) {
promises.push(
API.createIstioConfigDetail(this.props.namespace, 'k8shttproutes', JSON.stringify(k8shttproute))
API.createIstioConfigDetail(
this.props.namespace,
'k8shttproutes',
JSON.stringify(k8shttproute),
this.props.cluster
)
);
}

if (pa) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'peerauthentications', JSON.stringify(pa)));
promises.push(
API.createIstioConfigDetail(
this.props.namespace,
'peerauthentications',
JSON.stringify(pa),
this.props.cluster
)
);
}
}
break;
Expand Down Expand Up @@ -427,14 +470,29 @@ class ServiceWizard extends React.Component<ServiceWizardProps, ServiceWizardSta
): void => {
if (pa) {
if (this.state.trafficPolicy.peerAuthnSelector.addPeerAuthnModified) {
promises.push(API.createIstioConfigDetail(this.props.namespace, 'peerauthentications', JSON.stringify(pa)));
promises.push(
API.createIstioConfigDetail(
this.props.namespace,
'peerauthentications',
JSON.stringify(pa),
this.props.cluster
)
);
} else {
promises.push(
API.updateIstioConfigDetail(this.props.namespace, 'peerauthentications', dr.metadata.name, JSON.stringify(pa))
API.updateIstioConfigDetail(
this.props.namespace,
'peerauthentications',
dr.metadata.name,
JSON.stringify(pa),
this.props.cluster
)
);
}
} else if (this.state.trafficPolicy.peerAuthnSelector.addPeerAuthnModified) {
promises.push(API.deleteIstioConfigDetail(this.props.namespace, 'peerauthentications', dr.metadata.name));
promises.push(
API.deleteIstioConfigDetail(this.props.namespace, 'peerauthentications', dr.metadata.name, this.props.cluster)
);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type ReduxProps = {

type Props = ReduxProps & {
namespace: string;
cluster?: string;
serviceName: string;
show: boolean;
readOnly: boolean;
Expand Down Expand Up @@ -165,7 +166,8 @@ class ServiceWizardDropdownComponent extends React.Component<Props, State> {
deleteServiceTrafficRouting(
this.props.virtualServices,
DestinationRuleC.fromDrArray(this.props.destinationRules),
this.props.k8sHTTPRoutes
this.props.k8sHTTPRoutes,
this.props.cluster
)
.then(_results => {
this.setState({
Expand Down Expand Up @@ -207,7 +209,7 @@ class ServiceWizardDropdownComponent extends React.Component<Props, State> {

onChangeAnnotations = (annotations: { [key: string]: string }) => {
const jsonInjectionPatch = buildAnnotationPatch(annotations);
API.updateService(this.props.namespace, this.props.serviceName, jsonInjectionPatch, 'json')
API.updateService(this.props.namespace, this.props.serviceName, jsonInjectionPatch, 'json', this.props.cluster)
.then(_ => {
AlertUtils.add('Service ' + this.props.serviceName + ' updated', 'default', MessageType.SUCCESS);
this.setState(
Expand Down Expand Up @@ -268,6 +270,7 @@ class ServiceWizardDropdownComponent extends React.Component<Props, State> {
type={this.state.wizardType}
update={this.state.updateWizard}
namespace={this.props.namespace}
cluster={this.props.cluster}
serviceName={this.props.serviceName}
workloads={validWorkloads}
subServices={this.props.subServices}
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/components/IstioWizards/WizardActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export type ServiceWizardProps = {
type: string;
update: boolean;
namespace: string;
cluster?: string;
serviceName: string;
servicePort?: number;
tlsStatus?: TLSStatus;
Expand Down Expand Up @@ -2053,10 +2054,12 @@ export const buildWorkloadInjectionPatch = (workloadType: string, enable: boolea
};

export const buildAnnotationPatch = (annotations: { [key: string]: string }): string => {
const patch = [{
"op": "replace",
"path": "/metadata/annotations",
"value": annotations
}];
const patch = [
{
op: 'replace',
path: '/metadata/annotations',
value: annotations
}
];
return JSON.stringify(patch);
};
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ class IstioConfigDetailsPageComponent extends React.Component<IstioConfigDetails
API.deleteIstioConfigDetail(
this.props.match.params.namespace,
this.props.match.params.objectType,
this.props.match.params.object
this.props.match.params.object,
this.state.cluster
)
.then(() => this.backToList())
.catch(error => {
Expand All @@ -273,7 +274,8 @@ class IstioConfigDetailsPageComponent extends React.Component<IstioConfigDetails
this.props.match.params.namespace,
this.props.match.params.objectType,
this.props.match.params.object,
jsonPatch
jsonPatch,
this.state.cluster
)
.then(() => {
const targetMessage =
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/pages/Overview/OverviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,14 +506,15 @@ export class OverviewPage extends React.Component<OverviewProps, State> {
}

fetchValidationResultForCluster(namespaces: NamespaceInfo[], cluster: string) {
return Promise.all([API.getConfigValidations(cluster), API.getAllIstioConfigs([], [], false, '', '')])
return Promise.all([API.getConfigValidations(cluster), API.getAllIstioConfigs([], [], false, '', '', cluster)])
.then(results => {
namespaces.forEach(nsInfo => {
if (nsInfo.cluster && nsInfo.cluster === cluster && results[0].data[nsInfo.cluster]) {
nsInfo.validations = results[0].data[nsInfo.cluster][nsInfo.name];
}
// TODO: cluster param here when remote cluster config creation supported
nsInfo.istioConfig = results[1].data[nsInfo.name];
if (nsInfo.cluster && nsInfo.cluster === cluster) {
nsInfo.istioConfig = results[1].data[nsInfo.name];
}
});
})
.catch(err => this.handleAxiosError('Could not fetch validations status', err));
Expand Down
22 changes: 14 additions & 8 deletions frontend/src/pages/Overview/OverviewTrafficPolicies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export default class OverviewTrafficPolicies extends React.Component<OverviewTra
onAddRemoveTrafficPolicies = (): void => {
const op = this.props.opTarget;
const ns = this.props.nsTarget;
const cluster = this.props.nsInfo?.cluster;
const duration = this.props.duration;
const apsP = this.state.authorizationPolicies;
const sdsP = this.state.sidecars;
Expand All @@ -173,13 +174,13 @@ export default class OverviewTrafficPolicies extends React.Component<OverviewTra
.registerAll(
'trafficPoliciesDelete',
apsP
.map(ap => API.deleteIstioConfigDetail(ns, 'authorizationpolicies', ap.metadata.name))
.concat(sdsP.map(sc => API.deleteIstioConfigDetail(ns, 'sidecars', sc.metadata.name)))
.map(ap => API.deleteIstioConfigDetail(ns, 'authorizationpolicies', ap.metadata.name, cluster))
.concat(sdsP.map(sc => API.deleteIstioConfigDetail(ns, 'sidecars', sc.metadata.name, cluster)))
)
.then(_ => {
//Error here
if (op !== 'delete') {
this.createTrafficPolicies(ns, duration, apsP, sdsP, op);
this.createTrafficPolicies(ns, duration, apsP, sdsP, op, cluster);
} else {
AlertUtils.add('Traffic policies ' + op + 'd for ' + ns + ' namespace.', 'default', MessageType.SUCCESS);
this.props.load();
Expand All @@ -191,7 +192,7 @@ export default class OverviewTrafficPolicies extends React.Component<OverviewTra
}
});
} else {
this.createTrafficPolicies(ns, duration, apsP, sdsP);
this.createTrafficPolicies(ns, duration, apsP, sdsP, op, cluster);
}
};

Expand All @@ -200,16 +201,17 @@ export default class OverviewTrafficPolicies extends React.Component<OverviewTra
duration: DurationInSeconds,
aps: AuthorizationPolicy[],
sds: Sidecar[],
op: string = 'create'
op: string = 'create',
cluster?: string
) => {
const graphDataSource = new GraphDataSource();
graphDataSource.on('fetchSuccess', () => {
this.promises
.registerAll(
'trafficPoliciesCreate',
aps
.map(ap => API.createIstioConfigDetail(ns, 'authorizationpolicies', JSON.stringify(ap)))
.concat(sds.map(sc => API.createIstioConfigDetail(ns, 'sidecars', JSON.stringify(sc))))
.map(ap => API.createIstioConfigDetail(ns, 'authorizationpolicies', JSON.stringify(ap), cluster))
.concat(sds.map(sc => API.createIstioConfigDetail(ns, 'sidecars', JSON.stringify(sc), cluster)))
)
.then(results => {
if (results.length > 0) {
Expand Down Expand Up @@ -250,7 +252,11 @@ export default class OverviewTrafficPolicies extends React.Component<OverviewTra
const aps = items.filter(i => i.type === 'authorizationPolicy')[0];
const sds = items.filter(i => i.type === 'sidecar')[0];
this.setState(
{ authorizationPolicies: aps.items as AuthorizationPolicy[], sidecars: sds.items as Sidecar[], loaded: false },
{
authorizationPolicies: aps ? (aps.items as AuthorizationPolicy[]) : [],
sidecars: sds ? (sds.items as Sidecar[]) : [],
loaded: false
},
() => this.fetchPermission(true, false)
);
};
Expand Down
1 change: 1 addition & 0 deletions frontend/src/pages/ServiceDetails/ServiceDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ class ServiceDetails extends React.Component<ServiceDetailsProps, ServiceDetails
const actionsToolbar = this.state.serviceDetails ? (
<ServiceWizardDropdown
namespace={this.props.match.params.namespace}
cluster={this.state.cluster ? this.state.cluster : ''}
serviceName={this.state.serviceDetails.service.name}
annotations={this.state.serviceDetails.service.annotations}
show={false}
Expand Down