Skip to content

Commit 3aa1ee3

Browse files
gadresushrutanotherchrisberry
authored andcommitted
Openstack Security group support for UDP and ICMP (spinnaker#2580)
1 parent 1060b49 commit 3aa1ee3

File tree

8 files changed

+278
-32
lines changed

8 files changed

+278
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
let angular = require('angular');
4+
5+
module.exports = angular.module('spinnaker.openstack.validateType.directive', [
6+
])
7+
.directive('validateType', [
8+
function() {
9+
10+
var link = function($scope, $element, $attrs, ctrl) {
11+
12+
var validate = function(viewValue) {
13+
var comparisonModel = $attrs.validateType;
14+
15+
if(parseInt(viewValue, 10) === -1 && parseInt(comparisonModel, 10) > -1) {
16+
ctrl.$setValidity('validateType', false);
17+
}
18+
else {
19+
ctrl.$setValidity('validateType', true);
20+
}
21+
return viewValue;
22+
};
23+
24+
ctrl.$parsers.unshift(validate);
25+
ctrl.$formatters.push(validate);
26+
27+
$attrs.$observe('validateType', function() {
28+
return validate(ctrl.$viewValue);
29+
});
30+
};
31+
32+
return {
33+
require: 'ngModel',
34+
link: link
35+
};
36+
37+
}
38+
]);

app/scripts/modules/openstack/securityGroup/configure/wizard/basicSettings.html

+14
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ <h3 us-spinner="{radius:30, width:8, length: 16}"></h3>
4242
<input type="text" class="form-control input-sm" ng-disabled="true" ng-model="securityGroup.stack" />
4343
</div>
4444
</div>
45+
<div class="form-group">
46+
<div class="col-md-3 sm-label-right">Detail <help-field key="openstack.securitygroup.detail"></help-field></div>
47+
<div class="col-md-7" ng-if="isNew">
48+
<input type="text"
49+
class="form-control input-sm"
50+
ng-model="securityGroup.detail"
51+
name="detailName"
52+
ng-change="ctrl.updateName()"
53+
ng-pattern="/^[a-zA-Z0-9]*$/"/>
54+
</div>
55+
<div class="col-md-7" ng-if="!isNew">
56+
<input type="text" class="form-control input-sm" ng-disabled="true" ng-model="securityGroup.detail" />
57+
</div>
58+
</div>
4559
<div class="form-group">
4660
<div class="col-md-3 sm-label-right">Description <help-field key="openstack.securitygroup.description"></help-field></div>
4761
<div class="col-md-7">

app/scripts/modules/openstack/securityGroup/configure/wizard/rules.controller.js

+86-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,98 @@ let angular = require('angular');
44

55
module.exports = angular.module('spinnaker.securityGroup.configure.openstack.ports', [
66
require('../../transformer.js'),
7+
require('../../../common/validateType.directive.js'),
78
])
8-
.controller('openstackSecurityGroupRulesController', function($scope, openstackSecurityGroupTransformer) {
9+
.controller('openstackSecurityGroupRulesController', function($scope, openstackSecurityGroupTransformer, infrastructureCaches, securityGroupReader, cacheInitializer, rx) {
10+
this.infiniteScroll = {
11+
currentItems: 20,
12+
};
13+
14+
$scope.$watch('securityGroup.region', function() {
15+
var account = $scope.securityGroup.credentials || $scope.securityGroup.account;
16+
$scope.loadSecurityGroups(account, $scope.securityGroup.region);
17+
});
18+
19+
$scope.$watch('securityGroup.account', function() {
20+
var account = $scope.securityGroup.credentials || $scope.securityGroup.account;
21+
$scope.loadSecurityGroups(account, $scope.securityGroup.region);
22+
});
23+
24+
this.addMoreItems = () => this.infiniteScroll.currentItems += 20;
25+
926
this.addRule = function() {
1027
$scope.securityGroup.rules.push(openstackSecurityGroupTransformer.constructNewIngressRule());
1128
};
1229

30+
this.getSecurityGroupRefreshTime = function() {
31+
return infrastructureCaches.securityGroups.getStats().ageMax;
32+
};
33+
34+
$scope.allSecurityGroupsUpdated = new rx.Subject();
35+
36+
$scope.initializeSecurityGroups = function() {
37+
return securityGroupReader.getAllSecurityGroups().then(function (securityGroups) {
38+
$scope.state.securityGroupsLoaded = true;
39+
var account = $scope.securityGroup.credentials || $scope.securityGroup.account;
40+
var region = $scope.securityGroup.region;
41+
42+
if(account && region) {
43+
$scope.availableSecurityGroups = _.filter(securityGroups[account].openstack[region]);
44+
} else {
45+
$scope.availableSecurityGroups = securityGroups;
46+
}
47+
48+
//Add CIDR at the start of avaibleSecurityGroup Collection.
49+
var cidrObj = {
50+
id: 'CIDR',
51+
name: 'CIDR'
52+
};
53+
54+
if ($scope.availableSecurityGroups.unshift && $scope.availableSecurityGroups.some(g => g.id !== 'CIDR')) {
55+
$scope.availableSecurityGroups.unshift(cidrObj);
56+
}
57+
58+
$scope.allSecurityGroupsUpdated.onNext();
59+
});
60+
};
61+
62+
$scope.remoteSecurityGroupSelected = function(indx, remoteSecurityGroupId) {
63+
var rule = $scope.securityGroup.rules[indx];
64+
if(remoteSecurityGroupId === 'CIDR') {
65+
if(rule.prevcidr === '') {
66+
rule.cidr = '0.0.0.0/0';
67+
rule.prevcidr = '0.0.0.0/0';
68+
}
69+
else {
70+
rule.cidr = rule.prevcidr;
71+
}
72+
}
73+
else {
74+
rule.prevcidr = rule.cidr;
75+
rule.cidr = '';
76+
}
77+
};
78+
79+
this.refreshSecurityGroups = function() {
80+
$scope.state.refreshingSecurityGroups = true;
81+
return cacheInitializer.refreshCache('securityGroups').then(function() {
82+
return $scope.initializeSecurityGroups().then(function() {
83+
$scope.state.refreshingSecurityGroups = false;
84+
}, function() {
85+
$scope.state.refreshingSecurityGroups = false;
86+
});
87+
}, function() {
88+
$scope.state.refreshingSecurityGroups = false;
89+
});
90+
};
91+
1392
this.removeRule = function(i) {
1493
$scope.securityGroup.rules.splice(i, 1);
1594
};
95+
96+
$scope.loadSecurityGroups = function(account, region) {
97+
if(account !== undefined && region !== undefined) {
98+
$scope.initializeSecurityGroups();
99+
}
100+
};
16101
});

app/scripts/modules/openstack/securityGroup/configure/wizard/rules.html

+74-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
<thead>
66
<tr>
77
<th style="width: 15%">Protocol</th>
8-
<th style="width: 15%">From Port</th>
9-
<th style="width: 15%">To Port</th>
8+
<th style="width: 12%" uib-tooltip="From Port is used for TCP and UDP, Type is used for ICMP.">From Port/Type</th>
9+
<th style="width: 12%" uib-tooltip="To Port is used for TCP and UDP, Code is used for ICMP.">To Port/Code</th>
10+
<th style="width: 20%" uib-tooltip="Remote value can either be a security group or a CIDR.">Remote</th>
1011
<th style="width: 15%">CIDR</th>
1112
<th></th>
1213
</tr>
@@ -15,14 +16,51 @@
1516
<tbody>
1617
<tr ng-repeat="rule in securityGroup.rules">
1718
<td><select class="form-control input-sm" ng-model="rule.ruleType"
18-
ng-options="protocol as protocol.toUpperCase() for protocol in ['tcp']" required></select></td>
19-
<td><input class="form-control input-sm" name="fromPort_{{$index}}" type="number" min="1" max="{{rule.toPort}}" ng-model="rule.fromPort" required/></td>
20-
<td><input class="form-control input-sm" name="toPort_{{$index}}" type="number" min="{{rule.fromPort}}" max="65535" ng-model="rule.toPort" required/></td>
21-
<td><input class="form-control input-sm" type="text" ng-model="rule.cidr" required/></td>
19+
ng-options="protocol as protocol.toUpperCase() for protocol in ['TCP', 'UDP','ICMP']" required></select></td>
20+
21+
<!--If ruleType is equal to TCP or UDP-->
22+
<td ng-if="rule.ruleType.toUpperCase() === 'TCP' || rule.ruleType.toUpperCase() === 'UDP' ">
23+
<input class="form-control input-sm" name="fromPort_{{$index}}" type="number" min="1" max="{{rule.toPort}}" ng-model="rule.fromPort" required/>
24+
</td>
25+
<td ng-if="rule.ruleType.toUpperCase() === 'TCP' || rule.ruleType.toUpperCase() === 'UDP' ">
26+
<input class="form-control input-sm" name="toPort_{{$index}}" type="number" min="{{rule.fromPort}}" max="65535" ng-model="rule.toPort" required/>
27+
</td>
28+
29+
<!--If ruleType equal to ICMP-->
30+
<td ng-if="rule.ruleType.toUpperCase() === 'ICMP' ">
31+
<input class="form-control input-sm" name="icmpType_{{$index}}"
32+
type="number" min="-1" max="255" ng-model="rule.icmpType" required
33+
validate-type="{{rule.icmpCode}}" icmp-index="{{$index}}"/>
34+
</td>
35+
<td ng-if="rule.ruleType.toUpperCase() === 'ICMP' ">
36+
<input class="form-control input-sm" name="icmpCode_{{$index}}"
37+
type="number" min="-1" max="255" ng-model="rule.icmpCode" required/>
38+
</td>
39+
40+
<td>
41+
<ui-select ng-model="rule.remoteSecurityGroupId" class="form-control input-sm" required style="width: 100%" on-select="remoteSecurityGroupSelected($index, rule.remoteSecurityGroupId)">
42+
<ui-select-match>{{$select.selected.name}}</ui-select-match>
43+
<ui-select-choices repeat="securityGroup.id as securityGroup in availableSecurityGroups | filter: $select.search | limitTo: rulesController.infiniteScroll.currentItems"
44+
infinite-scroll="rulesController.addMoreItems()"
45+
infinite-scroll-distance="2">
46+
<span ng-bind-html="securityGroup.name | highlight: $select.search"></span>
47+
</ui-select-choices>
48+
</ui-select>
49+
</td>
50+
51+
<td ng-if="rule.remoteSecurityGroupId.id === undefined && rule.remoteSecurityGroupId === 'CIDR'">
52+
<input class="form-control input-sm" type="text" ng-model="rule.cidr" required ng-disabled="!(rule.remoteSecurityGroupId.id === undefined && rule.remoteSecurityGroupId === 'CIDR')" />
53+
</td>
54+
<td ng-if="rule.remoteSecurityGroupId.id === undefined && rule.remoteSecurityGroupId !== 'CIDR'">
55+
<input class="form-control input-sm" type="text" ng-model="rule.cidr" ng-disabled="rule.remoteSecurityGroupId.id === undefined && rule.remoteSecurityGroupId !== 'CIDR'"/>
56+
</td>
57+
2258
<td><a class="sm-label"
2359
ng-click="rulesController.removeRule($index)">
2460
<span class="glyphicon glyphicon-trash"></span></a>
2561
</td>
62+
63+
<!--Validation-->
2664
<td>
2765
<validation-error ng-if="rules['fromPort_'+$index].$error.min"
2866
message="Port should be greater than 0.">
@@ -36,6 +74,23 @@
3674
<validation-error ng-if="rules['toPort_'+$index].$error.max"
3775
message="Port should be less than or equal to 65535.">
3876
</validation-error>
77+
78+
<validation-error ng-if="rules['icmpType_'+$index].$error.min"
79+
message="ICMP Type should be greater than or equal to -1.">
80+
</validation-error>
81+
<validation-error ng-if="rules['icmpType_'+$index].$error.max"
82+
message="ICMP Type should be less than or equal to 255.">
83+
</validation-error>
84+
<validation-error ng-if="rules['icmpType_'+$index].$error.validateType"
85+
message="ICMP Type cannot be -1 when Code is greater that -1.">
86+
</validation-error>
87+
88+
<validation-error ng-if="rules['icmpCode_'+$index].$error.min"
89+
message="ICMP Code should be greater than or equal to -1.">
90+
</validation-error>
91+
<validation-error ng-if="rules['icmpCode_'+$index].$error.max"
92+
message="ICMP Code should be less than or equal to 255.">
93+
</validation-error>
3994
</td>
4095
</tr>
4196
</tbody>
@@ -51,5 +106,18 @@
51106
</div>
52107
</table>
53108
</div>
109+
<div class="col-md-12" style="margin-top: 20px">
110+
<div class="col-md-12">
111+
<p>
112+
<span ng-if="state.refreshingSecurityGroups"><span class="small glyphicon glyphicon-refresh glyphicon-spinning"></span></span>
113+
Security groups
114+
<span ng-if="!state.refreshingSecurityGroups">last refreshed {{ rulesController.getSecurityGroupRefreshTime() | timestamp }}</span>
115+
<span ng-if="state.refreshingSecurityGroups"> refreshing...</span>
116+
</p>
117+
<p>If you're not finding a security group that was recently added,
118+
<a href ng-click="rulesController.refreshSecurityGroups()">click here</a> to refresh the list.
119+
</p>
120+
</div>
121+
</div>
54122
</div>
55123
</ng-form>

app/scripts/modules/openstack/securityGroup/configure/wizard/upsert.controller.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = angular.module('spinnaker.securityGroup.openstack.create.contro
1717
.controller('openstackUpsertSecurityGroupController', function($q, $scope, $uibModalInstance, $state,
1818
application, securityGroup,
1919
accountService, openstackSecurityGroupTransformer, securityGroupReader, loadBalancerReader,
20-
_, searchService, v2modalWizardService, securityGroupWriter, taskMonitorService) {
20+
_, searchService, v2modalWizardService, securityGroupWriter, taskMonitorService, namingService) {
2121
var ctrl = this;
2222
$scope.isNew = !securityGroup.edit;
2323
$scope.securityGroup = securityGroup;
@@ -133,17 +133,15 @@ module.exports = angular.module('spinnaker.securityGroup.openstack.create.contro
133133
}
134134
else {
135135
$scope.securityGroup = openstackSecurityGroupTransformer.prepareForEdit(securityGroup);
136-
$scope.securityGroup.stack = extractStack($scope.securityGroup.name);
136+
137+
var result = namingService.parseServerGroupName($scope.securityGroup.name);
138+
$scope.securityGroup.stack = result.stack;
139+
$scope.securityGroup.detail = result.freeFormDetails;
137140
$scope.state.accountsLoaded = true;
138141
}
139142

140143
initializeSecurityGroupNames();
141144

142-
function extractStack(securityGroupName) {
143-
var n = securityGroupName.indexOf('-');
144-
return securityGroupName.slice( n + 1 , securityGroupName.length);
145-
}
146-
147145
// Controller API
148146
this.updateName = function() {
149147
$scope.securityGroup.name = this.getName();
@@ -152,7 +150,7 @@ module.exports = angular.module('spinnaker.securityGroup.openstack.create.contro
152150

153151
this.getName = function() {
154152
var securityGroup = $scope.securityGroup;
155-
var securityGroupName = [application.name, (securityGroup.stack || '')].join('-');
153+
var securityGroupName = _.compact([application.name, (securityGroup.stack), (securityGroup.detail)]).join('-');
156154
return _.trimRight(securityGroupName, '-');
157155
};
158156

@@ -172,7 +170,10 @@ module.exports = angular.module('spinnaker.securityGroup.openstack.create.contro
172170
cloudProvider: 'openstack',
173171
};
174172

175-
return securityGroupWriter.upsertSecurityGroup($scope.securityGroup, application, descriptor, params);
173+
var copyOfSG = angular.copy($scope.securityGroup);
174+
copyOfSG = openstackSecurityGroupTransformer.prepareForSaving(copyOfSG);
175+
176+
return securityGroupWriter.upsertSecurityGroup(copyOfSG, application, descriptor, params);
176177
}
177178
);
178179
};

app/scripts/modules/openstack/securityGroup/configure/wizard/upsert.controller.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ describe('Controller: openstackCreateSecurityGroupCtrl', function() {
3333
};
3434

3535
this.testEditData = {
36-
account: undefined, region: 'region1', name: 'sc111', edit: true, rules: [ ], accountName: undefined, stack: 'sc111'
36+
account: undefined, region: 'region1', name: 'sc111', edit: true, rules: [ ], accountName: undefined, description: undefined, detail: '', stack: ''
3737
};
3838

3939

4040
this.securityGroupDefaults = {
41-
provider: 'openstack', region: '', stack: '', detail: '', account: 'account1', rules: []
41+
provider: 'openstack', region: '', stack: '', description: '', detail: '', account: 'account1', rules: []
4242
};
4343

4444
this.$scope = $rootScope.$new();

app/scripts/modules/openstack/securityGroup/details/details.html

+21-3
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,36 @@ <h3 select-on-dbl-click>
4444
<dd>{{securityGroup.description}}</dd>
4545
</dl>
4646
</collapsible-section>
47-
<collapsible-section heading="Rules ({{securityGroup.ipRangeRules.length || 0}})" expanded="{{!!(securityGroup.ipRangeRules && securityGroup.ipRangeRules.length)}}">
47+
<collapsible-section heading="IP Range Rules ({{securityGroup.ipRangeRules.length || 0}})" expanded="{{!!(securityGroup.ipRangeRules && securityGroup.ipRangeRules.length)}}">
4848
<div ng-if="!securityGroup.ipRangeRules.length">None</div>
4949

5050
<dl ng-class="insightCtrl.vm.filtersExpanded ? '' : 'dl-horizontal dl-medium'"
5151
ng-repeat="ipRangeRule in securityGroup.ipRangeRules | orderBy: 'range.ip'">
5252
<dt>IP Range</dt>
5353
<dd>{{ipRangeRule.range.ip}}{{ipRangeRule.range.cidr}}</dd>
54-
<dt ng-if="ipRangeRule.portRanges && ipRangeRule.portRanges[0].startPort">Port Ranges</dt>
55-
<dd ng-repeat="portRange in ipRangeRule.portRanges" ng-if="portRange.startPort && portRange.endPort">
54+
<dt ng-if="ipRangeRule.portRanges && ipRangeRule.portRanges.length">Port Ranges</dt>
55+
<dd ng-repeat="portRange in ipRangeRule.portRanges">
5656
{{ipRangeRule.protocol}}: {{portRange.startPort}} &rarr; {{portRange.endPort}}
5757
</dd>
5858
</dl>
5959
</collapsible-section>
60+
<collapsible-section heading="Security Group Rules ({{securityGroup.securityGroupRules.length || 0}})" expanded="{{!!securityGroup.securityGroupRules.length}}">
61+
<div ng-if="!securityGroup.securityGroupRules.length">None</div>
62+
63+
<dl ng-class="insightCtrl.vm.filtersExpanded ? '' : 'dl-horizontal dl-medium'"
64+
ng-repeat="rule in securityGroup.securityGroupRules | orderBy: 'securityGroup.name' ">
65+
<dt>Security Group</dt>
66+
<dd ng-if="rule.securityGroup.name">
67+
<a ui-sref="^.securityGroupDetails({name: rule.securityGroup.name, accountId: rule.securityGroup.accountName, region: rule.securityGroup.region, vpcId: rule.securityGroup.vpcId, provider: 'openstack'})">
68+
<account-tag account="rule.securityGroup.accountName || rule.securityGroup.accountId" ng-if="rule.securityGroup.accountName !== securityGroup.accountName"></account-tag>
69+
{{rule.securityGroup.name}} ({{rule.securityGroup.id}})
70+
</a>
71+
</dd>
72+
<dt ng-if="rule.portRanges && rule.portRanges.length !== null">Port Ranges</dt>
73+
<dd ng-repeat="portRange in rule.portRanges">
74+
{{rule.protocol}}: {{portRange.startPort}} &rarr; {{portRange.endPort}}
75+
</dd>
76+
</dl>
77+
</collapsible-section>
6078
</div>
6179
</div>

0 commit comments

Comments
 (0)