@spinnaker/kubernetes 2025.1.4 → 2025.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/index.js +37010 -4266
  2. package/dist/index.js.map +1 -1
  3. package/dist/interfaces/infrastructure.types.d.ts +4 -0
  4. package/dist/loadBalancer/details/KubernetesLoadBalancerActions.d.ts +7 -0
  5. package/dist/loadBalancer/details/sections/IKubernetesLoadBalancerDetailsSectionProps.d.ts +5 -0
  6. package/dist/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.d.ts +3 -0
  7. package/dist/loadBalancer/details/sections/LoadBalancerEventsSection.d.ts +3 -0
  8. package/dist/loadBalancer/details/sections/LoadBalancerInformationSection.d.ts +3 -0
  9. package/dist/loadBalancer/details/sections/LoadBalancerLabelsSection.d.ts +3 -0
  10. package/dist/loadBalancer/details/sections/LoadBalancerStatusSection.d.ts +3 -0
  11. package/dist/loadBalancer/details/sections/index.d.ts +6 -0
  12. package/dist/loadBalancer/details/useKubernetesLoadBalancerDetails.d.ts +3 -0
  13. package/dist/loadBalancer/index.d.ts +4 -1
  14. package/dist/loadBalancer/transformer.d.ts +5 -1
  15. package/dist/manifest/delete/Delete.d.ts +10 -0
  16. package/dist/manifest/delete/DeleteModal.d.ts +13 -0
  17. package/dist/manifest/rollout/PauseRollout.d.ts +9 -0
  18. package/dist/manifest/rollout/ResumeRollout.d.ts +9 -0
  19. package/dist/manifest/rollout/RollingRestart.d.ts +1 -0
  20. package/dist/manifest/rollout/UndoRollout.d.ts +9 -0
  21. package/dist/manifest/rollout/UndoRolloutModal.d.ts +12 -0
  22. package/dist/manifest/rollout/undo.controller.d.ts +4 -0
  23. package/dist/manifest/scale/Scale.d.ts +10 -0
  24. package/dist/manifest/scale/ScaleModal.d.ts +13 -0
  25. package/package.json +2 -2
  26. package/src/interfaces/infrastructure.types.ts +11 -0
  27. package/src/kubernetes.module.ts +30 -7
  28. package/src/loadBalancer/details/KubernetesLoadBalancerActions.tsx +68 -0
  29. package/src/loadBalancer/details/sections/IKubernetesLoadBalancerDetailsSectionProps.ts +6 -0
  30. package/src/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.tsx +9 -0
  31. package/src/loadBalancer/details/sections/LoadBalancerEventsSection.tsx +15 -0
  32. package/src/loadBalancer/details/sections/LoadBalancerInformationSection.tsx +28 -0
  33. package/src/loadBalancer/details/sections/LoadBalancerLabelsSection.tsx +15 -0
  34. package/src/loadBalancer/details/sections/LoadBalancerStatusSection.tsx +138 -0
  35. package/src/loadBalancer/details/sections/index.ts +6 -0
  36. package/src/loadBalancer/details/useKubernetesLoadBalancerDetails.ts +52 -0
  37. package/src/loadBalancer/index.ts +4 -1
  38. package/src/loadBalancer/transformer.ts +2 -13
  39. package/src/manifest/delete/Delete.tsx +34 -0
  40. package/src/manifest/delete/DeleteModal.tsx +152 -0
  41. package/src/manifest/editor/json/JsonEditor.tsx +3 -4
  42. package/src/manifest/rollout/PauseRollout.tsx +46 -0
  43. package/src/manifest/rollout/ResumeRollout.tsx +46 -0
  44. package/src/manifest/rollout/RollingRestart.tsx +21 -13
  45. package/src/manifest/rollout/UndoRollout.tsx +33 -0
  46. package/src/manifest/rollout/UndoRolloutModal.tsx +123 -0
  47. package/src/manifest/rollout/undo.controller.ts +1 -1
  48. package/src/manifest/scale/Scale.tsx +34 -0
  49. package/src/manifest/scale/ScaleModal.tsx +115 -0
  50. package/src/manifest/selector/ManifestSelector.spec.tsx +2 -3
  51. package/src/manifest/selector/ManifestSelector.tsx +1 -2
  52. package/src/rawResource/rawResource.dataSource.ts +20 -24
  53. package/src/securityGroup/transformer.ts +1 -5
  54. package/dist/loadBalancer/details/details.controller.d.ts +0 -1
  55. package/src/loadBalancer/details/details.controller.ts +0 -101
  56. package/src/loadBalancer/details/details.html +0 -187
