@spinnaker/ecs 0.0.0-main-2
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.
- package/CHANGELOG.md +1186 -0
- package/LICENSE.txt +203 -0
- package/dist/common/common.module.d.ts +1 -0
- package/dist/common/footer.component.d.ts +1 -0
- package/dist/domain/IEcsLoadBalancer.d.ts +119 -0
- package/dist/ecs.help.d.ts +1 -0
- package/dist/ecs.module.d.ts +4 -0
- package/dist/ecs.settings.d.ts +9 -0
- package/dist/ecsCluster/IEcsCapacityProviderDetails.d.ts +10 -0
- package/dist/ecsCluster/IEcsCluster.d.ts +5 -0
- package/dist/ecsCluster/ecsCluster.read.service.d.ts +7 -0
- package/dist/iamRoles/IRole.d.ts +6 -0
- package/dist/iamRoles/iamRole.read.service.d.ts +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6010 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/details/instance.details.controller.d.ts +2 -0
- package/dist/loadBalancer/EcsLoadBalancerClusterContainer.d.ts +6 -0
- package/dist/loadBalancer/TargetGroup.d.ts +11 -0
- package/dist/loadBalancer/details/loadBalancerDetails.d.ts +27 -0
- package/dist/loadBalancer/details/targetGroupDetails.d.ts +30 -0
- package/dist/loadBalancer/listener.d.ts +10 -0
- package/dist/loadBalancer/loadBalancer.transformer.d.ts +8 -0
- package/dist/loadBalancer/targetGroup.states.d.ts +1 -0
- package/dist/metricAlarm/MetricAlarm.d.ts +6 -0
- package/dist/metricAlarm/metricAlarm.read.service.d.ts +5 -0
- package/dist/pipeline/stages/destroyAsg/ecsDestroyAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/disableAsg/ecsDisableAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/disableCluster/ecsDisableClusterStage.d.ts +2 -0
- package/dist/pipeline/stages/enableAsg/ecsEnableAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/findImageFromTags/ecsFindImageFromTagStage.d.ts +2 -0
- package/dist/pipeline/stages/resizeAsg/ecsResizeAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/scaleDownCluster/ecsScaleDownClusterStage.d.ts +2 -0
- package/dist/pipeline/stages/shrinkCluster/ecsShrinkClusterStage.d.ts +2 -0
- package/dist/placementStrategy/IPlacementStrategy.d.ts +4 -0
- package/dist/placementStrategy/placementStrategy.service.d.ts +11 -0
- package/dist/secrets/ISecret.d.ts +5 -0
- package/dist/secrets/secret.read.service.d.ts +5 -0
- package/dist/securityGroup/details/securityGroupDetail.controller.d.ts +2 -0
- package/dist/securityGroup/securityGroup.module.d.ts +1 -0
- package/dist/securityGroup/securityGroup.reader.d.ts +5 -0
- package/dist/securityGroup/securityGroup.transformer.d.ts +2 -0
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +2 -0
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +165 -0
- package/dist/serverGroup/configure/wizard/CloneServerGroup.ecs.controller.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/capacityProvider/CapacityProvider.d.ts +7 -0
- package/dist/serverGroup/configure/wizard/container/Container.d.ts +32 -0
- package/dist/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/location/ServerGroupBasicSettings.controller.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/logging/logging.component.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/networking/Networking.d.ts +28 -0
- package/dist/serverGroup/configure/wizard/serviceDiscovery/ServiceDiscovery.d.ts +34 -0
- package/dist/serverGroup/configure/wizard/taskDefinition/TaskDefinition.d.ts +39 -0
- package/dist/serverGroup/details/resize/resizeCapacity.component.d.ts +1 -0
- package/dist/serverGroup/details/resize/resizeServerGroup.controller.d.ts +2 -0
- package/dist/serverGroup/details/rollback/rollbackServerGroup.controller.d.ts +2 -0
- package/dist/serverGroup/details/serverGroupDetails.ecs.controller.d.ts +2 -0
- package/dist/serverGroup/details/serverGroupDetails.module.d.ts +1 -0
- package/dist/serverGroup/events/EventsLink.d.ts +4 -0
- package/dist/serverGroup/events/events.component.d.ts +3 -0
- package/dist/serverGroup/events/events.controller.d.ts +19 -0
- package/dist/serverGroup/events/serverGroupEventsReader.service.d.ts +10 -0
- package/dist/serverGroup/serverGroup.module.d.ts +1 -0
- package/dist/serverGroup/serverGroup.transformer.d.ts +11 -0
- package/dist/serviceDiscovery/IServiceDiscovery.d.ts +12 -0
- package/dist/serviceDiscovery/serviceDiscovery.read.service.d.ts +4 -0
- package/package.json +47 -0
- package/src/common/common.module.ts +6 -0
- package/src/common/footer.component.ts +29 -0
- package/src/domain/IEcsLoadBalancer.ts +129 -0
- package/src/ecs.help.ts +91 -0
- package/src/ecs.module.ts +116 -0
- package/src/ecs.settings.ts +17 -0
- package/src/ecsCluster/IEcsCapacityProviderDetails.ts +11 -0
- package/src/ecsCluster/IEcsCluster.ts +5 -0
- package/src/ecsCluster/ecsCluster.read.service.ts +23 -0
- package/src/iamRoles/IRole.ts +6 -0
- package/src/iamRoles/iamRole.read.service.ts +14 -0
- package/src/index.ts +1 -0
- package/src/instance/details/instance.details.controller.js +356 -0
- package/src/instance/details/instanceDetails.html +204 -0
- package/src/loadBalancer/EcsLoadBalancerClusterContainer.tsx +48 -0
- package/src/loadBalancer/TargetGroup.tsx +74 -0
- package/src/loadBalancer/details/loadBalancerDetails.tsx +235 -0
- package/src/loadBalancer/details/targetGroupDetails.tsx +251 -0
- package/src/loadBalancer/listener.tsx +65 -0
- package/src/loadBalancer/loadBalancer.transformer.ts +21 -0
- package/src/loadBalancer/targetGroup.states.ts +59 -0
- package/src/logo/ecs.icon.svg +27 -0
- package/src/logo/ecs.logo.less +5 -0
- package/src/logo/ecs.logo.svg +27 -0
- package/src/metricAlarm/MetricAlarm.ts +6 -0
- package/src/metricAlarm/metricAlarm.read.service.ts +14 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html +101 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html +1 -0
- package/src/pipeline/stages/cloneServerGroup/ecsCloneServerGroupStage.js +94 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +9 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +1 -0
- package/src/pipeline/stages/destroyAsg/ecsDestroyAsgStage.js +65 -0
- package/src/pipeline/stages/disableAsg/disableAsgStage.html +11 -0
- package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/disableAsg/ecsDisableAsgStage.js +70 -0
- package/src/pipeline/stages/disableCluster/disableClusterStage.html +26 -0
- package/src/pipeline/stages/disableCluster/ecsDisableClusterStage.js +84 -0
- package/src/pipeline/stages/enableAsg/ecsEnableAsgStage.js +73 -0
- package/src/pipeline/stages/enableAsg/enableAsgStage.html +11 -0
- package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/findImageFromTags/ecsFindImageFromTagStage.js +26 -0
- package/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.html +36 -0
- package/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html +9 -0
- package/src/pipeline/stages/resizeAsg/ecsResizeAsgStage.js +128 -0
- package/src/pipeline/stages/resizeAsg/resizeAsgStage.html +87 -0
- package/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html +1 -0
- package/src/pipeline/stages/scaleDownCluster/ecsScaleDownClusterStage.js +78 -0
- package/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html +35 -0
- package/src/pipeline/stages/shrinkCluster/ecsShrinkClusterStage.js +73 -0
- package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +34 -0
- package/src/placementStrategy/IPlacementStrategy.ts +4 -0
- package/src/placementStrategy/placementStrategy.service.ts +63 -0
- package/src/secrets/ISecret.ts +5 -0
- package/src/secrets/secret.read.service.ts +14 -0
- package/src/securityGroup/details/securityGroupDetail.controller.js +147 -0
- package/src/securityGroup/details/securityGroupDetail.html +104 -0
- package/src/securityGroup/securityGroup.module.ts +12 -0
- package/src/securityGroup/securityGroup.reader.ts +16 -0
- package/src/securityGroup/securityGroup.transformer.js +13 -0
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.js +281 -0
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +747 -0
- package/src/serverGroup/configure/wizard/CloneServerGroup.ecs.controller.js +259 -0
- package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.html +194 -0
- package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.js +17 -0
- package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html +10 -0
- package/src/serverGroup/configure/wizard/capacityProvider/CapacityProvider.tsx +381 -0
- package/src/serverGroup/configure/wizard/capacityProvider/capacityProvider.html +11 -0
- package/src/serverGroup/configure/wizard/container/Container.tsx +347 -0
- package/src/serverGroup/configure/wizard/container/container.html +9 -0
- package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.html +118 -0
- package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.js +20 -0
- package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.html +13 -0
- package/src/serverGroup/configure/wizard/location/ServerGroupBasicSettings.controller.js +41 -0
- package/src/serverGroup/configure/wizard/location/basicSettings.html +137 -0
- package/src/serverGroup/configure/wizard/logging/logging.component.html +29 -0
- package/src/serverGroup/configure/wizard/logging/logging.component.ts +12 -0
- package/src/serverGroup/configure/wizard/logging/logging.html +7 -0
- package/src/serverGroup/configure/wizard/networking/Networking.tsx +261 -0
- package/src/serverGroup/configure/wizard/networking/networking.html +9 -0
- package/src/serverGroup/configure/wizard/serverGroupWizard.html +55 -0
- package/src/serverGroup/configure/wizard/serviceDiscovery/ServiceDiscovery.tsx +250 -0
- package/src/serverGroup/configure/wizard/serviceDiscovery/serviceDiscovery.html +11 -0
- package/src/serverGroup/configure/wizard/taskDefinition/TaskDefinition.tsx +460 -0
- package/src/serverGroup/configure/wizard/taskDefinition/taskDefinition.html +51 -0
- package/src/serverGroup/configure/wizard/templateSelection/templateSelection.html +9 -0
- package/src/serverGroup/configure/wizard/verticalScaling/verticalScaling.html +10 -0
- package/src/serverGroup/details/resize/resizeCapacity.component.html +94 -0
- package/src/serverGroup/details/resize/resizeCapacity.component.ts +14 -0
- package/src/serverGroup/details/resize/resizeServerGroup.controller.js +85 -0
- package/src/serverGroup/details/resize/resizeServerGroup.html +28 -0
- package/src/serverGroup/details/rollback/rollbackServerGroup.controller.js +97 -0
- package/src/serverGroup/details/rollback/rollbackServerGroup.html +67 -0
- package/src/serverGroup/details/serverGroupDetails.ecs.controller.js +366 -0
- package/src/serverGroup/details/serverGroupDetails.html +216 -0
- package/src/serverGroup/details/serverGroupDetails.module.ts +5 -0
- package/src/serverGroup/events/EventsLink.tsx +5 -0
- package/src/serverGroup/events/events.component.ts +35 -0
- package/src/serverGroup/events/events.controller.ts +45 -0
- package/src/serverGroup/events/events.html +36 -0
- package/src/serverGroup/events/serverGroupEventsReader.service.ts +27 -0
- package/src/serverGroup/serverGroup.module.ts +6 -0
- package/src/serverGroup/serverGroup.transformer.spec.ts +184 -0
- package/src/serverGroup/serverGroup.transformer.ts +117 -0
- package/src/serviceDiscovery/IServiceDiscovery.ts +13 -0
- package/src/serviceDiscovery/serviceDiscovery.read.service.ts +8 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { UISref, UISrefActive } from '@uirouter/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { Application } from '@spinnaker/core';
|
|
5
|
+
import { AccountTag, CollapsibleSection, Spinner, timestamp } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import type { IEcsLoadBalancer } from '../../domain/IEcsLoadBalancer';
|
|
8
|
+
import { EcsListener } from '../listener';
|
|
9
|
+
|
|
10
|
+
interface IEcsLoadBalancerFromStateParams {
|
|
11
|
+
accountId: string;
|
|
12
|
+
region: string;
|
|
13
|
+
name: string;
|
|
14
|
+
vpcId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface IEcsLoadBalancerDetailsState {
|
|
18
|
+
loadBalancer: IEcsLoadBalancer;
|
|
19
|
+
loadBalancerNotFound?: string;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
refreshListenerUnsubscribe: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface IEcsLoadBalancerDetailsProps {
|
|
25
|
+
app: Application;
|
|
26
|
+
accountId: string;
|
|
27
|
+
loadBalancer: IEcsLoadBalancerFromStateParams;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class EcsLoadBalancerDetails extends React.Component<
|
|
31
|
+
IEcsLoadBalancerDetailsProps,
|
|
32
|
+
IEcsLoadBalancerDetailsState
|
|
33
|
+
> {
|
|
34
|
+
constructor(props: IEcsLoadBalancerDetailsProps) {
|
|
35
|
+
super(props);
|
|
36
|
+
this.state = {
|
|
37
|
+
loading: true,
|
|
38
|
+
loadBalancer: undefined,
|
|
39
|
+
refreshListenerUnsubscribe: () => {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
props.app
|
|
43
|
+
.getDataSource('loadBalancers')
|
|
44
|
+
.ready()
|
|
45
|
+
.then(() => this.extractLoadBalancer());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public componentWillUnmount(): void {
|
|
49
|
+
this.state.refreshListenerUnsubscribe();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private extractLoadBalancer(): void {
|
|
53
|
+
const { name, region } = this.props.loadBalancer;
|
|
54
|
+
const loadBalancer: IEcsLoadBalancer = this.props.app
|
|
55
|
+
.getDataSource('loadBalancers')
|
|
56
|
+
.data.find((test: IEcsLoadBalancer) => {
|
|
57
|
+
return test.name === name && test.account === this.props.loadBalancer.accountId && test.region === region;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
this.setState({
|
|
61
|
+
loading: false,
|
|
62
|
+
loadBalancer,
|
|
63
|
+
});
|
|
64
|
+
this.state.refreshListenerUnsubscribe();
|
|
65
|
+
|
|
66
|
+
if (loadBalancer) {
|
|
67
|
+
this.setState({
|
|
68
|
+
refreshListenerUnsubscribe: this.props.app
|
|
69
|
+
.getDataSource('loadBalancers')
|
|
70
|
+
.onRefresh(null, () => this.extractLoadBalancer()),
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
this.setState({
|
|
74
|
+
refreshListenerUnsubscribe: () => {},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public render(): React.ReactElement<EcsLoadBalancerDetails> {
|
|
80
|
+
const loadBalancerName = this.props.loadBalancer.name;
|
|
81
|
+
const { loadBalancer, loading } = this.state;
|
|
82
|
+
const { accountId } = this.props;
|
|
83
|
+
|
|
84
|
+
const CloseButton = (
|
|
85
|
+
<div className="close-button">
|
|
86
|
+
<UISref to="^">
|
|
87
|
+
<span className="glyphicon glyphicon-remove" />
|
|
88
|
+
</UISref>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const loadingHeader = () => (
|
|
93
|
+
<div className="header">
|
|
94
|
+
{CloseButton}
|
|
95
|
+
<div className="horizontal center middle">
|
|
96
|
+
<Spinner size="small" />
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const notFoundContent = () => (
|
|
102
|
+
<div className="content">
|
|
103
|
+
<div className="content-section">
|
|
104
|
+
<div className="content-body text-center">
|
|
105
|
+
<h3>Load balancer not found.</h3>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const loadBalancerHeader = () => (
|
|
112
|
+
<div className="header">
|
|
113
|
+
{CloseButton}
|
|
114
|
+
<div className="header-text horizontal middle">
|
|
115
|
+
<i className="fa icon-sitemap" />
|
|
116
|
+
<h3 className="horizontal middle space-between flex-1">{loadBalancerName}</h3>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const loadBalancerContent = () => (
|
|
122
|
+
<div className="content">
|
|
123
|
+
<CollapsibleSection heading="Load Balancer Details" defaultExpanded={true}>
|
|
124
|
+
<dl className="dl-horizontal dl-narrow">
|
|
125
|
+
<dt>Created</dt>
|
|
126
|
+
<dd>{timestamp(loadBalancer.createdTime)}</dd>
|
|
127
|
+
<dt>In</dt>
|
|
128
|
+
<dd>
|
|
129
|
+
<AccountTag account={accountId} />
|
|
130
|
+
<br />
|
|
131
|
+
{loadBalancer.region}
|
|
132
|
+
</dd>
|
|
133
|
+
<dt>VPC</dt>
|
|
134
|
+
<dd>{loadBalancer.vpcId}</dd>
|
|
135
|
+
<dt>Type</dt>
|
|
136
|
+
<dd>{loadBalancer.loadBalancerType}</dd>
|
|
137
|
+
<dt>IP Type</dt>
|
|
138
|
+
<dd>{loadBalancer.ipAddressType}</dd>
|
|
139
|
+
</dl>
|
|
140
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
141
|
+
<dt>Availability Zones</dt>
|
|
142
|
+
<dd>
|
|
143
|
+
<ul>
|
|
144
|
+
{loadBalancer.availabilityZones.map((az) => {
|
|
145
|
+
return <li key={az}>{az}</li>;
|
|
146
|
+
})}
|
|
147
|
+
</ul>
|
|
148
|
+
</dd>
|
|
149
|
+
</dl>
|
|
150
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
151
|
+
<dt>Target Groups</dt>
|
|
152
|
+
<dd>
|
|
153
|
+
<ul>
|
|
154
|
+
{loadBalancer.targetGroups.map((tg) => {
|
|
155
|
+
return (
|
|
156
|
+
<li key={tg.targetGroupName}>
|
|
157
|
+
<UISrefActive class="active">
|
|
158
|
+
<UISref
|
|
159
|
+
to="^.ecsTargetGroupDetails"
|
|
160
|
+
params={{
|
|
161
|
+
loadBalancerName: loadBalancer.name,
|
|
162
|
+
region: loadBalancer.region,
|
|
163
|
+
accountId: loadBalancer.account,
|
|
164
|
+
name: tg.targetGroupName,
|
|
165
|
+
vpcId: tg.vpcId,
|
|
166
|
+
provider: loadBalancer.cloudProvider,
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
<a>{tg.targetGroupName}</a>
|
|
170
|
+
</UISref>
|
|
171
|
+
</UISrefActive>
|
|
172
|
+
</li>
|
|
173
|
+
);
|
|
174
|
+
})}
|
|
175
|
+
</ul>
|
|
176
|
+
</dd>
|
|
177
|
+
</dl>
|
|
178
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
179
|
+
<dt>DNS Name</dt>
|
|
180
|
+
<dd>
|
|
181
|
+
<a target="_blank" href={'http://' + loadBalancer.dnsname}>
|
|
182
|
+
{loadBalancer.dnsname}
|
|
183
|
+
</a>
|
|
184
|
+
</dd>
|
|
185
|
+
</dl>
|
|
186
|
+
</CollapsibleSection>
|
|
187
|
+
<CollapsibleSection heading="Status" defaultExpanded={false}>
|
|
188
|
+
<span>Select a target group to check the instance health status from the view of its server groups.</span>
|
|
189
|
+
</CollapsibleSection>
|
|
190
|
+
<CollapsibleSection heading="Listeners" defaultExpanded={false}>
|
|
191
|
+
{loadBalancer.listeners ? (
|
|
192
|
+
loadBalancer.listeners.map((listener, index) => {
|
|
193
|
+
return (
|
|
194
|
+
<div key={index}>
|
|
195
|
+
<span>
|
|
196
|
+
<EcsListener listener={listener}></EcsListener>
|
|
197
|
+
</span>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
})
|
|
201
|
+
) : (
|
|
202
|
+
<li>No listeners provided.</li>
|
|
203
|
+
)}
|
|
204
|
+
</CollapsibleSection>
|
|
205
|
+
<CollapsibleSection heading="Firewalls" defaultExpanded={false}>
|
|
206
|
+
<ul>
|
|
207
|
+
{loadBalancer.securityGroups ? (
|
|
208
|
+
loadBalancer.securityGroups.map((sg) => {
|
|
209
|
+
return <li key={sg}>{sg}</li>;
|
|
210
|
+
})
|
|
211
|
+
) : (
|
|
212
|
+
<li>No security groups provided.</li>
|
|
213
|
+
)}
|
|
214
|
+
</ul>
|
|
215
|
+
</CollapsibleSection>
|
|
216
|
+
<CollapsibleSection heading="Subnets" defaultExpanded={false}>
|
|
217
|
+
<ul>
|
|
218
|
+
{loadBalancer.subnets.map((subnet) => {
|
|
219
|
+
return <li key={subnet}>{subnet}</li>;
|
|
220
|
+
})}
|
|
221
|
+
</ul>
|
|
222
|
+
</CollapsibleSection>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div className="details-panel">
|
|
228
|
+
{loading && loadingHeader()}
|
|
229
|
+
{!loading && loadBalancerHeader()}
|
|
230
|
+
{!loading && !loadBalancer && notFoundContent()}
|
|
231
|
+
{!loading && loadBalancer && loadBalancerContent()}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { UISref, UISrefActive } from '@uirouter/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { Application } from '@spinnaker/core';
|
|
5
|
+
import { AccountTag, CollapsibleSection, Spinner } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import type { IEcsLoadBalancer, IEcsTargetGroup } from '../../domain/IEcsLoadBalancer';
|
|
8
|
+
|
|
9
|
+
interface IEcsTargetGroupFromStateParams {
|
|
10
|
+
loadBalancerName: string;
|
|
11
|
+
targetGroupName: string;
|
|
12
|
+
region: string;
|
|
13
|
+
vpcId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface IEcsTargetGroupDetailsState {
|
|
17
|
+
loadBalancer: IEcsLoadBalancer;
|
|
18
|
+
targetGroup: IEcsTargetGroup;
|
|
19
|
+
targetGroupNotFound?: string;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
refreshListenerUnsubscribe: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface IEcsTargetGroupProps {
|
|
25
|
+
app: Application;
|
|
26
|
+
accountId: string;
|
|
27
|
+
name: string;
|
|
28
|
+
targetGroup: IEcsTargetGroupFromStateParams;
|
|
29
|
+
provider: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class EcsTargetGroupDetails extends React.Component<IEcsTargetGroupProps, IEcsTargetGroupDetailsState> {
|
|
33
|
+
constructor(props: IEcsTargetGroupProps) {
|
|
34
|
+
super(props);
|
|
35
|
+
this.state = {
|
|
36
|
+
loading: true,
|
|
37
|
+
targetGroup: undefined,
|
|
38
|
+
loadBalancer: undefined,
|
|
39
|
+
refreshListenerUnsubscribe: () => {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
props.app
|
|
43
|
+
.getDataSource('loadBalancers')
|
|
44
|
+
.ready()
|
|
45
|
+
.then(() => this.extractTargetGroup());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public componentWillUnmount(): void {
|
|
49
|
+
this.state.refreshListenerUnsubscribe();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private extractTargetGroup(): void {
|
|
53
|
+
const { loadBalancerName, region, targetGroupName } = this.props.targetGroup;
|
|
54
|
+
const loadBalancer: IEcsLoadBalancer = this.props.app
|
|
55
|
+
.getDataSource('loadBalancers')
|
|
56
|
+
.data.find((test: IEcsLoadBalancer) => {
|
|
57
|
+
return test.name === loadBalancerName && test.account === this.props.accountId && test.region === region;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
this.setState({
|
|
61
|
+
loadBalancer,
|
|
62
|
+
});
|
|
63
|
+
this.state.refreshListenerUnsubscribe();
|
|
64
|
+
|
|
65
|
+
if (!loadBalancer) {
|
|
66
|
+
this.setState({
|
|
67
|
+
loading: false,
|
|
68
|
+
targetGroup: null,
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
const targetGroup: IEcsTargetGroup = loadBalancer.targetGroups.find(
|
|
72
|
+
(tg) => tg.targetGroupName === targetGroupName,
|
|
73
|
+
);
|
|
74
|
+
if (!targetGroup) {
|
|
75
|
+
this.setState({
|
|
76
|
+
loading: false,
|
|
77
|
+
targetGroup: null,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (targetGroup) {
|
|
82
|
+
this.setState({
|
|
83
|
+
loading: false,
|
|
84
|
+
targetGroup,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (targetGroup) {
|
|
88
|
+
this.setState({
|
|
89
|
+
refreshListenerUnsubscribe: this.props.app
|
|
90
|
+
.getDataSource('loadBalancers')
|
|
91
|
+
.onRefresh(null, () => this.extractTargetGroup()),
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
this.setState({
|
|
95
|
+
refreshListenerUnsubscribe: () => {},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public render(): React.ReactElement<EcsTargetGroupDetails> {
|
|
103
|
+
const { name, accountId } = this.props;
|
|
104
|
+
const region = this.props.targetGroup.region;
|
|
105
|
+
const { targetGroup, loadBalancer, loading } = this.state;
|
|
106
|
+
|
|
107
|
+
const CloseButton = (
|
|
108
|
+
<div className="close-button">
|
|
109
|
+
<UISref to="^">
|
|
110
|
+
<span className="glyphicon glyphicon-remove" />
|
|
111
|
+
</UISref>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const loadingHeader = () => (
|
|
116
|
+
<div className="header">
|
|
117
|
+
{CloseButton}
|
|
118
|
+
<div className="horizontal center middle">
|
|
119
|
+
<Spinner size="small" />
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const notFoundContent = () => (
|
|
125
|
+
<div className="content">
|
|
126
|
+
<div className="content-section">
|
|
127
|
+
<div className="content-body text-center">
|
|
128
|
+
<h3>Target group not found.</h3>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const targetGroupHeader = () => (
|
|
135
|
+
<div className="header">
|
|
136
|
+
<div className="header-text horizontal middle">
|
|
137
|
+
<i className="fa fa-crosshairs icon" aria-hidden="true"></i>
|
|
138
|
+
<h3 className="horizontal middle space-between flex-1">{name}</h3>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const targetGroupContent = () => (
|
|
144
|
+
<div className="content">
|
|
145
|
+
<CollapsibleSection heading="Target Group Details" defaultExpanded={true}>
|
|
146
|
+
<dl className="dl-horizontal dl-narrow">
|
|
147
|
+
<dt>In</dt>
|
|
148
|
+
<dd>
|
|
149
|
+
<AccountTag account={accountId}></AccountTag>
|
|
150
|
+
<br />
|
|
151
|
+
{targetGroup.region}
|
|
152
|
+
</dd>
|
|
153
|
+
<dt>VPC</dt>
|
|
154
|
+
<dd>{targetGroup.vpcId}</dd>
|
|
155
|
+
<dt>Protocol</dt>
|
|
156
|
+
<dd>{targetGroup.protocol}</dd>
|
|
157
|
+
<dt>Port</dt>
|
|
158
|
+
<dd>{targetGroup.port}</dd>
|
|
159
|
+
<dt>Target Type</dt>
|
|
160
|
+
<dd>{targetGroup.targetType.toUpperCase()}</dd>
|
|
161
|
+
</dl>
|
|
162
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
163
|
+
<dt>Load Balancers</dt>
|
|
164
|
+
<dd>
|
|
165
|
+
{/* TODO: use UISref to display/highlight LBs in this app. See core/.../LoadBalancer.tsx */}
|
|
166
|
+
<ul className="collapse-margin-on-filter-collapse">
|
|
167
|
+
{targetGroup.loadBalancerNames ? (
|
|
168
|
+
targetGroup.loadBalancerNames.map((lb, i) => {
|
|
169
|
+
return <li key={i}>{lb}</li>;
|
|
170
|
+
})
|
|
171
|
+
) : (
|
|
172
|
+
<li>No load balncers provided.</li>
|
|
173
|
+
)}
|
|
174
|
+
</ul>
|
|
175
|
+
</dd>
|
|
176
|
+
</dl>
|
|
177
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
178
|
+
<dt>Load Balancer DNS Name</dt>
|
|
179
|
+
<dd>
|
|
180
|
+
<a target="_blank" href={'http://' + loadBalancer.dnsname}>
|
|
181
|
+
{loadBalancer.dnsname}
|
|
182
|
+
</a>
|
|
183
|
+
</dd>
|
|
184
|
+
</dl>
|
|
185
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
186
|
+
<dt>Server Groups</dt>
|
|
187
|
+
<dd>
|
|
188
|
+
<ul className="collapse-margin-on-filter-collapse">
|
|
189
|
+
{targetGroup.serverGroups.map((sg, i) => {
|
|
190
|
+
return (
|
|
191
|
+
<li key={i}>
|
|
192
|
+
<UISrefActive class="active">
|
|
193
|
+
<UISref
|
|
194
|
+
to="^.serverGroup"
|
|
195
|
+
params={{
|
|
196
|
+
region: region,
|
|
197
|
+
accountId: accountId,
|
|
198
|
+
serverGroup: sg,
|
|
199
|
+
provider: 'ecs',
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
<a>{sg}</a>
|
|
203
|
+
</UISref>
|
|
204
|
+
</UISrefActive>
|
|
205
|
+
</li>
|
|
206
|
+
);
|
|
207
|
+
})}
|
|
208
|
+
</ul>
|
|
209
|
+
</dd>
|
|
210
|
+
</dl>
|
|
211
|
+
</CollapsibleSection>
|
|
212
|
+
<CollapsibleSection heading="Health Checks" defaultExpanded={false}>
|
|
213
|
+
<dl className="horizontal-when-filters-collapsed">
|
|
214
|
+
<dt>Target</dt>
|
|
215
|
+
<dd>
|
|
216
|
+
{targetGroup.healthCheckProtocol}:{targetGroup.healthCheckPort}
|
|
217
|
+
{targetGroup.healthCheckPath}
|
|
218
|
+
</dd>
|
|
219
|
+
<dt>Timeout</dt>
|
|
220
|
+
<dd>{targetGroup.healthCheckTimeoutSeconds} seconds</dd>
|
|
221
|
+
<dt>Interval</dt>
|
|
222
|
+
<dd>{targetGroup.healthCheckIntervalSeconds} seconds</dd>
|
|
223
|
+
<dt>Healthy Threshold</dt>
|
|
224
|
+
<dd>{targetGroup.healthyThresholdCount}</dd>
|
|
225
|
+
<dt>Unhealthy Threshold</dt>
|
|
226
|
+
<dd>{targetGroup.unhealthyThresholdCount}</dd>
|
|
227
|
+
<dt>Matcher</dt>
|
|
228
|
+
<dd>HTTP Code(s): {targetGroup && targetGroup.matcher ? targetGroup.matcher.httpCode : 'None'}</dd>
|
|
229
|
+
</dl>
|
|
230
|
+
</CollapsibleSection>
|
|
231
|
+
<CollapsibleSection heading="Attributes" defaultExpanded={false}>
|
|
232
|
+
<dl>
|
|
233
|
+
<dt>Deregistration Delay Timeout</dt>
|
|
234
|
+
<dd>{targetGroup.attributes['deregistration_delay.timeout_seconds'] || 0} seconds</dd>
|
|
235
|
+
<dt>Stickiness Enabled</dt>
|
|
236
|
+
<dd>{targetGroup.attributes['stickiness.enabled'] || 'N/A'}</dd>
|
|
237
|
+
</dl>
|
|
238
|
+
</CollapsibleSection>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div className="details-panel">
|
|
244
|
+
{loading && loadingHeader()}
|
|
245
|
+
{!loading && targetGroupHeader()}
|
|
246
|
+
{!loading && !targetGroup && notFoundContent()}
|
|
247
|
+
{!loading && targetGroup && targetGroupContent()}
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IEcsListener } from '../domain/IEcsLoadBalancer';
|
|
4
|
+
|
|
5
|
+
export interface IEcsListenerProps {
|
|
6
|
+
listener: IEcsListener;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class EcsListener extends React.Component<IEcsListenerProps> {
|
|
10
|
+
constructor(props: IEcsListenerProps) {
|
|
11
|
+
super(props);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private getTargetGroupNameFromArn(arn: string): string {
|
|
15
|
+
const parts = arn.split('/');
|
|
16
|
+
if (parts.length < 0) {
|
|
17
|
+
return arn;
|
|
18
|
+
}
|
|
19
|
+
return parts[1];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public render(): React.ReactElement<EcsListener> {
|
|
23
|
+
const { port, protocol, defaultActions } = this.props.listener;
|
|
24
|
+
|
|
25
|
+
// TODO: retrieve and display 'rules' in addition to default actions
|
|
26
|
+
return (
|
|
27
|
+
<div>
|
|
28
|
+
<h4>
|
|
29
|
+
{protocol}:{port}
|
|
30
|
+
</h4>
|
|
31
|
+
<div style={{ paddingLeft: 10 }}>
|
|
32
|
+
<i>default actions</i>
|
|
33
|
+
<ul>
|
|
34
|
+
{defaultActions.map((action, i) => {
|
|
35
|
+
if (action.type == 'forward') {
|
|
36
|
+
return (
|
|
37
|
+
<li key={i}>
|
|
38
|
+
→
|
|
39
|
+
<i className="fa fa-fw fa-crosshairs icon" aria-hidden="true"></i>
|
|
40
|
+
<span>
|
|
41
|
+
{action.targetGroupArn ||
|
|
42
|
+
action.forwardConfig.targetGroups.map((tg) => {
|
|
43
|
+
return this.getTargetGroupNameFromArn(tg.targetGroupArn);
|
|
44
|
+
})}
|
|
45
|
+
</span>
|
|
46
|
+
</li>
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
return (
|
|
50
|
+
<li key={i}>
|
|
51
|
+
{action.type}:{' '}
|
|
52
|
+
{action.redirectConfig.statusCode ||
|
|
53
|
+
action.fixedResponseConfig.statusCode ||
|
|
54
|
+
action.authenticateOidcConfig.clientId ||
|
|
55
|
+
action.authenticateCognitoActionConfig.userPoolDomain}
|
|
56
|
+
</li>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
})}
|
|
60
|
+
</ul>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IQService } from 'angular';
|
|
2
|
+
|
|
3
|
+
import type { IEcsLoadBalancer, IEcsLoadBalancerSourceData, IEcsTargetGroup } from '../domain/IEcsLoadBalancer';
|
|
4
|
+
|
|
5
|
+
export class EcsLoadBalancerTransformer {
|
|
6
|
+
public static $inject = ['$q'];
|
|
7
|
+
constructor(private $q: IQService) {}
|
|
8
|
+
|
|
9
|
+
public normalizeLoadBalancer(loadBalancer: IEcsLoadBalancerSourceData): PromiseLike<IEcsLoadBalancer> {
|
|
10
|
+
loadBalancer.targetGroups.forEach((tg: IEcsTargetGroup) => {
|
|
11
|
+
tg.region = loadBalancer.region;
|
|
12
|
+
tg.account = loadBalancer.account;
|
|
13
|
+
tg.cloudProvider = loadBalancer.cloudProvider;
|
|
14
|
+
if (loadBalancer.targetGroupServices) {
|
|
15
|
+
const tgServiceMap = loadBalancer.targetGroupServices;
|
|
16
|
+
tg.serverGroups = tgServiceMap[tg.targetGroupArn];
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return this.$q.resolve(loadBalancer);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { StateParams } from '@uirouter/angularjs';
|
|
2
|
+
import { module } from 'angular';
|
|
3
|
+
|
|
4
|
+
import type { ApplicationStateProvider, INestedState } from '@spinnaker/core';
|
|
5
|
+
import { APPLICATION_STATE_PROVIDER } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { EcsTargetGroupDetails } from '../loadBalancer/details/targetGroupDetails';
|
|
8
|
+
//import { IEcsTargetGroup } from '../domain/IEcsLoadBalancer';
|
|
9
|
+
|
|
10
|
+
export const ECS_TARGET_GROUP_STATES = 'spinnaker.ecs.loadBalancer.targetGroup.states';
|
|
11
|
+
module(ECS_TARGET_GROUP_STATES, [APPLICATION_STATE_PROVIDER]).config([
|
|
12
|
+
'applicationStateProvider',
|
|
13
|
+
(applicationStateProvider: ApplicationStateProvider) => {
|
|
14
|
+
const ecsTargetGroupDetails: INestedState = {
|
|
15
|
+
name: 'ecsTargetGroupDetails',
|
|
16
|
+
url: '/ecsTargetGroupDetails/:provider/:accountId/:region/:vpcId/:loadBalancerName/:name',
|
|
17
|
+
params: {
|
|
18
|
+
vpcId: {
|
|
19
|
+
value: null,
|
|
20
|
+
squash: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
views: {
|
|
24
|
+
'detail@../insight': {
|
|
25
|
+
component: EcsTargetGroupDetails,
|
|
26
|
+
$type: 'react',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
resolve: {
|
|
30
|
+
accountId: ['$stateParams', ($stateParams: StateParams) => $stateParams.accountId],
|
|
31
|
+
name: ['$stateParams', ($stateParams: StateParams) => $stateParams.name],
|
|
32
|
+
provider: ['$stateParams', ($stateParams: StateParams) => $stateParams.provider],
|
|
33
|
+
// resolve flat params into an IEcsTargetGroup
|
|
34
|
+
targetGroup: [
|
|
35
|
+
'$stateParams',
|
|
36
|
+
($stateParams: StateParams) => {
|
|
37
|
+
return {
|
|
38
|
+
loadBalancerName: $stateParams.loadBalancerName,
|
|
39
|
+
targetGroupName: $stateParams.name,
|
|
40
|
+
accountId: $stateParams.accountId,
|
|
41
|
+
region: $stateParams.region,
|
|
42
|
+
vpcId: $stateParams.vpcId,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
data: {
|
|
48
|
+
pageTitleDetails: {
|
|
49
|
+
title: 'Target Group Details',
|
|
50
|
+
nameParam: 'name',
|
|
51
|
+
accountParam: 'accountId',
|
|
52
|
+
regionParam: 'region',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
applicationStateProvider.addInsightDetailState(ecsTargetGroupDetails);
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
|
2
|
+
<title>Compute</title>
|
|
3
|
+
<g>
|
|
4
|
+
<polygon points="19.64 22.51 16 24.32 16 75.68 19.64 77.49 33.98 51.07 19.64 22.51" fill="#9d5025"/>
|
|
5
|
+
<polygon points="30.72 31.78 36.08 24.19 60.1 34.87 54.5 35.76 30.72 31.78" fill="#9d5025"/>
|
|
6
|
+
<polygon points="26.56 68.77 32.46 76.88 60.1 64.95 54.76 64.12 26.56 68.77" fill="#9d5025"/>
|
|
7
|
+
<polygon points="28.12 75.5 19.64 77.49 19.64 22.51 28.12 24.44 28.12 75.5" fill="#f58536"/>
|
|
8
|
+
<polygon points="23.66 20.5 28.12 18.27 36.52 53.56 28.12 81.73 23.66 79.5 23.66 20.5" fill="#9d5025"/>
|
|
9
|
+
<polygon points="54.46 64.08 60.1 64.95 65.26 50.58 60.1 34.87 54.46 35.76 54.46 64.08" fill="#9d5025"/>
|
|
10
|
+
<polygon points="36.8 79.28 28.12 81.73 28.12 18.27 36.8 20.73 36.8 79.28" fill="#f58536"/>
|
|
11
|
+
<polygon points="66.01 63.84 28.12 70.88 28.12 81.73 66.01 71.03 66.01 63.84" fill="#f58536"/>
|
|
12
|
+
<polygon points="66.08 36 28.12 28.79 28.12 18.27 66.08 29.04 66.08 36" fill="#f58536"/>
|
|
13
|
+
<polygon points="60.1 27.34 66.19 29.02 66.19 71.03 60.1 72.7 60.1 27.34" fill="#f58536"/>
|
|
14
|
+
<polygon points="84 44.45 67.7 45.22 61.94 44.83 78.42 31.55 84 44.45" fill="#6b3a19"/>
|
|
15
|
+
<polygon points="61.94 44.83 78.42 43.9 78.42 31.55 61.94 34.22 61.94 44.83" fill="#9d5025"/>
|
|
16
|
+
<polygon points="46.43 43.79 63.13 26.96 71.62 43.22 54.31 44.32 46.43 43.79" fill="#6b3a19"/>
|
|
17
|
+
<polygon points="46.43 43.79 63.13 42.37 63.13 26.96 46.43 30.87 46.43 43.79" fill="#9d5025"/>
|
|
18
|
+
<polygon points="61.94 55.39 84 55.77 78.42 68.67 61.94 66 61.94 55.39" fill="#9d5025"/>
|
|
19
|
+
<polygon points="46.43 56.43 71.62 57.01 63.13 73.26 46.43 69.36 46.43 56.43" fill="#9d5025"/>
|
|
20
|
+
<polygon points="46.43 56.43 63.13 57.86 71.62 57.01 54.31 55.9 46.43 56.43" fill="#fbbf93"/>
|
|
21
|
+
<polygon points="84 55.77 67.7 55.01 61.94 55.39 78.42 56.33 84 55.77" fill="#fbbf93"/>
|
|
22
|
+
<polygon points="78.42 43.9 84 44.45 84 33.22 78.42 31.55 78.42 43.9" fill="#f58536"/>
|
|
23
|
+
<polygon points="71.62 43.22 63.13 42.37 63.13 26.96 71.62 29.51 71.62 43.22" fill="#f58536"/>
|
|
24
|
+
<polygon points="78.42 56.33 84 55.77 84 67 78.42 68.67 78.42 56.33" fill="#f58536"/>
|
|
25
|
+
<polygon points="71.62 57.01 63.13 57.86 63.13 73.26 71.62 70.72 71.62 57.01" fill="#f58536"/>
|
|
26
|
+
</g>
|
|
27
|
+
</svg>
|