提交 bd650fe8 编写于 作者: P Piotr Bryk 提交者: GitHub

Details page for secrets (#1060)

上级 a2625978
......@@ -544,4 +544,10 @@
<translation id="4677646778972512564" key="MSG_RC_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Label 'Namespace' which appears as a column label in the table of replication controllers (RC list view).">Namespace</translation>
<translation id="6106340570580081121" key="MSG_DAEMON_SET_DETAIL_SERVICES_TITLE" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title 'Services' for the services information section on the daemon set detail page.">Services</translation>
<translation id="4440799155040620024" key="MSG_REPLICA_SET_DETAIL_SERVICES_TITLE" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title 'Services' for the services information section on the replica set detail page.">Services</translation>
<translation id="7998045333531060596" key="MSG_SECRET_INFO_DATA_SECTION" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name.">Data</translation>
<translation id="4100438030563245765" key="MSG_SECRET_DETAIL_SECRET_LABEL" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Label 'Secret' which appears at the top of the delete dialog, opened from a secret details page.">Secret</translation>
<translation id="6735398210716403327" key="MSG_SECRET_INFO_DETAILS_SECTION" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name.">Details</translation>
<translation id="2492306272014913632" key="MSG_SECRET_INFO_NAME_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name entry.">Name</translation>
<translation id="2646286682599680700" key="MSG_SECRET_INFO_NAMESPACE_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section namespace entry.">Namespace</translation>
<translation id="849285697167851134" key="MSG_SECRET_INFO_LABELS_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section labels entry.">Labels</translation>
</translationbundle>
\ No newline at end of file
......@@ -712,8 +712,8 @@
<translation id="7249897739191369792" key="MSG_CONTAINER_DETAILS_NO_ENV_VARS" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Label when there is no container environment variables.">-</translation>
<translation id="3684607543489263600" key="MSG_ENV_FROM_CONFIG_MAP" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Label for environment variable that comes from a Config Map">value from ConfigMap <ph name="NAME" />/<ph name="KEY" /></translation>
<translation id="6987669710926523285" key="MSG_NODE_LIST_READY_LABEL" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Label 'Ready' which appears as a column label in the table of nodes (node list view).">Ready</translation>
<translation id="910153156225182799" key="MSG_CONFIG_MAP_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Config map list header: namespace.">Namespace</translation>
<translation id="7091154364335994224" key="MSG_DAEMON_SET_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Label 'Namespace' which appears as a column label in the table of daemon sets (daemon set list view).">Namespace</translation>
<translation id="910153156225182799" key="MSG_CONFIG_MAP_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Config map list header: namespace.">Namespace</translation>
<translation id="4677646778972512564" key="MSG_RC_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Label 'Namespace' which appears as a column label in the table of replication controllers (RC list view).">Namespace</translation>
<translation id="6537383105836584689" key="MSG_DEPLOYMENT_LIST_NAMESPACE_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Label 'Namespace' which appears as a column label in the table of deployments (deployment list view).">Namespace</translation>
<translation id="2872431258829082433" key="MSG_JOB_LIST_NAME_LABEL" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Label 'Name' which appears as a column label in the table of jobs (Job list view).">Name</translation>
......@@ -733,4 +733,10 @@
<translation id="6106340570580081121" key="MSG_DAEMON_SET_DETAIL_SERVICES_TITLE" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title 'Services' for the services information section on the daemon set detail page.">Services</translation>
<translation id="6106340570580081121" key="MSG_DAEMON_SET_DETAIL_SERVICES_TITLE" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title 'Services' for the services information section on the daemon set detail page.">Services</translation>
<translation id="4440799155040620024" key="MSG_REPLICA_SET_DETAIL_SERVICES_TITLE" source="/home/denis/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title 'Services' for the services information section on the replica set detail page.">Services</translation>
<translation id="7998045333531060596" key="MSG_SECRET_INFO_DATA_SECTION" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name.">Data</translation>
<translation id="4100438030563245765" key="MSG_SECRET_DETAIL_SECRET_LABEL" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Label 'Secret' which appears at the top of the delete dialog, opened from a secret details page.">Secret</translation>
<translation id="6735398210716403327" key="MSG_SECRET_INFO_DETAILS_SECTION" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name.">Details</translation>
<translation id="2492306272014913632" key="MSG_SECRET_INFO_NAME_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section name entry.">Name</translation>
<translation id="2646286682599680700" key="MSG_SECRET_INFO_NAMESPACE_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section namespace entry.">Namespace</translation>
<translation id="849285697167851134" key="MSG_SECRET_INFO_LABELS_ENTRY" source="/usr/local/google/home/bryk/src/github.com/kubernetes/dashboard/.tmp/serve/app-dev.js" desc="Config map info details section labels entry.">Labels</translation>
</translationbundle>
\ No newline at end of file
......@@ -32,9 +32,9 @@ import (
"github.com/kubernetes/dashboard/src/app/backend/resource/job"
"github.com/kubernetes/dashboard/src/app/backend/resource/namespace"
"github.com/kubernetes/dashboard/src/app/backend/resource/node"
"github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolume"
"github.com/kubernetes/dashboard/src/app/backend/resource/petset"
"github.com/kubernetes/dashboard/src/app/backend/resource/pod"
"github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolume"
"github.com/kubernetes/dashboard/src/app/backend/resource/replicaset"
"github.com/kubernetes/dashboard/src/app/backend/resource/replicationcontroller"
"github.com/kubernetes/dashboard/src/app/backend/resource/secret"
......@@ -289,13 +289,17 @@ func CreateHTTPAPIHandler(client *clientK8s.Client, heapsterClient client.Heapst
Writes(namespace.NamespaceDetail{}))
apiV1Ws.Route(
apiV1Ws.GET("/secret/{namespace}").
apiV1Ws.GET("/secret").
To(apiHandler.handleGetSecretList).
Writes(secret.SecretList{}))
apiV1Ws.Route(
apiV1Ws.GET("/secret").
apiV1Ws.GET("/secret/{namespace}").
To(apiHandler.handleGetSecretList).
Writes(secret.SecretList{}))
apiV1Ws.Route(
apiV1Ws.GET("/secret/{namespace}/{name}").
To(apiHandler.handleGetSecretDetail).
Writes(secret.SecretDetail{}))
apiV1Ws.Route(
apiV1Ws.POST("/secret").
To(apiHandler.handleCreateImagePullSecret).
......@@ -922,6 +926,17 @@ func (apiHandler *APIHandler) handleCreateImagePullSecret(request *restful.Reque
response.WriteHeaderAndEntity(http.StatusCreated, secret)
}
func (apiHandler *APIHandler) handleGetSecretDetail(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
name := request.PathParameter("name")
result, err := secret.GetSecretDetail(apiHandler.client, namespace, name)
if err != nil {
handleInternalError(response, err)
return
}
response.WriteHeaderAndEntity(http.StatusCreated, result)
}
// Handles get secrets list API call.
func (apiHandler *APIHandler) handleGetSecretList(request *restful.Request, response *restful.Response) {
namespace := parseNamespacePathParameter(request)
......@@ -947,8 +962,8 @@ func (apiHandler *APIHandler) handleGetConfigMapList(request *restful.Request, r
func (apiHandler *APIHandler) handleGetConfigMapDetail(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
service := request.PathParameter("configmap")
result, err := configmap.GetConfigMapDetail(apiHandler.client, namespace, service)
name := request.PathParameter("configmap")
result, err := configmap.GetConfigMapDetail(apiHandler.client, namespace, name)
if err != nil {
handleInternalError(response, err)
return
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package secret
import (
"log"
"github.com/kubernetes/dashboard/src/app/backend/resource/common"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
)
// SecretDetail API resource provides mechanisms to inject containers with configuration data while keeping
// containers agnostic of Kubernetes
type SecretDetail struct {
ObjectMeta common.ObjectMeta `json:"objectMeta"`
TypeMeta common.TypeMeta `json:"typeMeta"`
// Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN
// or leading dot followed by valid DNS_SUBDOMAIN.
// The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here.
Data map[string][]byte `json:"data"`
// Used to facilitate programmatic handling of secret data.
Type api.SecretType `json:"type"`
}
// GetSecretDetail returns returns detailed information about a secret
func GetSecretDetail(client *client.Client, namespace, name string) (*SecretDetail, error) {
log.Printf("Getting details of %s secret in %s namespace", name, namespace)
rawSecret, err := client.Secrets(namespace).Get(name)
if err != nil {
return nil, err
}
return getSecretDetail(rawSecret), nil
}
func getSecretDetail(rawSecret *api.Secret) *SecretDetail {
return &SecretDetail{
ObjectMeta: common.NewObjectMeta(rawSecret.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindSecret),
Data: rawSecret.Data,
Type: rawSecret.Type,
}
}
......@@ -676,6 +676,15 @@ backendApi.NamespaceList;
*/
backendApi.NamespaceDetail;
/**
* @typedef {{
* objectMeta: !backendApi.ObjectMeta,
* typeMeta: !backendApi.TypeMeta,
* data: !Object<string, string>,
* }}
*/
backendApi.SecretDetail;
/**
* @typedef {{
* objectMeta: !backendApi.ObjectMeta,
......
......@@ -93,6 +93,12 @@ a {
display: inline-block;
}
.kd-pre-block {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
// Disables animation on md-tab switch. Avoids some flickering and is simpler.
[role='tabpanel'] {
transition: none;
......
......@@ -35,7 +35,7 @@ export default function stateConfig($stateProvider) {
template: `<div ui-view="${toolbarViewName}"></div>`,
},
[actionbarViewName]: {
template: `<div ui-view="${actionbarViewName}"></div>`,
template: `<div ui-view="${actionbarViewName}" layout="row"></div>`,
},
},
});
......
......@@ -30,6 +30,8 @@
.kd-actionbar-breadcrumbs {
flex-grow: 2;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
}
......
......@@ -14,11 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<kd-actionbar-edit-item resource-kind-name="{{$ctrl.resourceKindName}}"
type-meta="$ctrl.typeMeta"
object-meta="$ctrl.objectMeta">
</kd-actionbar-edit-item>
<kd-actionbar-delete-item resource-kind-name="{{$ctrl.resourceKindName}}"
<div layout="row">
<kd-actionbar-edit-item resource-kind-name="{{$ctrl.resourceKindName}}"
type-meta="$ctrl.typeMeta"
object-meta="$ctrl.objectMeta">
</kd-actionbar-delete-item>
</kd-actionbar-edit-item>
<kd-actionbar-delete-item resource-kind-name="{{$ctrl.resourceKindName}}"
type-meta="$ctrl.typeMeta"
object-meta="$ctrl.objectMeta">
</kd-actionbar-delete-item>
</div>
......@@ -22,6 +22,8 @@
kd-info-card-section {
display: flex;
flex-grow: 1;
max-width: 100%;
overflow: hidden;
padding-right: 2 * $baseline-grid;
}
}
......@@ -15,7 +15,7 @@ limitations under the License.
-->
<div layout="row" class="kd-info-card-entry">
<div>{{$ctrl.title}}:</div>
<div flex="nogrow">{{$ctrl.title}}:</div>
<!-- TODO avoid using specified flex number -->
<div flex="90" class="kd-info-card-entry-content" ng-transclude></div>
<div flex="auto" class="kd-info-card-entry-content" ng-transclude></div>
</div>
......@@ -24,5 +24,6 @@
.kd-info-card-entry-content {
font-weight: $regular-font-weight;
margin-left: $baseline-grid / 2;
overflow: hidden;
white-space: normal;
}
......@@ -17,6 +17,7 @@
.kd-info-card-section-container {
display: flex;
flex-grow: 1;
max-width: 100%;
}
.kd-info-card-section-name {
......
......@@ -55,8 +55,6 @@ export default function middleEllipsisDirective(middleEllipsisFilter, $window) {
let nonNullElement = ellipsisElem;
angular.element($window).ready(recalculateTextLength);
angular.element($window).bind('resize', recalculateTextLength);
function recalculateTextLength() {
......@@ -64,6 +62,7 @@ export default function middleEllipsisDirective(middleEllipsisFilter, $window) {
container.offsetWidth, nonNullElement, element, middleEllipsisFilter,
ctrl.displayString);
}
recalculateTextLength();
scope.$on('$destroy', () => {
angular.element($window).unbind('ready', recalculateTextLength);
......
......@@ -20,7 +20,7 @@ limitations under the License.
<kd-info-card-section name="{{::$ctrl.i18n.MSG_CONFIG_MAP_INFO_DATA_SECTION}}">
<kd-info-card-entry ng-repeat="(key, value) in ::$ctrl.configMapDetail.data"
title="{{::key}}">
<pre>{{::value}}</pre>
<pre class="kd-pre-block">{{::value}}</pre>
</kd-info-card-entry>
</kd-info-card-section>
</kd-info-card>
......
......@@ -34,7 +34,8 @@ import routeConfig from './index_route';
import serviceDetailModule from './servicedetail/servicedetail_module';
import serviceListModule from './servicelist/servicelist_module';
import workloadsModule from './workloads/workloads_module';
import secretListModule from './secretlist/secretlist_module';
import secretListModule from './secretlist/module';
import secretDetailModule from './secretdetail/module';
import podDetailModule from './poddetail/poddetail_module';
import petSetListModule from './petsetlist/petsetlist_module';
import configMapListModule from './configmaplist/configmaplist_module';
......@@ -72,6 +73,7 @@ export default angular
configMapListModule.name,
configMapDetailModule.name,
secretListModule.name,
secretDetailModule.name,
])
.config(indexConfig)
.config(routeConfig);
<!--
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<kd-actionbar-detail-buttons
resource-kind-name="{{::$ctrl.i18n.MSG_SECRET_DETAIL_SECRET_LABEL}}"
type-meta="$ctrl.details.typeMeta"
object-meta="$ctrl.details.objectMeta">
</kd-actionbar-detail-buttons>
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @final
*/
export class ActionBarController {
/**
* @param {!backendApi.SecretDetail} secretDetail
* @ngInject
*/
constructor(secretDetail) {
/** @export {!backendApi.SecretDetail} */
this.details = secretDetail;
/** @export */
this.i18n = i18n;
}
}
const i18n = {
/** @export {string} @desc Label 'Secret' which appears at the top of the
delete dialog, opened from a secret details page. */
MSG_SECRET_DETAIL_SECRET_LABEL: goog.getMsg('Secret'),
};
<!--
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div layout="column">
<kd-secret-info secret="::$ctrl.secretDetail"></kd-secret-info>
<kd-info-card>
<kd-info-card-section name="{{::$ctrl.i18n.MSG_SECRET_INFO_DATA_SECTION}}">
<kd-info-card-entry ng-repeat="(key, value) in ::$ctrl.secretDetail.data"
title="{{::key}}">
<!-- TODO(#1059): Don't show plain text value. -->
<pre class="kd-pre-block">{{::$ctrl.formatDataValue(value)}}</pre>
</kd-info-card-entry>
</kd-info-card-section>
</kd-info-card>
</div>
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @final
*/
export class SecretDetailController {
/**
* @param {!backendApi.SecretDetail} secretDetail
* @param {!angular.$window} $window
* @ngInject
*/
constructor(secretDetail, $window) {
/** @export {!backendApi.SecretDetail} */
this.secretDetail = secretDetail;
/** @export */
this.i18n = i18n;
/** @private {!angular.$window} */
this.window_ = $window;
}
/**
* @param {string} valueB64
* @return {string}
* @export
*/
formatDataValue(valueB64) { return this.window_.atob(valueB64); }
}
const i18n = {
/** @export {string} @desc Config map info details section name. */
MSG_SECRET_INFO_DATA_SECTION: goog.getMsg('Data'),
};
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/** Name of the state. Can be used in, e.g., $state.go method. */
export const stateName = 'secretdetail';
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {actionbarViewName, stateName as chromeStateName} from 'chrome/chrome_state';
import {breadcrumbsConfig} from 'common/components/breadcrumbs/breadcrumbs_service';
import {appendDetailParamsToUrl} from 'common/resource/resourcedetail';
import {stateName as secretList, stateUrl} from 'secretlist/list_state';
import {ActionBarController} from './actionbar_controller';
import {SecretDetailController} from './detail_controller';
import {stateName} from './detail_state';
/**
* Configures states for the secret details view.
*
* @param {!ui.router.$stateProvider} $stateProvider
* @ngInject
*/
export default function stateConfig($stateProvider) {
$stateProvider.state(stateName, {
url: appendDetailParamsToUrl(stateUrl),
parent: chromeStateName,
resolve: {
'secretDetailResource': getSecretDetailResource,
'secretDetail': getSecretDetail,
},
data: {
[breadcrumbsConfig]: {
'label': '{{$stateParams.objectName}}',
'parent': secretList,
},
},
views: {
'': {
controller: SecretDetailController,
controllerAs: '$ctrl',
templateUrl: 'secretdetail/detail.html',
},
[actionbarViewName]: {
controller: ActionBarController,
controllerAs: '$ctrl',
templateUrl: 'secretdetail/actionbar.html',
},
},
});
}
/**
* @param {!./../common/resource/resourcedetail.StateParams} $stateParams
* @param {!angular.$resource} $resource
* @return {!angular.Resource<!backendApi.SecretDetail>}
* @ngInject
*/
export function getSecretDetailResource($resource, $stateParams) {
return $resource(`api/v1/secret/${$stateParams.objectNamespace}/${$stateParams.objectName}`);
}
/**
* @param {!angular.Resource<!backendApi.SecretDetail>} secretDetailResource
* @return {!angular.$q.Promise}
* @ngInject
*/
export function getSecretDetail(secretDetailResource) {
return secretDetailResource.get().$promise;
}
<!--
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<kd-info-card>
<kd-info-card-section name="{{::$ctrl.i18n.MSG_SECRET_INFO_DETAILS_SECTION}}">
<kd-info-card-entry title="{{::$ctrl.i18n.MSG_SECRET_INFO_NAME_ENTRY}}">
<kd-middle-ellipsis
display-string="{{::$ctrl.secret.objectMeta.name}}">
</kd-middle-ellipsis>
</kd-info-card-entry>
<kd-info-card-entry title="{{::$ctrl.i18n.MSG_SECRET_INFO_NAMESPACE_ENTRY}}">
{{::$ctrl.secret.objectMeta.namespace}}
</kd-info-card-entry>
<kd-info-card-entry title="{{::$ctrl.i18n.MSG_SECRET_INFO_LABELS_ENTRY}}">
<kd-labels labels="::$ctrl.secret.objectMeta.labels"></kd-labels>
</kd-info-card-entry>
</kd-info-card-section>
</kd-info-card>
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @final
*/
export default class SecretInfoController {
constructor() {
/** @export {!backendApi.SecretDetail} Initialized from the scope. */
this.secret;
/** @export */
this.i18n = i18n;
}
}
/**
* Definition object for the component that displays secret info.
*
* @return {!angular.Directive}
*/
export const secretInfoComponent = {
controller: SecretInfoController,
templateUrl: 'secretdetail/info.html',
bindings: {
/** {!backendApi.SecretDetail} */
'secret': '=',
},
};
const i18n = {
/** @export {string} @desc Config map info details section name. */
MSG_SECRET_INFO_DETAILS_SECTION: goog.getMsg('Details'),
/** @export {string} @desc Config map info details section name entry. */
MSG_SECRET_INFO_NAME_ENTRY: goog.getMsg('Name'),
/** @export {string} @desc Config map info details section namespace entry. */
MSG_SECRET_INFO_NAMESPACE_ENTRY: goog.getMsg('Namespace'),
/** @export {string} @desc Config map info details section labels entry. */
MSG_SECRET_INFO_LABELS_ENTRY: goog.getMsg('Labels'),
};
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import chromeModule from 'chrome/chrome_module';
import componentsModule from 'common/components/components_module';
import eventsModule from 'events/events_module';
import filtersModule from 'common/filters/filters_module';
import stateConfig from './detail_stateconfig';
import {secretInfoComponent} from './info_component';
/**
* Angular module for the Secret details view.
*
* The view shows detailed view of a Secret.
*/
export default angular
.module(
'kubernetesDashboard.secretDetail',
[
'ngMaterial',
'ngResource',
'ui.router',
componentsModule.name,
filtersModule.name,
eventsModule.name,
chromeModule.name,
])
.config(stateConfig)
.component('kdSecretInfo', secretInfoComponent);
......@@ -18,8 +18,10 @@ limitations under the License.
<kd-resource-card-columns>
<kd-resource-card-column>
<div>
<kd-middle-ellipsis display-string="{{::$ctrl.secret.objectMeta.name}}">
</kd-middle-ellipsis>
<a ng-href="{{::$ctrl.getSecretDetailHref()}}" class="kd-middle-ellipsised-link">
<kd-middle-ellipsis display-string="{{::$ctrl.secret.objectMeta.name}}">
</kd-middle-ellipsis>
</a>
</div>
</kd-resource-card-column>
<kd-resource-card-column ng-if="$ctrl.areMultipleNamespacesSelected()">
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {StateParams} from 'common/resource/resourcedetail';
import {stateName} from 'secretdetail/detail_state';
class SecretCardController {
/**
* @ngInject
* @param {!angular.$interpolate} $interpolate
* @param {!./../common/namespace/namespace_service.NamespaceService} kdNamespaceService
* @param {!ui.router.$state} $state
* @ngInject
*/
constructor($interpolate, kdNamespaceService) {
/**
* Secret initialised from scope
* @export {!backendApi.Secret}
*/
constructor($interpolate, $state) {
/** @export {!backendApi.Secret} Secret initialised from a bindig. */
this.secret;
/** @private {!angular.$interpolate} */
this.interpolate_ = $interpolate;
/** @private {!./../common/namespace/namespace_service.NamespaceService} */
this.kdNamespaceService_ = kdNamespaceService;
}
/**
* @return {boolean}
* @export
*/
areMultipleNamespacesSelected() {
return this.kdNamespaceService_.areMultipleNamespacesSelected();
/** @private {!ui.router.$state} */
this.state_ = $state;
}
/**
......@@ -39,6 +45,15 @@ class SecretCardController {
goog.getMsg('Started at {$startDate} UTC', {'startDate': filter({'date': startDate})});
return MSG_SECRET_LIST_STARTED_AT_TOOLTIP;
}
/**
* @return {string}
* @export
*/
getSecretDetailHref() {
return this.state_.href(
stateName, new StateParams(this.secret.objectMeta.namespace, this.secret.objectMeta.name));
}
}
/**
......@@ -49,5 +64,5 @@ export const secretCardComponent = {
'secret': '=',
},
controller: SecretCardController,
templateUrl: 'secretlist/secretcard.html',
templateUrl: 'secretlist/card.html',
};
......@@ -43,7 +43,7 @@ export class SecretCardListController {
* @type {!angular.Component}
*/
export const secretCardListComponent = {
templateUrl: 'secretlist/secretcardlist.html',
templateUrl: 'secretlist/cardlist.html',
controller: SecretCardListController,
bindings: {
/** {!backendApi.SecretList} */
......
......@@ -16,9 +16,9 @@ import {actionbarViewName, stateName as chromeStateName} from 'chrome/chrome_sta
import {breadcrumbsConfig} from 'common/components/breadcrumbs/breadcrumbs_service';
import {PaginationService} from 'common/pagination/pagination_service';
import {SecretListController} from './secretlist_controller';
import {stateName} from './secretlist_state';
import {stateUrl} from './secretlist_state';
import {SecretListController} from './list_controller';
import {stateName} from './list_state';
import {stateUrl} from './list_state';
/**
* @param {!ui.router.$stateProvider} $stateProvider
......@@ -40,7 +40,7 @@ export default function stateConfig($stateProvider) {
'': {
controller: SecretListController,
controllerAs: '$ctrl',
templateUrl: 'secretlist/secretlist.html',
templateUrl: 'secretlist/list.html',
},
[actionbarViewName]: {
templateUrl: 'secretlist/actionbar.html',
......
......@@ -13,13 +13,13 @@
// limitations under the License.
import chromeModule from 'chrome/chrome_module';
import stateConfig from './secretlist_stateconfig';
import stateConfig from './list_stateconfig';
import paginationModule from 'common/pagination/pagination_module';
import componentsModule from 'common/components/components_module';
import filtersModule from 'common/filters/filters_module';
import namespaceModule from 'common/namespace/namespace_module';
import {secretCardListComponent} from './secretcardlist_component';
import {secretCardComponent} from './secretcard_component';
import {secretCardListComponent} from './cardlist_component';
import {secretCardComponent} from './card_component';
/**
* Angular module for the Secrets list view.
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package secret
import (
"reflect"
"testing"
"github.com/kubernetes/dashboard/src/app/backend/resource/common"
"k8s.io/kubernetes/pkg/api"
)
func TestGetSecretDetail(t *testing.T) {
cases := []struct {
secrets *api.Secret
expected *SecretDetail
}{
{
&api.Secret{
Data: map[string][]byte{"app": {0, 1, 2, 3}}, ObjectMeta: api.ObjectMeta{Name: "foo"},
},
&SecretDetail{
TypeMeta: common.TypeMeta{Kind: "secret"},
ObjectMeta: common.ObjectMeta{Name: "foo"},
Data: map[string][]byte{"app": {0, 1, 2, 3}},
},
},
}
for _, c := range cases {
actual := getSecretDetail(c.secrets)
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("getSecretDetail(%#v) == \n%#v\nexpected \n%#v\n",
c.secrets, actual, c.expected)
}
}
}
......@@ -43,6 +43,7 @@ describe('Middle ellipsis directive', () => {
scope.displayString = new Array(stringLength + 1).join('x');
let element = compileFn(scope);
document.body.appendChild(element[0]);
scope.$digest();
// when
element[0].style.width = '500px';
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {ActionBarController} from 'secretdetail/actionbar_controller';
import module from 'secretdetail/module';
describe('Action Bar controller', () => {
/** @type {!ActionBarController} */
let ctrl;
let details = {};
beforeEach(() => {
angular.mock.module(module.name);
angular.mock.inject(
($controller) => { ctrl = $controller(ActionBarController, {secretDetail: details}); });
});
it('should initialize details', () => { expect(ctrl.details).toBe(details); });
});
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {SecretDetailController} from 'secretdetail/detail_controller';
import module from 'secretdetail/module';
describe('Secret Detail controller', () => {
beforeEach(() => { angular.mock.module(module.name); });
it('should initialize secret controller', angular.mock.inject(($controller) => {
let data = {};
/** @type {!SecretDetailController} */
let ctrl = $controller(SecretDetailController, {secretDetail: data});
expect(ctrl.secretDetail).toBe(data);
}));
it('should format base64', angular.mock.inject(($controller) => {
let data = {};
/** @type {!SecretDetailController} */
let ctrl = $controller(SecretDetailController, {secretDetail: data});
expect(ctrl.formatDataValue('SGVsbG8gd29ybGQ=')).toBe('Hello world');
}));
});
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import module from 'secretdetail/module';
describe('Secret Info controller', () => {
/** @type {!SecretInfoController} */
let ctrl;
beforeEach(() => {
angular.mock.module(module.name);
angular.mock.inject(($componentController, $rootScope) => {
ctrl = $componentController('kdSecretInfo', {$scope: $rootScope}, {
secret: {
data: {foo: 'bar'},
},
});
});
});
it('should initialize the ctrl', () => { expect(ctrl.i18n).not.toBeUndefined(); });
});
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import secretListModule from 'secretlist/secretlist_module';
import secretListModule from 'secretlist/module';
describe('Secret card', () => {
/**
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import secretListModule from 'secretlist/secretlist_module';
import secretListModule from 'secretlist/module';
describe('Secret Card List controller', () => {
/**
......
......@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {SecretListController} from 'secretlist/secretlist_controller';
import secretListModule from 'secretlist/secretlist_module';
import {SecretListController} from 'secretlist/list_controller';
import secretListModule from 'secretlist/module';
describe('Secret list controller', () => {
/** @type {!secretlist/secretlist_controller.SecretListController} */
......
......@@ -14,8 +14,8 @@
import {PaginationService} from 'common/pagination/pagination_service';
import secretListModule from 'secretlist/secretlist_module';
import {resolveSecretList} from 'secretlist/secretlist_stateconfig';
import secretListModule from 'secretlist/module';
import {resolveSecretList} from 'secretlist/list_stateconfig';
describe('StateConfig for secret list', () => {
beforeEach(() => { angular.mock.module(secretListModule.name); });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册