@@ -0,0 +1,115 @@
1
+ import type { FormikProps } from 'formik';
2
+ import { Form } from 'formik';
3
+ import React, { useState } from 'react';
4
+ import { Button, Modal } from 'react-bootstrap';
5
+
6
+ import type { Application, IModalComponentProps, IModalProps } from '@spinnaker/core';
7
+ import {
8
+ ManifestWriter,
9
+ ModalClose,
10
+ robotToHuman,
11
+ SpinFormik,
12
+ SubmitButton,
13
+ TaskMonitorWrapper,
14
+ TaskReason,
15
+ UserVerification,
16
+ useTaskMonitor,
17
+ } from '@spinnaker/core';
18
+
19
+ import { ScaleSettingsForm } from './ScaleSettingsForm';
20
+ import type { IAnyKubernetesResource } from '../../interfaces';
21
+ import type { IScaleCommand } from './scale.controller';
22
+
23
+ export interface IKubernetesScaleModalProps
24
+ extends Pick<IModalProps, 'isOpen'>,
25
+ Pick<IModalComponentProps, 'dismissModal'> {
26
+ application: Application;
27
+ resource: IAnyKubernetesResource;
28
+ currentReplicas: number;
29
+ }
30
+
31
+ export interface IKubernetesScaleValues {
32
+ reason?: string;
33
+ replicas: number;
34
+ }
35
+
36
+ export function ScaleModal({
37
+ application,
38
+ resource,
39
+ currentReplicas,
40
+ isOpen,
41
+ dismissModal,
42
+ }: IKubernetesScaleModalProps) {
43
+ const initialValues: IKubernetesScaleValues = {
44
+ replicas: currentReplicas,
45
+ };
46
+ const [verified, setVerified] = useState<boolean>(false);
47
+ const taskMonitor = useTaskMonitor(
48
+ {
49
+ application,
50
+ title: `Scaling ${resource.name} in ${resource.namespace}`,
51
+ onTaskComplete: () => application.serverGroups.refresh(true),
52
+ },
53
+ dismissModal,
54
+ );
55
+
56
+ const submit = (values: IKubernetesScaleValues): void => {
57
+ const payload = {
58
+ cloudProvider: 'kubernetes',
59
+ manifestName: resource.name,
60
+ location: resource.namespace,
61
+ account: resource.account,
62
+ reason: values.reason,
63
+ replicas: values.replicas,
64
+ };
65
+ return taskMonitor.submit(() => ManifestWriter.scaleManifest(payload, application));
66
+ };
67
+
68
+ const onOptionChange = (formik: FormikProps<IKubernetesScaleValues>, values: IScaleCommand): void => {
69
+ formik.setFieldValue('replicas', values.replicas);
70
+ };
71
+
72
+ return (
73
+ <Modal show={isOpen} onHide={dismissModal}>
74
+ <TaskMonitorWrapper monitor={taskMonitor} />
75
+ <SpinFormik<IKubernetesScaleValues>
76
+ initialValues={initialValues}
77
+ onSubmit={submit}
78
+ render={(formik) => (
79
+ <>
80
+ <ModalClose dismiss={dismissModal} />
81
+ <Modal.Header>
82
+ <Modal.Title>
83
+ Scale {robotToHuman(resource.name)} in {resource.namespace}
84
+ </Modal.Title>
85
+ </Modal.Header>
86
+ <Modal.Body>
87
+ <Form className="form-horizontal">
88
+ <ScaleSettingsForm
89
+ onChange={(options: IScaleCommand) => onOptionChange(formik, options)}
90
+ options={
91
+ ({
92
+ replicas: formik.values.replicas,
93
+ } as unknown) as IScaleCommand
94
+ }
95
+ />
96
+ <TaskReason reason={formik.values.reason} onChange={(val) => formik.setFieldValue('reason', val)} />
97
+ </Form>
98
+ </Modal.Body>
99
+ <Modal.Footer>
100
+ <UserVerification account={resource.account} onValidChange={setVerified} />
101
+ <Button onClick={dismissModal}>Cancel</Button>
102
+ <SubmitButton
103
+ onClick={() => submit(formik.values)}
104
+ isDisabled={!formik.isValid || formik.isSubmitting || !verified}
105
+ isFormSubmit={true}
106
+ submitting={formik.isSubmitting}
107
+ label={`Scale ${resource.name}`}
108
+ />
109
+ </Modal.Footer>
110
+ </>
111
+ )}
112
+ />
113
+ </Modal>
114
+ );
115
+ }
@@ -1,5 +1,4 @@
1
1
  import { mount } from 'enzyme';
