Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
凌波微步_大先生
dashboard
提交
f353a432
D
dashboard
项目概览
凌波微步_大先生
/
dashboard
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dashboard
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f353a432
编写于
11月 16, 2015
作者:
P
Piotr Bryk
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #61 from kubernetes/deploy-app-form
Initial e2e implementation of the deploy view
上级
c161f128
1b3d9baf
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
229 addition
and
37 deletion
+229
-37
src/app/backend/apihandler.go
src/app/backend/apihandler.go
+3
-3
src/app/backend/deploy.go
src/app/backend/deploy.go
+88
-18
src/app/externs/backendapi.js
src/app/externs/backendapi.js
+18
-3
src/app/frontend/deploy/deploy.controller.js
src/app/frontend/deploy/deploy.controller.js
+79
-7
src/app/frontend/deploy/deploy.html
src/app/frontend/deploy/deploy.html
+41
-6
未找到文件。
src/app/backend/apihandler.go
浏览文件 @
f353a432
...
@@ -33,8 +33,8 @@ func CreateHttpApiHandler(client *client.Client) http.Handler {
...
@@ -33,8 +33,8 @@ func CreateHttpApiHandler(client *client.Client) http.Handler {
deployWs
.
Route
(
deployWs
.
Route
(
deployWs
.
POST
(
""
)
.
deployWs
.
POST
(
""
)
.
To
(
apiHandler
.
handleDeploy
)
.
To
(
apiHandler
.
handleDeploy
)
.
Reads
(
DeployAppConfig
{})
.
Reads
(
AppDeployment
{})
.
Writes
(
DeployAppConfig
{}))
Writes
(
AppDeployment
{}))
wsContainer
.
Add
(
deployWs
)
wsContainer
.
Add
(
deployWs
)
microserviceListWs
:=
new
(
restful
.
WebService
)
microserviceListWs
:=
new
(
restful
.
WebService
)
...
@@ -55,7 +55,7 @@ type ApiHandler struct {
...
@@ -55,7 +55,7 @@ type ApiHandler struct {
// Handles deploy API call.
// Handles deploy API call.
func
(
apiHandler
*
ApiHandler
)
handleDeploy
(
request
*
restful
.
Request
,
response
*
restful
.
Response
)
{
func
(
apiHandler
*
ApiHandler
)
handleDeploy
(
request
*
restful
.
Request
,
response
*
restful
.
Response
)
{
cfg
:=
new
(
DeployAppConfig
)
cfg
:=
new
(
AppDeployment
)
if
err
:=
request
.
ReadEntity
(
cfg
);
err
!=
nil
{
if
err
:=
request
.
ReadEntity
(
cfg
);
err
!=
nil
{
handleInternalError
(
response
,
err
)
handleInternalError
(
response
,
err
)
return
return
...
...
src/app/backend/deploy.go
浏览文件 @
f353a432
...
@@ -15,43 +15,113 @@
...
@@ -15,43 +15,113 @@
package
main
package
main
import
(
import
(
"math/rand"
"strconv"
"time"
api
"k8s.io/kubernetes/pkg/api"
api
"k8s.io/kubernetes/pkg/api"
client
"k8s.io/kubernetes/pkg/client/unversioned"
client
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/util"
)
)
// Configuration for an app deployment.
// Configuration for an app deployment.
type
DeployAppConfig
struct
{
type
AppDeployment
struct
{
// Name of the application.
// Name of the application.
AppName
string
`json:"appN
ame"`
Name
string
`json:"n
ame"`
// Docker image path for the application.
// Docker image path for the application.
ContainerImage
string
`json:"containerImage"`
ContainerImage
string
`json:"containerImage"`
// Number of replicas of the image to maintain.
Replicas
int
`json:"replicas"`
// Port mappings for the service that is created. The service is created if there is at least
// one port mapping.
PortMappings
[]
PortMapping
`json:"portMappings"`
// Whether the created service is external.
IsExternal
bool
`json:"isExternal"`
}
}
// Deploys an app based on the given configuration. The app is deployed using the given client.
// Port mapping for an application deployment.
func
DeployApp
(
config
*
DeployAppConfig
,
client
*
client
.
Client
)
error
{
type
PortMapping
struct
{
// TODO(bryk): The implementation below is just for tests. To complete an end-to-end setup of
// Port that will be exposed on the service.
// the project. It'll be replaced with a real implementation.
Port
int
`json:"port"`
rand
.
Seed
(
time
.
Now
()
.
UTC
()
.
UnixNano
())
podName
:=
config
.
AppName
+
"-"
+
strconv
.
Itoa
(
rand
.
Intn
(
10000
))
// Docker image path for the application.
TargetPort
int
`json:"targetPort"`
// IP protocol for the mapping, e.g., "TCP" or "UDP".
Protocol
api
.
Protocol
`json:"protocol"`
}
pod
:=
api
.
Pod
{
// Deploys an app based on the given configuration. The app is deployed using the given client.
// App deployment consists of a replication controller and an optional service. Both of them share
// common labels.
// TODO(bryk): Write tests for this function.
func
DeployApp
(
deployment
*
AppDeployment
,
client
*
client
.
Client
)
error
{
podTemplate
:=
&
api
.
PodTemplateSpec
{
ObjectMeta
:
api
.
ObjectMeta
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
podName
,
Labels
:
map
[
string
]
string
{
"name"
:
deployment
.
Name
}
,
},
},
Spec
:
api
.
PodSpec
{
Spec
:
api
.
PodSpec
{
Containers
:
[]
api
.
Container
{{
Containers
:
[]
api
.
Container
{{
Name
:
config
.
App
Name
,
Name
:
deployment
.
Name
,
Image
:
config
.
ContainerImage
,
Image
:
deployment
.
ContainerImage
,
}},
}},
},
},
}
}
_
,
err
:=
client
.
Pods
(
api
.
NamespaceDefault
)
.
Create
(
&
pod
)
replicaSet
:=
&
api
.
ReplicationController
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
deployment
.
Name
,
},
Spec
:
api
.
ReplicationControllerSpec
{
Replicas
:
deployment
.
Replicas
,
Selector
:
map
[
string
]
string
{
"name"
:
deployment
.
Name
},
Template
:
podTemplate
,
},
}
_
,
err
:=
client
.
ReplicationControllers
(
api
.
NamespaceDefault
)
.
Create
(
replicaSet
)
if
err
!=
nil
{
// TODO(bryk): Roll back created resources in case of error.
return
err
}
if
len
(
deployment
.
PortMappings
)
>
0
{
service
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
deployment
.
Name
,
Labels
:
map
[
string
]
string
{
"name"
:
deployment
.
Name
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"name"
:
deployment
.
Name
},
},
}
return
err
if
deployment
.
IsExternal
{
service
.
Spec
.
Type
=
api
.
ServiceTypeLoadBalancer
}
else
{
service
.
Spec
.
Type
=
api
.
ServiceTypeNodePort
}
for
_
,
portMapping
:=
range
deployment
.
PortMappings
{
servicePort
:=
api
.
ServicePort
{
Protocol
:
portMapping
.
Protocol
,
Port
:
portMapping
.
Port
,
TargetPort
:
util
.
IntOrString
{
Kind
:
util
.
IntstrInt
,
IntVal
:
portMapping
.
TargetPort
,
},
}
service
.
Spec
.
Ports
=
append
(
service
.
Spec
.
Ports
,
servicePort
)
}
_
,
err
=
client
.
Services
(
api
.
NamespaceDefault
)
.
Create
(
service
)
// TODO(bryk): Roll back created resources in case of error.
return
err
}
else
{
return
nil
}
}
}
src/app/externs/backendapi.js
浏览文件 @
f353a432
...
@@ -22,12 +22,27 @@
...
@@ -22,12 +22,27 @@
* @externs
* @externs
*/
*/
const
backendApi
=
{};
const
backendApi
=
{};
/**
* @typedef {{
* port: (number|null),
* protocol: string,
* targetPort: (number|null)
* }}
*/
backendApi
.
PortMapping
;
/**
/**
* @typedef {{
* @typedef {{
* appName: string,
* containerImage: string,
* containerImage: string
* isExternal: boolean,
* name: string,
* portMappings: !Array<!backendApi.PortMapping>,
* replicas: number
* }}
* }}
*/
*/
backendApi
.
DeployAppConfig
;
backendApi
.
AppDeployment
;
src/app/frontend/deploy/deploy.controller.js
浏览文件 @
f353a432
...
@@ -22,20 +22,43 @@ export default class DeployController {
...
@@ -22,20 +22,43 @@ export default class DeployController {
/**
/**
* @param {!angular.$resource} $resource
* @param {!angular.$resource} $resource
* @param {!angular.$log} $log
* @param {!angular.$log} $log
* @param {!ui.router.$state} $state
* @ngInject
* @ngInject
*/
*/
constructor
(
$resource
,
$log
)
{
constructor
(
$resource
,
$log
,
$state
)
{
/** @export {string} */
/** @export {string} */
this
.
appN
ame
=
''
;
this
.
n
ame
=
''
;
/** @export {string} */
/** @export {string} */
this
.
containerImage
=
''
;
this
.
containerImage
=
''
;
/** @private {!angular.Resource<!backendApi.DeployAppConfig>} */
/** @export {number} */
this
.
replicas
=
1
;
/**
* List of supported protocols.
* TODO(bryk): Do not hardcode the here, move to backend.
* @const @export {!Array<string>}
*/
this
.
protocols
=
[
'
TCP
'
,
'
UDP
'
];
/** @export {!Array<!backendApi.PortMapping>} */
this
.
portMappings
=
[
this
.
newEmptyPortMapping_
(
this
.
protocols
[
0
])];
/** @export {boolean} */
this
.
isExternal
=
false
;
/** @private {!angular.Resource<!backendApi.AppDeployment>} */
this
.
resource_
=
$resource
(
'
/api/deploy
'
);
this
.
resource_
=
$resource
(
'
/api/deploy
'
);
/** @private {!angular.$log} */
/** @private {!angular.$log} */
this
.
log_
=
$log
;
this
.
log_
=
$log
;
/** @private {!ui.router.$state} */
this
.
state_
=
$state
;
/** @private {boolean} */
this
.
isDeployInProgress_
=
false
;
}
}
/**
/**
...
@@ -44,19 +67,68 @@ export default class DeployController {
...
@@ -44,19 +67,68 @@ export default class DeployController {
* @export
* @export
*/
*/
deploy
()
{
deploy
()
{
/** @type {!backendApi.DeployAppConfig} */
// TODO(bryk): Validate input data before sending to the server.
/** @type {!backendApi.AppDeployment} */
let
deployAppConfig
=
{
let
deployAppConfig
=
{
appName
:
this
.
appName
,
containerImage
:
this
.
containerImage
,
containerImage
:
this
.
containerImage
,
isExternal
:
this
.
isExternal
,
name
:
this
.
name
,
portMappings
:
this
.
portMappings
.
filter
(
this
.
isPortMappingEmpty_
),
replicas
:
this
.
replicas
,
};
};
this
.
isDeployInProgress_
=
true
;
this
.
resource_
.
save
(
this
.
resource_
.
save
(
deployAppConfig
,
deployAppConfig
,
(
savedConfig
)
=>
{
(
savedConfig
)
=>
{
this
.
log_
.
info
(
'
Succesfully deployed application:
'
,
savedConfig
);
this
.
isDeployInProgress_
=
false
;
this
.
log_
.
info
(
'
Successfully deployed application:
'
,
savedConfig
);
this
.
state_
.
go
(
'
servicelist
'
);
},
},
(
err
)
=>
{
(
err
)
=>
{
this
.
log_
.
error
(
'
Error deployng application:
'
,
err
);
this
.
isDeployInProgress_
=
false
;
this
.
log_
.
error
(
'
Error deploying application:
'
,
err
);
});
});
}
}
/**
* Returns true when the deploy action should be enabled.
* @return {boolean}
* @export
*/
isDeployDisabled
()
{
return
this
.
isDeployInProgress_
;
}
/**
* Cancels the deployment form.
* @export
*/
cancel
()
{
this
.
state_
.
go
(
'
zero
'
);
}
/**
* @param {string} defaultProtocol
* @return {!backendApi.PortMapping}
* @private
*/
newEmptyPortMapping_
(
defaultProtocol
)
{
return
{
port
:
null
,
targetPort
:
null
,
protocol
:
defaultProtocol
,
};
}
/**
* Returns true when the given port mapping hasn't been filled by the user, i.e., is empty.
* @param {!backendApi.PortMapping} portMapping
* @return {boolean}
* @private
*/
isPortMappingEmpty_
(
portMapping
)
{
return
!!
portMapping
.
port
&&
!!
portMapping
.
targetPort
;
}
}
}
src/app/frontend/deploy/deploy.html
浏览文件 @
f353a432
...
@@ -17,17 +17,52 @@ limitations under the License.
...
@@ -17,17 +17,52 @@ limitations under the License.
<div
layout=
"column"
layout-padding
layout-align=
"center center"
>
<div
layout=
"column"
layout-padding
layout-align=
"center center"
>
<md-whiteframe
class=
"kd-deploy-whiteframe md-whiteframe-5dp"
flex
flex-gt-md
>
<md-whiteframe
class=
"kd-deploy-whiteframe md-whiteframe-5dp"
flex
flex-gt-md
>
<h3
class=
"md-headline"
>
Deploy a Containerized App
</h3>
<h3
class=
"md-headline"
>
Deploy a Containerized App
</h3>
<form
n
ame=
"userForm
"
>
<form
n
g-submit=
"ctrl.deploy()
"
>
<md-input-container
class=
"md-block"
>
<md-input-container
class=
"md-block"
>
<label>
App name
</label>
<label>
App name
</label>
<input
ng-model=
"ctrl.
appName"
>
<input
ng-model=
"ctrl.
name"
required
>
</md-input-container>
</md-input-container>
<md-radio-group
ng-model=
"data.group1"
>
<md-radio-button
class=
"md-primary"
>
Specify app details below
</md-radio-button>
<md-radio-button
value=
"bar"
class=
"md-primary"
>
Upload a YAML or JSON file
</md-radio-button>
</md-radio-group>
<md-input-container
class=
"md-block"
>
<md-input-container
class=
"md-block"
>
<label>
Container
I
mage
</label>
<label>
Container
i
mage
</label>
<input
ng-model=
"ctrl.containerImage"
>
<input
ng-model=
"ctrl.containerImage"
required
>
</md-input-container>
</md-input-container>
<md-button
href
class=
"md-raised md-primary"
ng-click=
"ctrl.deploy()"
>
Submit
</md-button>
<md-input-container
class=
"md-block"
>
<md-button
class=
"md-raised"
ui-sref=
"zero"
>
Cancel
</md-button>
<label>
Number of pods
</label>
<input
ng-model=
"ctrl.replicas"
type=
"number"
required
min=
"1"
>
</md-input-container>
<div
layout=
"row"
ng-repeat=
"portMapping in ctrl.portMappings"
>
<md-input-container
class=
"md-block"
>
<label>
Port
</label>
<input
ng-model=
"portMapping.port"
type=
"number"
min=
"0"
>
</md-input-container>
<md-input-container
class=
"md-block"
>
<label>
Target port
</label>
<input
ng-model=
"portMapping.targetPort"
type=
"number"
min=
"0"
>
</md-input-container>
<md-input-container
class=
"md-block"
>
<label>
Protocol
</label>
<md-select
ng-model=
"portMapping.protocol"
required
>
<md-option
ng-repeat=
"protocol in ctrl.protocols"
ng-value=
"protocol"
>
{{protocol}}
</md-option>
</md-select>
</md-input-container>
</div>
<md-switch
ng-model=
"ctrl.isExternal"
class=
"md-primary"
>
Expose service externally
</md-switch>
<md-button
class=
"md-raised md-primary"
type=
"submit"
ng-disabled=
"ctrl.isDeployDisabled()"
>
Deploy
</md-button>
<md-button
class=
"md-raised"
ng-click=
"ctrl.cancel()"
>
Cancel
</md-button>
</form>
</form>
</md-whiteframe>
</md-whiteframe>
</div>
</div>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录