Skip to content

Commit

Permalink
frontend: add service accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
squat committed Sep 14, 2016
1 parent 6aeb20f commit eae20da
Show file tree
Hide file tree
Showing 16 changed files with 157 additions and 7 deletions.
2 changes: 2 additions & 0 deletions frontend/public/_vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ $color-hpa-dark: $color-controller-dark;
$color-deployment-dark: $color-controller-dark;
$color-petset-dark: $color-controller-dark;

$color-serviceaccount-dark: $color-configmap-dark;

$color-grey-lightest: #efefef;
$color-grey-medium: #666;
$color-grey-dark: #3D3D3D;
Expand Down
1 change: 1 addition & 0 deletions frontend/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ angular.module('bridge', [
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('daemonsets');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('jobs');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('horizontalpodautoscalers');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('serviceaccounts');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('configmaps');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('secrets');
activeNamespaceSvcProvider.registerNamespaceFriendlyPrefix('replicasets');
Expand Down
1 change: 1 addition & 0 deletions frontend/public/components/_module.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ import './service';
import './replication-controller';
import './RBAC';
import './horizontal-pod-autoscaler';
import './service-account';
8 changes: 8 additions & 0 deletions frontend/public/components/factory/list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import {angulars, register} from '../react-wrapper';

const filters = {
'name': (filter, obj) => fuzzy(filter, obj.metadata.name),

'selector': (selector, obj) => {
if (!selector || !selector.values || !selector.values.size) {
return true;
}
return selector.values.has(_.get(obj, selector.field));
},

'pod-status': (phases, pod) => {
if (!phases || !phases.size) {
return true;
Expand Down
36 changes: 33 additions & 3 deletions frontend/public/components/secret.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {angulars} from './react-wrapper';
import {makeDetailsPage, makeListPage, makeList} from './factory';
import ConfigMapAndSecretData from './configmap-and-secret-data';
import {Cog, LabelList, ResourceIcon, Timestamp, detailsPage} from './utils'
import classnames from 'classnames';

const SecretCog = ({secret}) => {
const kind = angulars.kinds.SECRET;
Expand Down Expand Up @@ -58,10 +59,39 @@ const SecretDetails = (secret) => {
</div>;
}

const withSecretsList = (Row) => {
return class WithSecretsList extends React.Component {
constructor (props) {
super(props);
this.state = {open: false};
}

onClick_ (e) {
e.preventDefault();
this.setState({open: !this.state.open});
}

render () {
const {metadata: {namespace}, secrets} = this.props;
const filters = {selector: {field: 'metadata.name', values: new Set(_.map(secrets, 'name'))}};

return (
<div onClick={this.onClick_.bind(this)} ref="target" className={classnames({clickable: !!secrets})} >
<Row {...this.props} />
{
this.state.open && secrets &&
<SecretsList namespace={namespace} filters={filters}></SecretsList>
}
</div>
);
}
}
}

const pages = [{href: 'details', name: 'Details', component: detailsPage(SecretDetails)}];

const Secrets = makeList('Secrets', 'SECRET', SecretHeader, SecretRow);
const SecretsPage = makeListPage('SecretsPage', 'SECRET', Secrets);
const SecretsList = makeList('Secrets', 'SECRET', SecretHeader, SecretRow);
const SecretsPage = makeListPage('SecretsPage', 'SECRET', SecretsList);
const SecretsDetailsPage = makeDetailsPage('SecretsDetailsPage', 'SECRET', pages);

export {Secrets, SecretsPage, SecretsDetailsPage};
export {SecretsList, SecretsPage, SecretsDetailsPage, withSecretsList};
77 changes: 77 additions & 0 deletions frontend/public/components/service-account.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';

import {angulars} from './react-wrapper';
import {makeDetailsPage, makeListPage, makeList} from './factory';
import {Cog, ResourceIcon, Timestamp} from './utils'
import {SecretsList, withSecretsList} from './secret';

const Header = () => <div className="row co-m-table-grid__head">
<div className="col-xs-4">Name</div>
<div className="col-xs-4">Secrets</div>
<div className="col-xs-4">Age</div>
</div>;

const ServiceAccountCog = ({serviceaccount}) => {
const kind = angulars.kinds.SERVICEACCOUNT;
const {factory: {Delete}} = Cog;
const options = [Delete].map(f => f(kind, serviceaccount));
return <Cog options={options} size="small" anchor="left"></Cog>;
}

const ServiceAccountRow = (serviceaccount) => {
const {metadata: {name, namespace, uid, creationTimestamp}, secrets} = serviceaccount;

return (
<div className="row co-resource-list__item">
<div className="col-xs-4">
<ServiceAccountCog serviceaccount={serviceaccount} />
<ResourceIcon kind={angulars.kinds.SERVICEACCOUNT.id}></ResourceIcon>
<a href={`ns/${namespace}/${angulars.kinds.SERVICEACCOUNT.plural}/${name}/details`} title={uid}>
{serviceaccount.metadata.name}
</a>
</div>
<div className="col-xs-4">
{secrets ? secrets.length : 0}
</div>
<div className="col-xs-4">
{moment(creationTimestamp).fromNow()}
</div>
</div>
);
}

const Details = (serviceaccount) => {
const {metadata: {namespace, creationTimestamp}, secrets} = serviceaccount;
const filters = {selector: {field: 'metadata.name', values: new Set(_.map(secrets, 'name'))}};

return (
<div>
<div className="co-m-pane__body">
<div className="row">
<div className="col-md-6">
<div className="co-m-pane__body-group">
<dl>
<dt>Created At</dt>
<dd><Timestamp timestamp={creationTimestamp} /></dd>
</dl>
</div>
</div>
</div>
</div>
<div className="co-m-pane__body">
<div className="row">
<div className="col-xs-12">
<h1 className="co-section-title">Secrets</h1>
</div>
</div>
<SecretsList namespace={namespace} filters={filters}></SecretsList>
</div>
</div>
);
}

const pages = [{href: 'details', name: 'Details', component: Details}];
const ServiceAccountsDetailsPage = makeDetailsPage('ServiceAccountsDetailsPage', 'SERVICEACCOUNT', pages);
const ServiceAccountsList = makeList('ServiceAccounts', 'SERVICEACCOUNT', Header, withSecretsList(ServiceAccountRow));
const ServiceAccountsPage = makeListPage('ServiceAccountsPage', 'SERVICEACCOUNT', ServiceAccountsList);
export {ServiceAccountsList, ServiceAccountsPage, ServiceAccountsDetailsPage};
8 changes: 5 additions & 3 deletions frontend/public/components/utils/firehose.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Firehose extends React.Component {
return this.firehose && this.firehose.id;
}

stateToProps ({k8s}) {
stateToProps ({k8s}, {filters}) {
const reduxID = this.id;

// are we watching a single object?
Expand All @@ -39,11 +39,13 @@ export class Firehose extends React.Component {

// We are watching a list of objects.
const data = k8s.getIn([reduxID, 'data']);
const filters = k8s.getIn([reduxID, 'filters']);
const _filters = k8s.getIn([reduxID, 'filters']);

return {
data: data && data.toArray().map(p => p.toJSON()),
filters: filters && filters.toJS(),
// This is a hack to allow filters passed down from props to make it to
// the injected component. Ideally filters should all come from redux.
filters: _.extend({}, _filters && _filters.toJS(), filters),
loadError: k8s.getIn([reduxID, 'loadError']),
loaded: k8s.getIn([reduxID, 'loaded']),
};
Expand Down
1 change: 1 addition & 0 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<co-navbar-link ng-cloak href="{{'replicationcontrollers'|ns}}">Replication Controllers</co-navbar-link>
<co-navbar-link ng-cloak href="{{'horizontalpodautoscalers'|ns}}">Autoscalers</co-navbar-link>
<co-navbar-link ng-cloak href="{{'pods'|ns}}">Pods</co-navbar-link>
<co-navbar-link ng-cloak href="{{'serviceaccounts'|ns}}">Service Accounts</co-navbar-link>
<co-navbar-link ng-cloak href="{{'configmaps'|ns}}">Config Maps</co-navbar-link>
<co-navbar-link ng-cloak href="{{'secrets'|ns}}">Secrets</co-navbar-link>
<co-navbar-link ng-cloak href="{{'events'|ns}}">Events</co-navbar-link>
Expand Down
8 changes: 8 additions & 0 deletions frontend/public/module/k8s/enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ angular.module('k8s').constant('k8sEnum', {
isExtension: true,
apiVersion: 'v1beta1',
},
SERVICEACCOUNT: {
id: 'serviceaccount',
kind: 'ServiceAccount',
label: 'Service Account',
labelPlural: 'Service Accounts',
path: 'serviceaccounts',
plural: 'serviceaccounts',
},
REPLICASET: {
id: 'replicaset',
kind: 'replicaset',
Expand Down
1 change: 1 addition & 0 deletions frontend/public/module/k8s/k8s.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ angular.module('k8s')
this.jobs = addDefaults({}, k8sEnum.Kind.JOB);
this.daemonsets = addDefaults({}, k8sEnum.Kind.DAEMONSET);
this.horizontalpodautoscalers = addDefaults({}, k8sEnum.Kind.HORIZONTALPODAUTOSCALER);
this.serviceaccounts = addDefaults({}, k8sEnum.Kind.SERVICEACCOUNT);
this.secrets = addDefaults({}, k8sEnum.Kind.SECRET);

this.componentstatuses = addDefaults({}, k8sEnum.Kind.COMPONENTSTATUS);
Expand Down
4 changes: 4 additions & 0 deletions frontend/public/module/ui/icons/_resource-icon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ $height: 18px;
background-color: $color-hpa-dark;
}

.co-m-resource-icon--serviceaccount {
background-color: $color-serviceaccount-dark;
}

.co-m-resource-icon--pod {
background-color: $color-pod-dark;
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/module/ui/icons/resource-icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ function iconLabel(k8sKind, kindId) {
return 'S';
case k8sKind.HORIZONTALPODAUTOSCALER.id:
return 'HPA';
case k8sKind.SERVICEACCOUNT.id:
return 'SA';
case k8sKind['*'].id:
return 'All';
case k8sKind.ROLE.id:
Expand Down
6 changes: 6 additions & 0 deletions frontend/public/module/ui/labels/_label.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ $height: 18px;
}
}

&.co-m-label--serviceaccount {
.co-m-label__link {
color: $color-serviceaccount-dark;
}
}

&.co-m-label--pod {
.co-m-label__link {
color: $color-pod-dark;
Expand Down
1 change: 1 addition & 0 deletions frontend/public/module/ui/resources/resource-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<co-generic-k8s-list ng-if="kind == 'service'" namespace="namespace" component="'Services'" selector="selector"></co-generic-k8s-list>
<co-generic-k8s-list ng-if="kind == 'daemonset'" namespace="namespace" component="'DaemonSets'" selector="selector"></co-generic-k8s-list>
<co-generic-k8s-list ng-if="kind == 'horizontalpodautoscaler'" namespace="namespace" component="'HorizontalPodAutoscalers'" selector="selector"></co-generic-k8s-list>
<co-generic-k8s-list ng-if="kind == 'serviceaccount'" namespace="namespace" component="'ServiceAccounts'" selector="selector"></co-generic-k8s-list>
<co-generic-k8s-list ng-if="kind == 'configmap'" namespace="namespace" component="'ConfigMaps'" selector="selector"></co-generic-k8s-list>
<co-generic-k8s-list ng-if="kind == 'secret'" namespace="namespace" component="'Secrets'" selector="selector"></co-generic-k8s-list>

Expand Down
2 changes: 1 addition & 1 deletion frontend/public/page/k8s-list/k8s-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ angular.module('bridge.page')
return;
}

if (['daemonsets', 'configmaps', 'secrets', 'jobs', 'horizontalpodautoscalers'].indexOf($routeParams.kind) === -1) {
if (['daemonsets', 'configmaps', 'secrets', 'jobs', 'horizontalpodautoscalers', 'serviceaccounts'].indexOf($routeParams.kind) === -1) {
$scope.canCreate = true;
} else {
$scope.canCreate = false;
Expand Down
6 changes: 6 additions & 0 deletions frontend/public/page/search/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
type="radio"/>
<span>Autoscalers</span>
</a>
<a href="#" ng-click="changeKind(kinds.SERVICEACCOUNT.id)">
<input name="selectedKindId"
ng-checked="fields.selectedKindId == kinds.SERVICEACCOUNT.id"
type="radio"/>
<span ng-bind="kinds.SERVICEACCOUNT.labelPlural"></span>
</a>
<a href="#" ng-click="changeKind(kinds.CONFIGMAP.id)">
<input name="selectedKindId"
ng-checked="fields.selectedKindId == kinds.CONFIGMAP.id"
Expand Down

0 comments on commit eae20da

Please sign in to comment.