2
- import { $q } from 'ngimport';
3
2
  import React from 'react';
4
3
  import type { Option } from 'react-select';
5
4
  import Select, { Creatable } from 'react-select';
@@ -25,8 +24,8 @@ describe('<ManifestSelector />', () => {
25
24
  let searchService: Spy;
26
25
 
27
26
  beforeEach(() => {
28
- searchService = spyOn(ManifestKindSearchService, 'search').and.returnValue($q.resolve([]));
29
- spyOn(AccountService, 'getAllAccountDetailsForProvider').and.returnValue($q.resolve([]));
27
+ searchService = spyOn(ManifestKindSearchService, 'search').and.returnValue(Promise.resolve([]));
28
+ spyOn(AccountService, 'getAllAccountDetailsForProvider').and.returnValue(Promise.resolve([]));
30
29
  });
31
30
 
32
31
  describe('initialization', () => {
@@ -1,5 +1,4 @@
1
1
  import { get, isEmpty } from 'lodash';
2
- import { $q } from 'ngimport';
3
2
  import React from 'react';
4
3
  import type { Option } from 'react-select';
5
4
  import Select, { Creatable } from 'react-select';
@@ -247,7 +246,7 @@ export class ManifestSelector extends React.Component<IManifestSelectorProps, IM
247
246
 
248
247
  private search = (kind: string, namespace: string, account: string): PromiseLike<string[]> => {
249
248
  if (this.isExpression(account)) {
250
- return $q.resolve([]);
249
+ return Promise.resolve([]);
251
250
  }
252
251
  return ManifestKindSearchService.search(kind, namespace, account).then((results) =>
253
252
  results.map((result) => result.name).sort(),
@@ -1,4 +1,3 @@
1
- import type { IQService } from 'angular';
2
1
  import { module } from 'angular';
3
2
 
4
3
  import type { Application } from '@spinnaker/core';
@@ -13,27 +12,24 @@ type ApiK8sResource = any;
13
12
  const fetchK8sResources = (application: Application): PromiseLike<ApiK8sResource> =>
14
13
  REST('applications').path(application.name, 'rawResources').get();
15
14
 
16
- const formatK8sResources = ($q: IQService) => (_: Application, result: ApiK8sResource): PromiseLike<ApiK8sResource> =>
17
- $q.resolve(result);
15
+ const formatK8sResources = (_: Application, result: ApiK8sResource): PromiseLike<ApiK8sResource> =>
16
+ Promise.resolve(result);
18
17
 
19
- module(KUBERNETS_RAW_RESOURCE_DATA_SOURCE, []).run([
20
- '$q',
21
- ($q: IQService) => {
22
- ApplicationDataSourceRegistry.registerDataSource({
23
- key: KUBERNETS_RAW_RESOURCE_DATA_SOURCE_KEY,
24
- label: 'Kubernetes',
25
- category: INFRASTRUCTURE_KEY,
26
- sref: KUBERNETS_RAW_RESOURCE_DATA_SOURCE_SREF,
27
- primary: true,
28
- icon: 'fas fa-xs fa-fw fa-th-large',
29
- iconName: 'spMenuK8s',
30
- loader: fetchK8sResources,
31
- onLoad: formatK8sResources($q),
32
- providerField: 'cloudProvider',
33
- credentialsField: 'account',
34
- regionField: 'region',
35
- description: 'Collections of kubernetes resources',
36
- defaultData: [],
37
- });
38
- },
39
- ]);
18
+ module(KUBERNETS_RAW_RESOURCE_DATA_SOURCE, []).run(() => {
19
+ ApplicationDataSourceRegistry.registerDataSource({
20
+ key: KUBERNETS_RAW_RESOURCE_DATA_SOURCE_KEY,
21
+ label: 'Kubernetes',
22
+ category: INFRASTRUCTURE_KEY,
23
+ sref: KUBERNETS_RAW_RESOURCE_DATA_SOURCE_SREF,
24
+ primary: true,
25
+ icon: 'fas fa-xs fa-fw fa-th-large',
26
+ iconName: 'spMenuK8s',
27
+ loader: fetchK8sResources,
28
+ onLoad: formatK8sResources,
29
+ providerField: 'cloudProvider',
30
+ credentialsField: 'account',
31
+ regionField: 'region',
32
+ description: 'Collections of kubernetes resources',
33
+ defaultData: [],
34
+ });
35
+ });
@@ -1,14 +1,10 @@
1
- import type { IQService } from 'angular';
2
1
  import { module } from 'angular';
3
2
 
4
3
  import type { ISecurityGroup } from '@spinnaker/core';
5
4
 
6
5
  class KubernetesV2SecurityGroupTransformer {
7
- public static $inject = ['$q'];
8
- constructor(private $q: IQService) {}
9
-
10
6
  public normalizeSecurityGroup(securityGroup: ISecurityGroup): PromiseLike<ISecurityGroup> {
11
- return this.$q.resolve(securityGroup);
7
+ return Promise.resolve(securityGroup);
12
8
  }
13
9
  }
14
10
 
@@ -1 +0,0 @@
1
- export declare const KUBERNETES_LOAD_BALANCER_DETAILS_CTRL = "spinnaker.kubernetes.loadBalancerDetails.controller";
@@ -1,101 +0,0 @@
1
- import type { StateService } from '@uirouter/angularjs';
2
- import type { IController, IScope } from 'angular';
3
- import { module } from 'angular';
4
- import type { IModalService } from 'angular-ui-bootstrap';
5
-
6
- import type { Application, ILoadBalancer, IManifest } from '@spinnaker/core';
7
- import { ManifestReader, SETTINGS } from '@spinnaker/core';
8
-
9
- import type { IKubernetesLoadBalancer } from '../../interfaces';
10
- import { KubernetesManifestCommandBuilder } from '../../manifest/manifestCommandBuilder.service';
11
- import { ManifestWizard } from '../../manifest/wizard/ManifestWizard';
12
-
13
- interface ILoadBalancerFromStateParams {
14
- accountId: string;
15
- region: string;
16
- name: string;
17
- }
18
-
19
- class KubernetesLoadBalancerDetailsController implements IController {
20
- public state = { loading: true };
21
- public manifest: IManifest;
22
- public loadBalancer: IKubernetesLoadBalancer;
23
-
24
- public static $inject = ['$uibModal', '$state', '$scope', 'loadBalancer', 'app'];
25
- constructor(
26
- private $uibModal: IModalService,
27
- private $state: StateService,
28
- private $scope: IScope,
29
- loadBalancer: ILoadBalancerFromStateParams,
30
- private app: Application,
31
- ) {
32
- const dataSource = this.app.getDataSource('loadBalancers');
33
- dataSource
34
- .ready()
35
- .then(() => {
36
- this.extractLoadBalancer(loadBalancer);
37
- this.$scope.isDisabled = !SETTINGS.kubernetesAdHocInfraWritesEnabled;
38
- dataSource.onRefresh(this.$scope, () => this.extractLoadBalancer(loadBalancer));
39
- })
40
- .catch(() => this.autoClose());
41
- }
42
-
43
- public deleteLoadBalancer(): void {
44
- this.$uibModal.open({
45
- templateUrl: require('../../manifest/delete/delete.html'),
46
- controller: 'kubernetesV2ManifestDeleteCtrl',
47
- controllerAs: 'ctrl',
48
- resolve: {
49
- coordinates: {
50
- name: this.loadBalancer.name,
51
- namespace: this.loadBalancer.namespace,
52
- account: this.loadBalancer.account,
53
- },
54
- application: this.app,
55
- manifestController: (): string => null,
56
- },
57
- });
58
- }
59
-
60
- public editLoadBalancer(): void {
61
- KubernetesManifestCommandBuilder.buildNewManifestCommand(
62
- this.app,
63
- this.manifest.manifest,
64
- this.loadBalancer.moniker,
65
- this.loadBalancer.account,
66
- ).then((builtCommand) => {
67
- ManifestWizard.show({ title: 'Edit Manifest', application: this.app, command: builtCommand });
68
- });
69
- }
70
-
71
- private extractLoadBalancer({ accountId, name, region }: ILoadBalancerFromStateParams): void {
72
- const loadBalancer = this.app.getDataSource('loadBalancers').data.find((test: ILoadBalancer) => {
73
- return test.name === name && test.account === accountId && test.region === region;
74
- });
75
-
76
- if (!loadBalancer) {
77
- return this.autoClose();
78
- }
79
-
80
- ManifestReader.getManifest(accountId, region, name).then((manifest: IManifest) => {
81
- this.manifest = manifest;
82
- this.loadBalancer = loadBalancer;
83
- this.state.loading = false;
84
- });
85
- }
86
-
87
- private autoClose(): void {
88
- if (this.$scope.$$destroyed) {
89
- return;
90
- } else {
91
- this.$state.params.allowModalToStayOpen = true;
92
- this.$state.go('^', null, { location: 'replace' });
93
- }
94
- }
95
- }
96
-
97
- export const KUBERNETES_LOAD_BALANCER_DETAILS_CTRL = 'spinnaker.kubernetes.loadBalancerDetails.controller';
98
- module(KUBERNETES_LOAD_BALANCER_DETAILS_CTRL, []).controller(
99
- 'kubernetesV2LoadBalancerDetailsCtrl',
100
- KubernetesLoadBalancerDetailsController,
101
- );
@@ -1,187 +0,0 @@
1
- <div class="details-panel">
2
- <div ng-if="ctrl.state.loading" class="header">
3
- <div class="close-button">
4
- <a class="btn btn-link" ui-sref="^">
5
- <span class="glyphicon glyphicon-remove"></span>
6
- </a>
7
- </div>
8
- <h4 class="text-center">
9
- <span us-spinner="{radius:20, width:6, length: 12}"></span>
10
- </h4>
11
- </div>
12
-
13
- <div class="header" ng-if="!ctrl.state.loading">
14
- <div class="close-button">
15
- <a class="btn btn-link" ui-sref="^">
16
- <span class="glyphicon glyphicon-remove"></span>
17
- </a>
18
- </div>
19
- <div class="header-text horizontal middle">
20
- <cloud-provider-logo
21
- provider="ctrl.loadBalancer.cloudProvider"
22
- height="'36px'"
23
- width="'36px'"
24
- ></cloud-provider-logo>
25
- <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>
26
- {{ctrl.loadBalancer.displayName}}
27
- <render-if-feature feature="entityTags">
28
- <entity-notifications
29
- ng-if="!state.loading"
30
- entity="ctrl.loadBalancer"
31
- application="ctrl.app"
32
- placement="bottom"
33
- h-offset-percent="90%"
34
- entity-type="loadBalancer"
35
- page-location="details"
36
- on-update="ctrl.app.loadBalancers.refresh()"
37
- ></entity-notifications>
38
- </render-if-feature>
39
- </h3>
40
- </div>
41
- <div class="actions">
42
- <div class="dropdown" uib-dropdown dropdown-append-to-body>
43
- <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle ng-hide="isDisabled">
44
- {{ctrl.loadBalancer.kind | robotToHuman}} Actions <span class="caret"></span>
45
- </button>
46
- <ul class="dropdown-menu" uib-dropdown-menu role="menu">
47
- <li>
48
- <a href ng-click="ctrl.deleteLoadBalancer()">Delete</a>
49
- </li>
50
- <li>
51
- <a href ng-click="ctrl.editLoadBalancer()">Edit</a>
52
- </li>
53
- <render-if-feature feature="entityTags">
54
- <add-entity-tag-links
55
- component="ctrl.loadBalancer"
56
- application="ctrl.app"
57
- entity-type="loadBalancer"
58
- on-update="ctrl.app.loadBalancers.refresh"
59
- ></add-entity-tag-links>
60
- </render-if-feature>
61
- </ul>
62
- </div>
63
- </div>
64
- </div>
65
-
66
- <div ng-if="!ctrl.state.loading" class="content">
67
- <collapsible-section heading="Information" expanded="true">
68
- <dl class="dl-horizontal dl-narrow">
69
- <dt>Created</dt>
70
- <dd>{{ctrl.loadBalancer.createdTime | timestamp}}</dd>
71
- <dt>Account</dt>
72
- <dd><account-tag account="ctrl.loadBalancer.account" pad="right"></account-tag></dd>
73
- <dt>Namespace</dt>
74
- <dd>{{ctrl.loadBalancer.namespace}}</dd>
75
- <dt>Kind</dt>
76
- <dd>{{ctrl.loadBalancer.kind}}</dd>
77
- <dt>Service Type</dt>
78
- <dd>{{ctrl.manifest.manifest.spec.type}}</dd>
79
- <dt>Sess. Affinity</dt>
80
- <dd>{{ctrl.manifest.manifest.spec.sessionAffinity}}</dd>
81
- </dl>
82
- </collapsible-section>
83
- <collapsible-section heading="Status" expanded="true">
84
- <dl class="dl-horizontal dl-narrow">
85
- <div ng-if="!ctrl.loadBalancer.serverGroups.length">
86
- No workloads associated with this {{ctrl.loadBalancer.kind | robotToHuman}}.
87
- </div>
88
- <dt ng-if="ctrl.loadBalancer.serverGroups.length">Workloads</dt>
89
- <dd ng-if="ctrl.loadBalancer.serverGroups.length">
90
- <ul>
91
- <li ng-repeat="serverGroup in ctrl.loadBalancer.serverGroups | orderBy: ['isDisabled', '-name']">
92
- <a
93
- ui-sref="^.serverGroup({region: serverGroup.region,
94
- accountId: serverGroup.account,
95
- serverGroup: serverGroup.name,
96
- provider: 'kubernetes'})"
97
- >
98
- {{serverGroup.name}}
99
- </a>
100
- </li>
101
- </ul>
102
- </dd>
103
- <div ng-if="ctrl.loadBalancer.serverGroups.length">
104
- <dt>Pod status</dt>
105
- <dd>
106
- <health-counts class="pull-left" container="ctrl.loadBalancer.instanceCounts"></health-counts>
107
- </dd>
108
- </div>
109
- <div ng-if="ctrl.manifest.manifest.spec.clusterIP">
110
- <dt>Cluster IP</dt>
111
- <dd>
112
- <a target="_blank" href="//{{ctrl.manifest.manifest.spec.clusterIP}}">
113
- {{ctrl.manifest.manifest.spec.clusterIP}}
114
- </a>
115
- <copy-to-clipboard
116
- class="copy-to-clipboard copy-to-clipboard-sm"
117
- text="ctrl.manifest.manifest.spec.clusterIP"
118
- tool-tip="'Copy Cluster IP to clipboard'"
119
- >
120
- </copy-to-clipboard>
121
- </dd>
122
- </div>
123
- <div ng-if="ctrl.manifest.manifest.spec.loadBalancerIP">
124
- <dt>Load Balancer IP</dt>
125
- <dd>
126
- <a target="_blank" href="//{{ctrl.manifest.manifest.spec.loadBalancerIP}}">
127
- {{ctrl.manifest.manifest.spec.loadBalancerIP}}
128
- </a>
129
- <copy-to-clipboard
130
- class="copy-to-clipboard copy-to-clipboard-sm"
131
- text="ctrl.manifest.manifest.spec.loadBalancerIP"
132
- tool-tip="'Copy Load Balancer IP to clipboard'"
133
- >
134
- </copy-to-clipboard>
135
- </dd>
136
- </div>
137
- <div ng-if="ctrl.manifest.manifest.spec.rules.length">
138
- <dt>Host Rules</dt>
139
- <dd ng-repeat="ingressRule in ctrl.manifest.manifest.spec.rules">
140
- <a ng-if="ingressRule.host" target="_blank" href="//{{ingressRule.host}}"> {{ingressRule.host}} </a>
141
- <copy-to-clipboard
142
- ng-if="ingressRule.host"
143
- class="copy-to-clipboard copy-to-clipboard-sm"
144
- text="ingressRule.host"
145
- tool-tip="'Copy ingress rule host to clipboard'"
146
- >
147
- </copy-to-clipboard>
148
- </dd>
149
- </div>
150
- <div ng-if="ctrl.manifest.manifest.status.loadBalancer.ingress.length">
151
- <dt>Ingress</dt>
152
- <dd ng-repeat="ingress in ctrl.manifest.manifest.status.loadBalancer.ingress">
153
- <a ng-if="ingress.hostname" target="_blank" href="//{{ingress.hostname}}"> {{ingress.hostname}} </a>
154
- <copy-to-clipboard
155
- ng-if="ingress.hostname"
156
- class="copy-to-clipboard copy-to-clipboard-sm"
157
- text="ingress.hostname"
158
- tool-tip="'Copy Ingress hostname to clipboard'"
159
- >
160
- </copy-to-clipboard>
161
- <a ng-if="ingress.ip" target="_blank" href="//{{ingress.ip}}"> {{ingress.ip}} </a>
162
- <copy-to-clipboard
163
- ng-if="ingress.ip"
164
- class="copy-to-clipboard copy-to-clipboard-sm"
165
- text="ingress.ip"
166
- tool-tip="'Copy Ingress IP to clipboard'"
167
- >
168
- </copy-to-clipboard>
169
- </dd>
170
- </div>
171
- </dl>
172
- </collapsible-section>
173
-
174
- <kubernetes-annotation-custom-sections
175
- manifest="ctrl.manifest.manifest"
176
- resource="ctrl.loadBalancer"
177
- ></kubernetes-annotation-custom-sections>
178
-
179
- <collapsible-section heading="Events" expanded="true">
180
- <kubernetes-manifest-events manifest="ctrl.manifest"></kubernetes-manifest-events>
181
- </collapsible-section>
182
-
183
- <collapsible-section heading="Labels" expanded="true">
184
- <kubernetes-manifest-labels manifest="ctrl.manifest.manifest"></kubernetes-manifest-labels>
185
- </collapsible-section>
186
- </div>
187
- </div>