Skip to content

Commit 65875b4

Browse files
digitalfishpondmaciaszczykm
authored andcommitted
Status icons for Services visible on list and resources pages + no labels handled (#845)
1 parent 12511ac commit 65875b4

File tree

9 files changed

+133
-10
lines changed

9 files changed

+133
-10
lines changed

src/app/backend/resource/service/servicecommon.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func ToService(service *api.Service) Service {
2929
// TODO(maciaszczykm): Fill ExternalEndpoints with data.
3030
Selector: service.Spec.Selector,
3131
ClusterIP: service.Spec.ClusterIP,
32+
Type: service.Spec.Type,
3233
}
3334
}
3435

src/app/backend/resource/service/servicelist.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package service
1717
import (
1818
"log"
1919

20+
"k8s.io/kubernetes/pkg/api"
2021
client "k8s.io/kubernetes/pkg/client/unversioned"
2122

2223
"github.com/kubernetes/dashboard/resource/common"
@@ -38,6 +39,9 @@ type Service struct {
3839
// Label selector of the service.
3940
Selector map[string]string `json:"selector"`
4041

42+
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
43+
Type api.ServiceType `json:"type"`
44+
4145
// ClusterIP is usually assigned by the master. Valid values are None, empty string (""), or
4246
// a valid IP address. None can be specified for headless services when proxying is not required
4347
ClusterIP string `json:"clusterIP"`

src/app/externs/backendapi.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ backendApi.ServiceDetail;
407407
* internalEndpoint: !backendApi.Endpoint,
408408
* externalEndpoints: !Array<!backendApi.Endpoint>,
409409
* selector: !Object<string, string>,
410+
* type: string,
410411
* clusterIP: string
411412
* }}
412413
*/

src/app/frontend/common/components/labels/labels.html

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
limitations under the License.
1515
-->
1616

17-
<kd-middle-ellipsis display-string="{{::key}}: {{::value}}" class="kd-labels"
18-
ng-repeat="(key, value) in ::labelsCtrl.labels"
19-
ng-if="labelsCtrl.isVisible($index)">
20-
</kd-middle-ellipsis>
21-
<div class="kd-labels kd-labels-switch" ng-show="labelsCtrl.isMoreAvailable()"
22-
ng-click="labelsCtrl.switchLabelsView()">
23-
{{labelsCtrl.isShowingAll() ?
24-
labelsCtrl.i18n.MSG_LABELS_SHOW_LESS_TOOLTIP : labelsCtrl.i18n.MSG_LABELS_SHOW_ALL_TOOLTIP}}
17+
<div ng-if="::labelsCtrl.labels">
18+
<kd-middle-ellipsis display-string="{{::key}}: {{::value}}" class="kd-labels"
19+
ng-repeat="(key, value) in ::labelsCtrl.labels"
20+
ng-if="labelsCtrl.isVisible($index)">
21+
</kd-middle-ellipsis>
22+
<div class="kd-labels kd-labels-switch" ng-show="labelsCtrl.isMoreAvailable()"
23+
ng-click="labelsCtrl.switchLabelsView()">
24+
{{labelsCtrl.isShowingAll() ?
25+
labelsCtrl.i18n.MSG_LABELS_SHOW_LESS_TOOLTIP : labelsCtrl.i18n.MSG_LABELS_SHOW_ALL_TOOLTIP}}
26+
</div>
2527
</div>
28+
<div ng-hide="::labelsCtrl.labels">-</div>

src/app/frontend/servicelist/servicecardlist.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@
2727

2828
<kd-resource-card ng-repeat="service in ::$ctrl.services"
2929
object-meta="service.objectMeta" type-meta="service.typeMeta">
30+
<kd-resource-card-status layout="row">
31+
<md-icon class="material-icons" ng-if="::$ctrl.isPending(service)">
32+
timelapse
33+
<md-tooltip md-direction="right">{{::$ctrl.i18n.MSG_POD_IS_PENDING_TOOLTIP}}</md-tooltip>
34+
</md-icon>
35+
<md-icon class="material-icons kd-success" ng-if="::$ctrl.isSuccess(service)">
36+
check_circle
37+
</md-icon>
38+
</kd-resource-card-status>
3039
<kd-resource-card-columns>
3140
<kd-resource-card-column>
3241
<a ng-href="{{::$ctrl.getServiceDetailHref(service)}}">
@@ -36,7 +45,9 @@
3645
<kd-resource-card-column>
3746
<kd-labels labels="::service.objectMeta.labels"></kd-labels>
3847
</kd-resource-card-column>
39-
<kd-resource-card-column>{{::service.clusterIP}}</kd-resource-card-column>
48+
<kd-resource-card-column>
49+
<div>{{::$ctrl.getServiceClusterIP(service)}}</div>
50+
</kd-resource-card-column>
4051
<kd-resource-card-column>
4152
<div ng-if="::service.internalEndpoint">
4253
<kd-internal-endpoint endpoint="::service.internalEndpoint"></kd-internal-endpoint>

