@@ -31,6 +31,7 @@ type Cloudbrain struct { | |||
RepoID int64 `xorm:"INDEX"` | |||
SubTaskName string `xorm:"INDEX"` | |||
ContainerID string | |||
ContainerIp string | |||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
CanDebug bool `xorm:"-"` | |||
@@ -219,9 +220,10 @@ type ImageInfo struct { | |||
} | |||
type CommitImageParams struct { | |||
Ip string `json:"ip"` | |||
TaskContainerId string `json:"taskContainerId"` | |||
ImageDescription string `json:"imageDescription"` | |||
Ip string `json:"ip"` | |||
TaskContainerId string `json:"taskContainerId"` | |||
ImageTag string `json:"imageTag"` | |||
ImageDescription string `json:"imageDescription"` | |||
} | |||
type CommitImageResult struct { | |||
@@ -339,6 +341,6 @@ func UpdateJob(job *Cloudbrain) error { | |||
func updateJob(e Engine, job *Cloudbrain) error { | |||
var sess *xorm.Session | |||
sess = e.Where("job_id = ?", job.JobID) | |||
_, err := sess.Cols("status", "container_id").Update(job) | |||
_, err := sess.Cols("status", "container_id", "container_ip").Update(job) | |||
return err | |||
} |
@@ -13,6 +13,11 @@ type CreateCloudBrainForm struct { | |||
Attachment string `form:"attachment" binding:"Required"` | |||
} | |||
type CommitImageCloudBrainForm struct { | |||
Description string `form:"description" binding:"Required"` | |||
Tag string `form:"tag" binding:"Required"` | |||
} | |||
func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} |
@@ -150,7 +150,7 @@ sendjob: | |||
return &getImagesResult, nil | |||
} | |||
func CommitImage(jobID string, params models.CommitImageParams) (*models.CommitImageResult, error) { | |||
func CommitImage(jobID string, params models.CommitImageParams) error { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.CommitImageResult | |||
@@ -166,7 +166,7 @@ sendjob: | |||
Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage") | |||
if err != nil { | |||
return nil, fmt.Errorf("resty CommitImage: %v", err) | |||
return fmt.Errorf("resty CommitImage: %v", err) | |||
} | |||
if result.Code == "S401" && retry < 1 { | |||
@@ -176,10 +176,10 @@ sendjob: | |||
} | |||
if result.Code != Success { | |||
return &result, fmt.Errorf("CommitImage err: %s", res.String()) | |||
return fmt.Errorf("CommitImage err: %s", res.String()) | |||
} | |||
return &result, nil | |||
return nil | |||
} | |||
func StopJob(jobID string) error { | |||
@@ -752,6 +752,7 @@ cloudbrain=云脑 | |||
cloudbrain.new=新建任务 | |||
cloudbrain.desc=云脑功能 | |||
cloudbrain.cancel=取消 | |||
cloudbrain.commit_image=提交 | |||
template.items=模板选项 | |||
template.git_content=Git数据(默认分支) | |||
@@ -1,6 +1,7 @@ | |||
package repo | |||
import ( | |||
"errors" | |||
"os" | |||
"os/exec" | |||
"strconv" | |||
@@ -164,6 +165,7 @@ func CloudBrainShow(ctx *context.Context) { | |||
ctx.Data["taskRes"] = taskRes | |||
task.Status = taskRes.TaskStatuses[0].State | |||
task.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||
task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
ctx.Data["error"] = err.Error() | |||
@@ -186,27 +188,23 @@ func CloudBrainDebug(ctx *context.Context) { | |||
ctx.Redirect(debugUrl) | |||
} | |||
func CloudBrainCommitImage(ctx *context.Context) { | |||
func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | |||
var jobID = ctx.Params(":jobid") | |||
var description = ctx.Query("description") | |||
log.Info(description) | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
if err != nil { | |||
ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
return | |||
} | |||
jobResult, err := cloudbrain.CommitImage(jobID, models.CommitImageParams{ | |||
Ip: setting.ImageServerHost, | |||
TaskContainerId: task.ContainerID, | |||
ImageDescription: description, | |||
err = cloudbrain.CommitImage(jobID, models.CommitImageParams{ | |||
Ip: task.ContainerIp, | |||
TaskContainerId: task.ContainerID, | |||
ImageDescription: form.Description, | |||
ImageTag: form.Tag, | |||
}) | |||
if err != nil { | |||
log.Error("CommitImage failed:", err.Error()) | |||
return | |||
} | |||
if jobResult.Code != cloudbrain.Success { | |||
log.Error("CommitImage(%s) failed:%s", jobID, jobResult.Msg) | |||
ctx.ServerError("CommitImage failed", err) | |||
return | |||
} | |||
@@ -215,9 +213,30 @@ func CloudBrainCommitImage(ctx *context.Context) { | |||
func CloudBrainStop(ctx *context.Context) { | |||
var jobID = ctx.Params(":jobid") | |||
err := cloudbrain.StopJob(jobID) | |||
log.Info(jobID) | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
if err != nil { | |||
ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
return | |||
} | |||
if task.Status != string(models.JobRunning) { | |||
log.Error("the job(%s) is not running", task.JobName) | |||
ctx.ServerError("the job is not running", errors.New("the job is not running")) | |||
return | |||
} | |||
err = cloudbrain.StopJob(jobID) | |||
if err != nil { | |||
log.Error("StopJob(%s) failed:%v", task.JobName, err.Error()) | |||
ctx.ServerError("StopJob failed", err) | |||
return | |||
} | |||
task.Status = string(models.JobStopped) | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
log.Error("StopJob failed:", err.Error()) | |||
ctx.ServerError("UpdateJob failed", err) | |||
return | |||
} | |||
@@ -894,10 +894,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Group("/cloudbrain", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainIndex) | |||
m.Get("/:jobid", reqRepoCloudBrainReader, repo.CloudBrainShow) | |||
m.Get("/:jobid/debug", reqRepoCloudBrainReader, repo.CloudBrainDebug) | |||
m.Post("/:jobid/commit_image", reqRepoCloudBrainReader, repo.CloudBrainCommitImage) | |||
m.Post("/:jobid/stop", reqRepoCloudBrainReader, repo.CloudBrainStop) | |||
m.Group("/:jobid", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | |||
m.Get("/debug", reqRepoCloudBrainReader, repo.CloudBrainDebug) | |||
m.Post("/commit_image", reqRepoCloudBrainWriter, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | |||
m.Post("/stop", reqRepoCloudBrainWriter, repo.CloudBrainStop) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
}, context.RepoRef()) | |||
@@ -63,18 +63,50 @@ | |||
</div> | |||
<div class="two wide column"> | |||
<span class="ui text center clipboard"> | |||
<form id="stopForm" action="{{if not .CanDebug}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/commit_image{{end}}" method="post"> | |||
<form id="commitImageForm" action="{{if not .CanDebug}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/commit_image{{end}}" method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a class="fitted" onclick="document.getElementById('commitImageForm').submit();" style="{{if not .CanDebug}}color:#CCCCCC{{end}}; font-size:16px; font-weight:bold">镜像提交</a> | |||
</form> | |||
<a class="fitted" onclick="document.getElementById(stopForm).submit();" style="{{if not .CanDebug}}color:#CCCCCC{{end}}; font-size:16px; font-weight:bold">镜像提交</a> | |||
</span> | |||
</div> | |||
<div class="one wide column"> | |||
<span class="ui text center clipboard"> | |||
<form id="stopForm" action="{{if ne .Status "RUNNING"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a class="fitted" onclick="document.getElementById('stopForm').submit();" style="{{if ne .Status "RUNNING"}}color:#CCCCCC{{end}}; font-size:16px; font-weight:bold">停止</a> | |||
</form> | |||
<a class="fitted" onclick="document.getElementById(stopForm).submit();" style="{{if ne .Status "RUNNING"}}color:#CCCCCC{{end}}; font-size:16px; font-weight:bold">停止</a> | |||
</span> | |||
</div> | |||
<!-- 打开弹窗按钮 --> | |||
<button id="imageBtn">提交镜像</button> | |||
<!-- 弹窗 --> | |||
<div id="imageModal" class="modal"> | |||
<!-- 弹窗内容 --> | |||
<div class="modal-content"> | |||
<span class="close">×</span> | |||
<form id="commitImageForm" action="{{$.Link}}/{{.JobID}}/commit_image" method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<div class="inline required field"> | |||
<label>镜像标签:</label> | |||
<input name="tag" id="image_tag" tabindex="3" autofocus required maxlength="255"> | |||
</div> | |||
<div class="inline required field"> | |||
<label>镜像描述:</label> | |||
<textarea name="description" rows="10"></textarea> | |||
</div> | |||
<div class="inline field"> | |||
<label></label> | |||
<button class="ui green button"> | |||
{{$.i18n.Tr "repo.cloudbrain.commit_image"}} | |||
</button> | |||
<a class="ui button" href="/">{{$.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
@@ -106,4 +138,71 @@ $( document ).ready(function() { | |||
}); | |||
}); | |||
// 获取弹窗 | |||
var modal = document.getElementById('imageModal'); | |||
// 打开弹窗的按钮对象 | |||
var btn = document.getElementById("imageBtn"); | |||
// 获取 <span> 元素,用于关闭弹窗 | |||
var span = document.querySelector('.close'); | |||
// 点击按钮打开弹窗 | |||
btn.onclick = function() { | |||
modal.style.display = "block"; | |||
} | |||
// 点击 <span> (x), 关闭弹窗 | |||
span.onclick = function() { | |||
modal.style.display = "none"; | |||
} | |||
// 在用户点击其他地方时,关闭弹窗 | |||
window.onclick = function(event) { | |||
if (event.target == modal) { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
</script> | |||
<style> | |||
/* 弹窗 (background) */ | |||
.modal { | |||
display: none; /* 默认隐藏 */ | |||
position: fixed; /* 固定定位 */ | |||
z-index: 1; /* 设置在顶层 */ | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
overflow: auto; | |||
background-color: rgb(0,0,0); | |||
background-color: rgba(0,0,0,0.4); | |||
} | |||
/* 弹窗内容 */ | |||
.modal-content { | |||
background-color: #fefefe; | |||
margin: 15% auto; | |||
padding: 20px; | |||
border: 1px solid #888; | |||
width: 30%; | |||
} | |||
/* 关闭按钮 */ | |||
.close { | |||
color: #aaa; | |||
float: right; | |||
font-size: 28px; | |||
font-weight: bold; | |||
} | |||
.close:hover, | |||
.close:focus { | |||
color: black; | |||
text-decoration: none; | |||
cursor: pointer; | |||
} | |||
</style> |