src/app/frontend/servicelist/servicecardlist_component.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,37 @@ export class ServiceCardListController {
4040
return this.state_.href(
4141
stateName, new StateParams(service.objectMeta.namespace, service.objectMeta.name));
4242
}
43+
44+
/**
45+
* Returns true if Service has no assigned Cluster IP
46+
* or if Service type is LoadBalancer or NodePort and doesn't have an external endpoint IP
47+
* @param {!backendApi.Service} service
48+
* @return {boolean}
49+
* @export
50+
*/
51+
isPending(service) {
52+
return service.clusterIP === null ||
53+
((service.type === 'LoadBalancer' || service.type === 'NodePort') &&
54+
service.externalEndpoints === null);
55+
}
56+
57+
/**
58+
* Returns true if Service has ClusterIP assigned and one of the following conditions is met:
59+
* - Service type is LoadBalancer or NodePort and has an external endpoint IP
60+
* - Service type is not LoadBalancer or NodePort
61+
* @param {!backendApi.Service} service
62+
* @return {boolean}
63+
* @export
64+
*/
65+
isSuccess(service) { return !this.isPending(service); }
66+
67+
/**
68+
* Returns the service's clusterIP or a dash ('-') if it is not yet set
69+
* @param {!backendApi.Service} service
70+
* @return {string}
71+
* @export
72+
*/
73+
getServiceClusterIP(service) { return service.clusterIP ? service.clusterIP : '-'; }
4374
}
4475

4576
/**
@@ -76,4 +107,6 @@ const i18n = {
76107
/** @export {string} @desc Label 'External endpoints' which appears as a column label in the
77108
table of services (service list view). */
78109
MSG_SERVICE_LIST_EXTERNAL_ENDPOINTS_LABEL: goog.getMsg('External endpoints'),
110+
/** @export {string} @desc tooltip for pending pod card icon */
111+
MSG_SERVICE_IS_PENDING_TOOLTIP: goog.getMsg('This service is in a pending state.'),
79112
};

src/app/frontend/servicelist/servicelist.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616

1717
<kd-content-card>
1818
<kd-content>
19-
<kd-service-card-list services="::ctrl.serviceList.services"></kd-service-card-list>
19+
<kd-service-card-list services="::ctrl.serviceList.services" with-statuses="true"></kd-service-card-list>
2020
</kd-content>
2121
</kd-content-card>

src/test/frontend/replicationcontrollerlist/replicationcontrollercard_component_test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14+
1415
import replicationControllerListModule from 'replicationcontrollerlist/replicationcontrollerlist_module';
1516

1617
describe('Replication controller card', () => {

src/test/frontend/servicelist/servicecardlist_component_test.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,73 @@ describe('Service list controller', () => {
3838
},
3939
})).toBe('#/service/foo-namespace/foo-service');
4040
});
41+
42+
it('should return true when service.clusterIP is null', () => {
43+
expect(ctrl.isPending({
44+
clusterIP: null,
45+
})).toBeTruthy();
46+
});
47+
48+
it('should return false when service.clusterIP is set', () => {
49+
expect(ctrl.isPending({
50+
clusterIP: '10.67.252.103',
51+
})).toBeFalsy();
52+
});
53+
54+
it('should return true when service.type is LoadBalancer AND service.externalEndpoints is null',
55+
() => {
56+
expect(ctrl.isPending({
57+
clusterIP: '10.67.252.103',
58+
type: 'LoadBalancer',
59+
externalEndpoints: null,
60+
})).toBeTruthy();
61+
});
62+
63+
it('should return true when service.type is NodePort AND service.externalEndpoints is null',
64+
() => {
65+
expect(ctrl.isPending({
66+
clusterIP: '10.67.252.103',
67+
type: 'NodePort',
68+
externalEndpoints: null,
69+
})).toBeTruthy();
70+
});
71+
72+
it('should return true when service.type is LoadBalancer AND service.externalEndpoints is set',
73+
() => {
74+
expect(ctrl.isSuccess({
75+
clusterIP: '10.67.252.103',
76+
type: 'LoadBalancer',
77+
externalEndpoints: ['10.64.0.4:80', '10.64.1.5:80', '10.64.2.4:80'],
78+
})).toBeTruthy();
79+
});
80+
81+
it('should return true when service.type is NodePort AND service.externalEndpoints is set',
82+
() => {
83+
expect(ctrl.isSuccess({
84+
clusterIP: '10.67.252.103',
85+
type: 'NodePort',
86+
externalEndpoints: ['10.64.0.4:80', '10.64.1.5:80', '10.64.2.4:80'],
87+
})).toBeTruthy();
88+
});
89+
90+
it('should return true when service.type is ClusterIP and service.externalEndpoints is null',
91+
() => {
92+
expect(ctrl.isSuccess({
93+
clusterIP: '10.67.252.103',
94+
type: 'ClusterIP',
95+
externalEndpoints: null,
96+
})).toBeTruthy();
97+
});
98+
99+
it('should return the service clusterIP when teh clusterIP is set', () => {
100+
expect(ctrl.getServiceClusterIP({
101+
clusterIP: '10.67.252.103',
102+
})).toBe('10.67.252.103');
103+
});
104+
105+
it('should return the service clusterIP when teh clusterIP is set', () => {
106+
expect(ctrl.getServiceClusterIP({
107+
clusterIP: null,
108+
})).toBe('-');
109+
});
41110
});

0 commit comments

Comments
 (0)